2 * Copyright 2000, International Business Machines Corporation and others.
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
15 #define CM_BUF_SIZE 4096
16 long buf_bufferSize = CM_BUF_SIZE;
18 long ReadData(cm_scache_t *scp, osi_hyper_t offset, long count, char *op,
19 cm_user_t *userp, long *readp)
24 osi_hyper_t fileLength;
27 osi_hyper_t bufferOffset;
28 long bufIndex, nbytes;
36 lock_ObtainMutex(&scp->mx);
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);
44 /* now we have the entry locked, look up the length */
45 fileLength = scp->length;
47 /* adjust count down so that it won't go past EOF */
48 thyper.LowPart = count;
50 thyper = LargeIntegerAdd(offset, thyper); /* where read should end */
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.
56 thyper = LargeIntegerSubtract(fileLength, offset);
58 /* if we are past EOF, read 0 bytes */
59 if (LargeIntegerLessThanZero(thyper))
62 count = thyper.LowPart;
67 /* now, copy the data one buffer at a time,
68 * until we've filled the request packet
71 /* if we've copied all the data requested, we're done */
72 if (count <= 0) break;
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)) {
83 lock_ReleaseMutex(&scp->mx);
85 lock_ObtainRead(&scp->bufCreateLock);
86 code = buf_Get(scp, &thyper, &bufferp);
87 lock_ReleaseRead(&scp->bufCreateLock);
89 lock_ObtainMutex(&scp->mx);
91 bufferOffset = thyper;
93 /* now get the data in the cache */
95 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
96 CM_SCACHESYNC_NEEDCALLBACK
97 | CM_SCACHESYNC_READ);
101 if (cm_HaveBuffer(scp, bufferp, 0)) break;
103 /* otherwise, load the buffer and try again */
104 code = cm_GetBuffer(scp, bufferp, NULL, userp, &req);
108 buf_Release(bufferp);
112 } /* if (wrong buffer) ... */
114 /* now we have the right buffer loaded. Copy out the
115 * data from here to the user's buffer.
117 bufIndex = offset.LowPart & (buf_bufferSize - 1);
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 */
123 /* now copy the data */
124 memcpy(op, bufferp->datap + bufIndex, nbytes);
126 /* adjust counters, pointers, etc. */
129 thyper.LowPart = nbytes;
131 offset = LargeIntegerAdd(thyper, offset);
135 lock_ReleaseMutex(&scp->mx);
136 //lock_ReleaseMutex(&fidp->mx);
138 buf_Release(bufferp);
140 if (code == 0 && sequential)
141 cm_ConsiderPrefetch(scp, &lastByte, userp, &req);
147 long WriteData(cm_scache_t *scp, osi_hyper_t offset, long count, char *op,
148 cm_user_t *userp, long *writtenp)
152 osi_hyper_t fileLength; /* file's length at start of write */
153 osi_hyper_t minLength; /* don't read past this */
154 long nbytes; /* # of bytes to transfer this iteration */
156 osi_hyper_t thyper; /* hyper tmp variable */
157 osi_hyper_t bufferOffset;
158 long bufIndex; /* index in buffer where our data is */
160 osi_hyper_t writeBackOffset; /* offset of region to write back when
170 lock_ObtainMutex(&scp->mx);
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);
181 /* make sure we have a writable FD */
182 if (!(fidp->flags & SMB_FID_OPENWRITE)) {
183 code = CM_ERROR_BADFDOP;
188 /* now we have the entry locked, look up the length */
189 fileLength = scp->length;
190 minLength = fileLength;
191 if (LargeIntegerGreaterThan(minLength, scp->serverLength))
192 minLength = scp->serverLength;
194 /* adjust file length if we extend past EOF */
195 thyper.LowPart = count;
197 thyper = LargeIntegerAdd(offset, thyper); /* where write should end */
198 if (LargeIntegerGreaterThan(thyper, fileLength)) {
199 /* we'd write past EOF, so extend the file */
200 scp->mask |= CM_SCACHEMASK_LENGTH;
201 scp->length = thyper;
202 filter |= (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE);
204 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
206 /* now, if the new position (thyper) and the old (offset) are in
207 * different storeback windows, remember to store back the previous
208 * storeback window when we're done with the write.
210 if ((thyper.LowPart & (-cm_chunkSize)) !=
211 (offset.LowPart & (-cm_chunkSize))) {
212 /* they're different */
214 writeBackOffset.HighPart = offset.HighPart;
215 writeBackOffset.LowPart = offset.LowPart & (-cm_chunkSize);
220 /* now, copy the data one buffer at a time, until we've filled the
223 /* if we've copied all the data requested, we're done */
224 if (count <= 0) break;
226 /* handle over quota or out of space */
227 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
232 /* otherwise, load up a buffer of data */
233 thyper.HighPart = offset.HighPart;
234 thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
235 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
238 lock_ReleaseMutex(&bufferp->mx);
239 buf_Release(bufferp);
242 lock_ReleaseMutex(&scp->mx);
244 lock_ObtainRead(&scp->bufCreateLock);
245 code = buf_Get(scp, &thyper, &bufferp);
246 lock_ReleaseRead(&scp->bufCreateLock);
248 lock_ObtainMutex(&bufferp->mx);
249 lock_ObtainMutex(&scp->mx);
253 bufferOffset = thyper;
255 /* now get the data in the cache */
257 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
258 CM_SCACHESYNC_NEEDCALLBACK
259 | CM_SCACHESYNC_WRITE
260 | CM_SCACHESYNC_BUFLOCKED);
264 /* If we're overwriting the entire buffer, or
265 * if we're writing at or past EOF, mark the
266 * buffer as current so we don't call
267 * cm_GetBuffer. This skips the fetch from the
268 * server in those cases where we're going to
269 * obliterate all the data in the buffer anyway,
270 * or in those cases where there is no useful
271 * data at the server to start with.
273 * Use minLength instead of scp->length, since
274 * the latter has already been updated by this
277 if (LargeIntegerGreaterThanOrEqualTo(bufferp->offset, minLength) ||
278 LargeIntegerEqualTo(offset, bufferp->offset) &&
279 (count >= buf_bufferSize ||
280 LargeIntegerGreaterThanOrEqualTo(LargeIntegerAdd(offset, ConvertLongToLargeInteger(count)), minLength))) {
281 if (count < buf_bufferSize
282 && bufferp->dataVersion == -1)
283 memset(bufferp->datap, 0,
285 bufferp->dataVersion = scp->dataVersion;
288 if (cm_HaveBuffer(scp, bufferp, 1)) break;
290 /* otherwise, load the buffer and try again */
291 lock_ReleaseMutex(&bufferp->mx);
292 code = cm_GetBuffer(scp, bufferp, NULL, userp,
294 lock_ReleaseMutex(&scp->mx);
295 lock_ObtainMutex(&bufferp->mx);
296 lock_ObtainMutex(&scp->mx);
301 lock_ReleaseMutex(&bufferp->mx);
302 buf_Release(bufferp);
306 } /* if (wrong buffer) ... */
308 /* now we have the right buffer loaded. Copy out the
309 * data from here to the user's buffer.
311 bufIndex = offset.LowPart & (buf_bufferSize - 1);
313 /* and figure out how many bytes we want from this buffer */
314 nbytes = buf_bufferSize - bufIndex; /* what remains in buffer */
316 nbytes = count; /* don't go past end of request */
318 /* now copy the data */
321 dosmemget((dos_ptr)op, nbytes, bufferp->datap + bufIndex);
324 memcpy(bufferp->datap + bufIndex, op, nbytes);
325 buf_SetDirty(bufferp);
327 /* and record the last writer */
328 if (bufferp->userp != userp) {
331 cm_ReleaseUser(bufferp->userp);
332 bufferp->userp = userp;
335 /* adjust counters, pointers, etc. */
339 thyper.LowPart = nbytes;
341 offset = LargeIntegerAdd(thyper, offset);
345 lock_ReleaseMutex(&scp->mx);
347 lock_ReleaseMutex(&bufferp->mx);
348 buf_Release(bufferp);
352 if (code == 0 /* && filter != 0 && (fidp->flags & SMB_FID_NTOPEN)
353 && (fidp->NTopen_dscp->flags & CM_SCACHEFLAG_ANYWATCH)*/) {
354 smb_NotifyChange(FILE_ACTION_MODIFIED, filter,
355 fidp->NTopen_dscp, fidp->NTopen_pathp,
360 if (code == 0 && doWriteBack) {
361 lock_ObtainMutex(&scp->mx);
362 cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE);
363 lock_ReleaseMutex(&scp->mx);
364 cm_QueueBKGRequest(scp, cm_BkgStore, writeBackOffset.LowPart,
365 writeBackOffset.HighPart, cm_chunkSize, 0, userp);