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