8cba65fb5ffc4d2f1a27a731216a6c5364060ce3
[openafs.git] / src / WINNT / afsd / rawops.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <roken.h>
13
14 #include <osi.h>
15 #include "afsd.h"
16
17 /*
18  * called with scp->rw lock held exclusive
19  */
20 afs_int32
21 raw_ReadData( cm_scache_t *scp, osi_hyper_t *offsetp,
22               afs_uint32 length, char *bufferp, afs_uint32 *readp,
23               cm_user_t *userp, cm_req_t *reqp)
24 {
25     long code;
26     cm_buf_t *bufp = NULL;
27     osi_hyper_t fileLength;
28     osi_hyper_t thyper;
29     osi_hyper_t lastByte;
30     osi_hyper_t bufferOffset;
31     long bufIndex, nbytes;
32     int sequential = 0;
33
34     /* start by looking up the file's end */
35     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
36                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
37     if (code)
38         goto done;
39
40     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
41
42     /* now we have the entry locked, look up the length */
43     fileLength = scp->length;
44
45     /* adjust length down so that it won't go past EOF */
46     thyper.LowPart = length;
47     thyper.HighPart = 0;
48     thyper = LargeIntegerAdd(*offsetp, thyper); /* where read should end */
49     lastByte = thyper;
50     if (LargeIntegerGreaterThan(thyper, fileLength)) {
51         /* we'd read past EOF, so just stop at fileLength bytes.
52         * Start by computing how many bytes remain in the file.
53         */
54         thyper = LargeIntegerSubtract(fileLength, *offsetp);
55
56         /* if we are past EOF, read 0 bytes */
57         if (LargeIntegerLessThanZero(thyper))
58             length = 0;
59         else
60             length = thyper.LowPart;
61     }
62
63     *readp = length;
64
65     /* now, copy the data one buffer at a time,
66      * until we've filled the request packet
67      */
68     while (1) {
69         /* if we've copied all the data requested, we're done */
70         if (length <= 0) break;
71
72         /* otherwise, load up a buffer of data */
73         thyper.HighPart = offsetp->HighPart;
74         thyper.LowPart = offsetp->LowPart & ~(cm_data.blockSize-1);
75         if (!bufp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
76             /* wrong buffer */
77             if (bufp) {
78                 buf_Release(bufp);
79                 bufp = NULL;
80             }
81             lock_ReleaseWrite(&scp->rw);
82
83             code = buf_Get(scp, &thyper, reqp, &bufp);
84
85             lock_ObtainWrite(&scp->rw);
86             if (code) goto done;
87             bufferOffset = thyper;
88
89             /* now get the data in the cache */
90             while (1) {
91                 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
92                                  CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
93                 if (code)
94                     goto done;
95
96                 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
97
98                 if (cm_HaveBuffer(scp, bufp, 0)) break;
99
100                 /* otherwise, load the buffer and try again */
101                 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
102                 if (code) break;
103             }
104             if (code) {
105                 buf_Release(bufp);
106                 bufp = NULL;
107                 goto done;
108             }
109         }       /* if (wrong buffer) ... */
110
111         /* now we have the right buffer loaded.  Copy out the
112          * data from here to the user's buffer.
113          */
114         bufIndex = offsetp->LowPart & (cm_data.blockSize - 1);
115
116         /* and figure out how many bytes we want from this buffer */
117         nbytes = cm_data.blockSize - bufIndex;  /* what remains in buffer */
118         if (nbytes > length) nbytes = length;   /* don't go past EOF */
119
120         /* now copy the data */
121         memcpy(bufferp, bufp->datap + bufIndex, nbytes);
122
123         /* adjust lengthers, pointers, etc. */
124         bufferp += nbytes;
125         length -= nbytes;
126         thyper.LowPart = nbytes;
127         thyper.HighPart = 0;
128         *offsetp = LargeIntegerAdd(thyper, *offsetp);
129     } /* while 1 */
130
131   done:
132     if (bufp)
133         buf_Release(bufp);
134
135     if (code == 0 && sequential)
136         cm_ConsiderPrefetch(scp, &lastByte, *readp, userp, reqp);
137
138     return code;
139 }
140
141
142 /*
143  * called with scp->rw lock held exclusive
144  */
145
146 afs_uint32
147 raw_WriteData( cm_scache_t *scp, osi_hyper_t *offsetp, afs_uint32 length, char *bufferp,
148                cm_user_t *userp, cm_req_t *reqp, afs_uint32 *writtenp)
149 {
150     long code = 0;
151     long written = 0;
152     osi_hyper_t fileLength;     /* file's length at start of write */
153     osi_hyper_t minLength;      /* don't read past this */
154     afs_uint32 nbytes;          /* # of bytes to transfer this iteration */
155     cm_buf_t *bufp = NULL;
156     osi_hyper_t thyper;         /* hyper tmp variable */
157     osi_hyper_t bufferOffset;
158     afs_uint32 bufIndex;        /* index in buffer where our data is */
159     int doWriteBack = 0;
160     osi_hyper_t writeBackOffset;/* offset of region to write back when I/O is done */
161     afs_uint32 writeBackLength;
162     DWORD filter = 0;
163
164     /* start by looking up the file's end */
165     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
166                      CM_SCACHESYNC_NEEDCALLBACK
167                       | CM_SCACHESYNC_SETSTATUS
168                       | CM_SCACHESYNC_GETSTATUS);
169     if (code)
170         goto done;
171
172     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_GETSTATUS);
173
174     /* now we have the entry locked, look up the length */
175     fileLength = scp->length;
176     minLength = fileLength;
177     if (LargeIntegerGreaterThan(minLength, scp->serverLength))
178         minLength = scp->serverLength;
179
180     /* adjust file length if we extend past EOF */
181     thyper.LowPart = length;
182     thyper.HighPart = 0;
183     thyper = LargeIntegerAdd(*offsetp, thyper); /* where write should end */
184     if (LargeIntegerGreaterThan(thyper, fileLength)) {
185         /* we'd write past EOF, so extend the file */
186         scp->mask |= CM_SCACHEMASK_LENGTH;
187         scp->length = thyper;
188         filter |= (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE);
189     } else
190         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
191
192     doWriteBack = 1;
193     writeBackOffset = *offsetp;
194     writeBackLength = length;
195
196     /* now, copy the data one buffer at a time, until we've filled the
197      * request packet */
198     while (1) {
199         /* if we've copied all the data requested, we're done */
200         if (length <= 0) break;
201
202         /* otherwise, load up a buffer of data */
203         thyper.HighPart = offsetp->HighPart;
204         thyper.LowPart = offsetp->LowPart & ~(cm_data.blockSize-1);
205         if (!bufp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
206             /* wrong buffer */
207             if (bufp) {
208                 lock_ReleaseMutex(&bufp->mx);
209                 buf_Release(bufp);
210                 bufp = NULL;
211             }
212             lock_ReleaseWrite(&scp->rw);
213
214             code = buf_Get(scp, &thyper, reqp, &bufp);
215             if (bufp)
216                 lock_ObtainMutex(&bufp->mx);
217
218             lock_ObtainWrite(&scp->rw);
219             if (code)
220                 goto done;
221
222             bufferOffset = thyper;
223
224             /* now get the data in the cache */
225             while (1) {
226                 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
227                                   CM_SCACHESYNC_NEEDCALLBACK
228                                   | CM_SCACHESYNC_WRITE
229                                   | CM_SCACHESYNC_BUFLOCKED);
230                 if (code)
231                     goto done;
232
233                 cm_SyncOpDone(scp, bufp,
234                                CM_SCACHESYNC_NEEDCALLBACK
235                                | CM_SCACHESYNC_WRITE
236                                | CM_SCACHESYNC_BUFLOCKED);
237
238                 /* If we're overwriting the entire buffer, or
239                  * if we're writing at or past EOF, mark the
240                  * buffer as current so we don't call
241                  * cm_GetBuffer.  This skips the fetch from the
242                  * server in those cases where we're going to
243                  * obliterate all the data in the buffer anyway,
244                  * or in those cases where there is no useful
245                  * data at the server to start with.
246                  *
247                  * Use minLength instead of scp->length, since
248                  * the latter has already been updated by this
249                  * call.
250                  */
251                 if (LargeIntegerGreaterThanOrEqualTo(bufp->offset, minLength) ||
252                      LargeIntegerEqualTo(*offsetp, bufp->offset) &&
253                      (length >= cm_data.blockSize ||
254                        LargeIntegerGreaterThanOrEqualTo(LargeIntegerAdd(*offsetp, ConvertLongToLargeInteger(length)), minLength))) {
255                     if (length < cm_data.blockSize
256                          && bufp->dataVersion == CM_BUF_VERSION_BAD)
257                         memset(bufp->datap, 0,
258                                 cm_data.blockSize);
259                     bufp->dataVersion = scp->dataVersion;
260                 }
261
262                 if (cm_HaveBuffer(scp, bufp, 1)) break;
263
264                 /* otherwise, load the buffer and try again */
265                 lock_ReleaseMutex(&bufp->mx);
266                 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
267                 lock_ReleaseWrite(&scp->rw);
268                 lock_ObtainMutex(&bufp->mx);
269                 lock_ObtainWrite(&scp->rw);
270                 if (code)
271                     break;
272             }
273             if (code) {
274                 lock_ReleaseMutex(&bufp->mx);
275                 buf_Release(bufp);
276                 bufp = NULL;
277                 goto done;
278             }
279         }       /* if (wrong buffer) ... */
280
281         /* now we have the right buffer loaded.  Copy out the
282          * data from here to the user's buffer.
283          */
284         bufIndex = offsetp->LowPart & (cm_data.blockSize - 1);
285
286         /* and figure out how many bytes we want from this buffer */
287         nbytes = cm_data.blockSize - bufIndex;  /* what remains in buffer */
288         if (nbytes > length)
289             nbytes = length;    /* don't go past end of request */
290
291         /* now copy the data */
292         memcpy(bufp->datap + bufIndex, bufferp, nbytes);
293         buf_SetDirty(bufp, reqp, bufIndex, nbytes, userp);
294
295         /* adjust lengthers, pointers, etc. */
296         bufferp += nbytes;
297         length -= nbytes;
298         written += nbytes;
299         thyper.LowPart = nbytes;
300         thyper.HighPart = 0;
301         *offsetp = LargeIntegerAdd(thyper, *offsetp);
302     } /* while 1 */
303
304   done:
305     if (writtenp)
306         *writtenp = written;
307
308     if (bufp) {
309         lock_ReleaseMutex(&bufp->mx);
310         buf_Release(bufp);
311     }
312
313     if (code == 0 && doWriteBack) {
314         rock_BkgStore_t *rockp = malloc(sizeof(*rockp));
315
316         if (rockp) {
317             code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_ASYNCSTORE);
318             if (code == 0) {
319                 rockp->length = writeBackLength;
320                 rockp->offset = writeBackOffset;
321
322                 cm_QueueBKGRequest(scp, cm_BkgStore, rockp, userp, reqp);
323
324                 /* rock is freed by cm_BkgDaemon */
325             } else {
326                 free(rockp);
327             }
328         }
329     }
330
331     /* cm_SyncOpDone is called when cm_BkgStore completes */
332     return code;
333 }
334
335