Windows: Add cm_req_t parameter to buf_Get* functions
[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 <osi.h>
11 #include "afsd.h"
12
13 #define CM_BUF_SIZE             4096
14 long buf_bufferSize = CM_BUF_SIZE;
15
16 long ReadData(cm_scache_t *scp, osi_hyper_t offset, long count, char *op,
17               cm_user_t *userp, long *readp)
18 {
19     //osi_hyper_t offset;
20     long code;
21     cm_buf_t *bufferp;
22     osi_hyper_t fileLength;
23     osi_hyper_t thyper;
24     osi_hyper_t lastByte;
25     osi_hyper_t bufferOffset;
26     long bufIndex, nbytes;
27     int sequential = 0;
28     cm_req_t req;
29
30     cm_InitReq(&req);
31
32     bufferp = NULL;
33
34     lock_ObtainWrite(&scp->rw);
35
36     /* start by looking up the file's end */
37     code = cm_SyncOp(scp, NULL, userp, &req, 0,
38                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
39     if (code) 
40         goto done;
41
42     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
43
44     /* now we have the entry locked, look up the length */
45     fileLength = scp->length;
46
47     /* adjust count down so that it won't go past EOF */
48     thyper.LowPart = count;
49     thyper.HighPart = 0;
50     thyper = LargeIntegerAdd(offset, thyper);   /* where read should end */
51     lastByte = thyper;
52     if (LargeIntegerGreaterThan(thyper, fileLength)) {
53         /* we'd read past EOF, so just stop at fileLength bytes.
54         * Start by computing how many bytes remain in the file.
55         */
56         thyper = LargeIntegerSubtract(fileLength, offset);
57
58         /* if we are past EOF, read 0 bytes */
59         if (LargeIntegerLessThanZero(thyper))
60             count = 0;
61         else
62             count = thyper.LowPart;
63     }       
64
65     *readp = count;
66
67     /* now, copy the data one buffer at a time,
68      * until we've filled the request packet
69      */
70     while (1) {
71         /* if we've copied all the data requested, we're done */
72         if (count <= 0) break;
73
74         /* otherwise, load up a buffer of data */
75         thyper.HighPart = offset.HighPart;
76         thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
77         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
78             /* wrong buffer */
79             if (bufferp) {
80                 buf_Release(bufferp);
81                 bufferp = NULL;
82             }
83             lock_ReleaseWrite(&scp->rw);
84
85             code = buf_Get(scp, &thyper, &req, &bufferp);
86
87             lock_ObtainWrite(&scp->rw);
88             if (code) goto done;
89             bufferOffset = thyper;
90
91             /* now get the data in the cache */
92             while (1) {
93                 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
94                                   CM_SCACHESYNC_NEEDCALLBACK
95                                   | CM_SCACHESYNC_READ);
96                 if (code) 
97                     goto done;
98
99                 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
100
101                 if (cm_HaveBuffer(scp, bufferp, 0)) break;
102
103                 /* otherwise, load the buffer and try again */
104                 code = cm_GetBuffer(scp, bufferp, NULL, userp, &req);
105                 if (code) break;
106             }
107             if (code) {
108                 buf_Release(bufferp);
109                 bufferp = NULL;
110                 goto done;
111             }
112         }       /* if (wrong buffer) ... */
113
114         /* now we have the right buffer loaded.  Copy out the
115          * data from here to the user's buffer.
116          */
117         bufIndex = offset.LowPart & (buf_bufferSize - 1);
118
119         /* and figure out how many bytes we want from this buffer */
120         nbytes = buf_bufferSize - bufIndex;     /* what remains in buffer */
121         if (nbytes > count) nbytes = count;     /* don't go past EOF */
122
123         /* now copy the data */
124         memcpy(op, bufferp->datap + bufIndex, nbytes);
125
126         /* adjust counters, pointers, etc. */
127         op += nbytes;
128         count -= nbytes;
129         thyper.LowPart = nbytes;
130         thyper.HighPart = 0;
131         offset = LargeIntegerAdd(thyper, offset);
132     } /* while 1 */
133
134   done:
135     lock_ReleaseWrite(&scp->rw);
136     //lock_ReleaseMutex(&fidp->mx);
137     if (bufferp) 
138         buf_Release(bufferp);
139
140     if (code == 0 && sequential)
141         cm_ConsiderPrefetch(scp, &lastByte, *readp, userp, &req);
142
143     return code;
144 }       
145
146
147 long WriteData(cm_scache_t *scp, osi_hyper_t offset, long count, char *op,
148                cm_user_t *userp, long *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 *bufferp;
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;
160     osi_hyper_t writeBackOffset;/* offset of region to write back when
161     * I/O is done */
162     DWORD filter = 0;
163     cm_req_t req;
164
165     cm_InitReq(&req);
166
167     bufferp = NULL;
168     doWriteBack = 0;
169
170     lock_ObtainWrite(&scp->rw);
171
172     /* start by looking up the file's end */
173     code = cm_SyncOp(scp, NULL, userp, &req, 0,
174                      CM_SCACHESYNC_NEEDCALLBACK
175                       | CM_SCACHESYNC_SETSTATUS
176                       | CM_SCACHESYNC_GETSTATUS);
177     if (code) 
178         goto done;
179     
180     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_GETSTATUS);
181
182 #if 0
183     /* make sure we have a writable FD */
184     if (!(fidp->flags & SMB_FID_OPENWRITE)) {
185     code = CM_ERROR_BADFDOP;
186     goto done;
187     }
188 #endif
189         
190     /* now we have the entry locked, look up the length */
191     fileLength = scp->length;
192     minLength = fileLength;
193     if (LargeIntegerGreaterThan(minLength, scp->serverLength))
194         minLength = scp->serverLength;
195
196     /* adjust file length if we extend past EOF */
197     thyper.LowPart = count;
198     thyper.HighPart = 0;
199     thyper = LargeIntegerAdd(offset, thyper);   /* where write should end */
200     if (LargeIntegerGreaterThan(thyper, fileLength)) {
201         /* we'd write past EOF, so extend the file */
202         scp->mask |= CM_SCACHEMASK_LENGTH;
203         scp->length = thyper;
204         filter |= (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE);
205     } else
206         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
207
208     /* now, if the new position (thyper) and the old (offset) are in
209      * different storeback windows, remember to store back the previous
210      * storeback window when we're done with the write.
211      */
212     if ((thyper.LowPart & (-cm_chunkSize)) !=
213          (offset.LowPart & (-cm_chunkSize))) {
214         /* they're different */
215         doWriteBack = 1;
216         writeBackOffset.HighPart = offset.HighPart;
217         writeBackOffset.LowPart = offset.LowPart & (-cm_chunkSize);
218     }
219
220     *writtenp = count;
221
222     /* now, copy the data one buffer at a time, until we've filled the
223      * request packet */
224     while (1) {
225         /* if we've copied all the data requested, we're done */
226         if (count <= 0) break;
227
228         /* handle over quota or out of space */
229         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
230             *writtenp = written;
231             break;
232         }
233
234         /* otherwise, load up a buffer of data */
235         thyper.HighPart = offset.HighPart;
236         thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
237         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
238             /* wrong buffer */
239             if (bufferp) {
240                 lock_ReleaseMutex(&bufferp->mx);
241                 buf_Release(bufferp);
242                 bufferp = NULL;
243             }   
244             lock_ReleaseWrite(&scp->rw);
245
246             code = buf_Get(scp, &thyper, &req, &bufferp);
247
248             lock_ObtainMutex(&bufferp->mx);
249             lock_ObtainWrite(&scp->rw);
250             if (code) 
251                 goto done;
252
253             bufferOffset = thyper;
254
255             /* now get the data in the cache */
256             while (1) {
257                 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
258                                   CM_SCACHESYNC_NEEDCALLBACK
259                                   | CM_SCACHESYNC_WRITE
260                                   | CM_SCACHESYNC_BUFLOCKED);
261                 if (code) 
262                     goto done;
263                        
264                 cm_SyncOpDone(scp, bufferp, 
265                                CM_SCACHESYNC_NEEDCALLBACK 
266                                | CM_SCACHESYNC_WRITE 
267                                | CM_SCACHESYNC_BUFLOCKED);
268
269                 /* If we're overwriting the entire buffer, or
270                  * if we're writing at or past EOF, mark the
271                  * buffer as current so we don't call
272                  * cm_GetBuffer.  This skips the fetch from the
273                  * server in those cases where we're going to 
274                  * obliterate all the data in the buffer anyway,
275                  * or in those cases where there is no useful
276                  * data at the server to start with.
277                  *
278                  * Use minLength instead of scp->length, since
279                  * the latter has already been updated by this
280                  * call.
281                  */
282                 if (LargeIntegerGreaterThanOrEqualTo(bufferp->offset, minLength) ||
283                      LargeIntegerEqualTo(offset, bufferp->offset) &&
284                      (count >= buf_bufferSize ||
285                        LargeIntegerGreaterThanOrEqualTo(LargeIntegerAdd(offset, ConvertLongToLargeInteger(count)), minLength))) {
286                     if (count < buf_bufferSize
287                          && bufferp->dataVersion == CM_BUF_VERSION_BAD)
288                         memset(bufferp->datap, 0,
289                                 buf_bufferSize);
290                     bufferp->dataVersion = scp->dataVersion;
291                 }
292
293                 if (cm_HaveBuffer(scp, bufferp, 1)) break;
294
295                 /* otherwise, load the buffer and try again */
296                 lock_ReleaseMutex(&bufferp->mx);
297                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
298                                      &req);
299                 lock_ReleaseWrite(&scp->rw);
300                 lock_ObtainMutex(&bufferp->mx);
301                 lock_ObtainWrite(&scp->rw);
302                 if (code) 
303                     break;
304             }
305             if (code) {
306                 lock_ReleaseMutex(&bufferp->mx);
307                 buf_Release(bufferp);
308                 bufferp = NULL;
309                 goto done;
310             }
311         }       /* if (wrong buffer) ... */
312
313         /* now we have the right buffer loaded.  Copy out the
314          * data from here to the user's buffer.
315          */
316         bufIndex = offset.LowPart & (buf_bufferSize - 1);
317
318         /* and figure out how many bytes we want from this buffer */
319         nbytes = buf_bufferSize - bufIndex;     /* what remains in buffer */
320         if (nbytes > count) 
321             nbytes = count;     /* don't go past end of request */
322
323         /* now copy the data */
324         memcpy(bufferp->datap + bufIndex, op, nbytes);
325         buf_SetDirty(bufferp, bufIndex, nbytes, userp);
326
327         /* adjust counters, pointers, etc. */
328         op += nbytes;
329         count -= nbytes;
330         written += nbytes;
331         thyper.LowPart = nbytes;
332         thyper.HighPart = 0;
333         offset = LargeIntegerAdd(thyper, offset);
334     } /* while 1 */
335
336   done:
337     lock_ReleaseWrite(&scp->rw);
338     if (bufferp) {
339         lock_ReleaseMutex(&bufferp->mx);
340         buf_Release(bufferp);
341     }
342
343     if (code == 0 && doWriteBack) {
344         lock_ObtainWrite(&scp->rw);
345         code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE);
346         lock_ReleaseWrite(&scp->rw);
347         if (code == 0)
348             cm_QueueBKGRequest(scp, cm_BkgStore, writeBackOffset.LowPart,
349                                writeBackOffset.HighPart, cm_chunkSize, 0, userp);
350     }   
351
352     /* cm_SyncOpDone is called when cm_BkgStore completes */
353     return code;
354 }
355
356