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 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
46 /* now we have the entry locked, look up the length */
47 fileLength = scp->length;
49 /* adjust count down so that it won't go past EOF */
50 thyper.LowPart = count;
52 thyper = LargeIntegerAdd(offset, thyper); /* where read should end */
54 if (LargeIntegerGreaterThan(thyper, fileLength)) {
55 /* we'd read past EOF, so just stop at fileLength bytes.
56 * Start by computing how many bytes remain in the file.
58 thyper = LargeIntegerSubtract(fileLength, offset);
60 /* if we are past EOF, read 0 bytes */
61 if (LargeIntegerLessThanZero(thyper))
64 count = thyper.LowPart;
69 /* now, copy the data one buffer at a time,
70 * until we've filled the request packet
73 /* if we've copied all the data requested, we're done */
74 if (count <= 0) break;
76 /* otherwise, load up a buffer of data */
77 thyper.HighPart = offset.HighPart;
78 thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
79 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
85 lock_ReleaseMutex(&scp->mx);
87 lock_ObtainRead(&scp->bufCreateLock);
88 code = buf_Get(scp, &thyper, &bufferp);
89 lock_ReleaseRead(&scp->bufCreateLock);
91 lock_ObtainMutex(&scp->mx);
93 bufferOffset = thyper;
95 /* now get the data in the cache */
97 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
98 CM_SCACHESYNC_NEEDCALLBACK
99 | CM_SCACHESYNC_READ);
103 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
105 if (cm_HaveBuffer(scp, bufferp, 0)) break;
107 /* otherwise, load the buffer and try again */
108 code = cm_GetBuffer(scp, bufferp, NULL, userp, &req);
112 buf_Release(bufferp);
116 } /* if (wrong buffer) ... */
118 /* now we have the right buffer loaded. Copy out the
119 * data from here to the user's buffer.
121 bufIndex = offset.LowPart & (buf_bufferSize - 1);
123 /* and figure out how many bytes we want from this buffer */
124 nbytes = buf_bufferSize - bufIndex; /* what remains in buffer */
125 if (nbytes > count) nbytes = count; /* don't go past EOF */
127 /* now copy the data */
128 memcpy(op, bufferp->datap + bufIndex, nbytes);
130 /* adjust counters, pointers, etc. */
133 thyper.LowPart = nbytes;
135 offset = LargeIntegerAdd(thyper, offset);
139 lock_ReleaseMutex(&scp->mx);
140 //lock_ReleaseMutex(&fidp->mx);
142 buf_Release(bufferp);
144 if (code == 0 && sequential)
145 cm_ConsiderPrefetch(scp, &lastByte, userp, &req);
151 long WriteData(cm_scache_t *scp, osi_hyper_t offset, long count, char *op,
152 cm_user_t *userp, long *writtenp)
156 osi_hyper_t fileLength; /* file's length at start of write */
157 osi_hyper_t minLength; /* don't read past this */
158 afs_uint32 nbytes; /* # of bytes to transfer this iteration */
160 osi_hyper_t thyper; /* hyper tmp variable */
161 osi_hyper_t bufferOffset;
162 afs_uint32 bufIndex; /* index in buffer where our data is */
164 osi_hyper_t writeBackOffset; /* offset of region to write back when
174 lock_ObtainMutex(&scp->mx);
176 /* start by looking up the file's end */
177 code = cm_SyncOp(scp, NULL, userp, &req, 0,
178 CM_SCACHESYNC_NEEDCALLBACK
179 | CM_SCACHESYNC_SETSTATUS
180 | CM_SCACHESYNC_GETSTATUS);
184 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_GETSTATUS);
187 /* make sure we have a writable FD */
188 if (!(fidp->flags & SMB_FID_OPENWRITE)) {
189 code = CM_ERROR_BADFDOP;
194 /* now we have the entry locked, look up the length */
195 fileLength = scp->length;
196 minLength = fileLength;
197 if (LargeIntegerGreaterThan(minLength, scp->serverLength))
198 minLength = scp->serverLength;
200 /* adjust file length if we extend past EOF */
201 thyper.LowPart = count;
203 thyper = LargeIntegerAdd(offset, thyper); /* where write should end */
204 if (LargeIntegerGreaterThan(thyper, fileLength)) {
205 /* we'd write past EOF, so extend the file */
206 scp->mask |= CM_SCACHEMASK_LENGTH;
207 scp->length = thyper;
208 filter |= (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE);
210 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
212 /* now, if the new position (thyper) and the old (offset) are in
213 * different storeback windows, remember to store back the previous
214 * storeback window when we're done with the write.
216 if ((thyper.LowPart & (-cm_chunkSize)) !=
217 (offset.LowPart & (-cm_chunkSize))) {
218 /* they're different */
220 writeBackOffset.HighPart = offset.HighPart;
221 writeBackOffset.LowPart = offset.LowPart & (-cm_chunkSize);
226 /* now, copy the data one buffer at a time, until we've filled the
229 /* if we've copied all the data requested, we're done */
230 if (count <= 0) break;
232 /* handle over quota or out of space */
233 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
238 /* otherwise, load up a buffer of data */
239 thyper.HighPart = offset.HighPart;
240 thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
241 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
244 lock_ReleaseMutex(&bufferp->mx);
245 buf_Release(bufferp);
248 lock_ReleaseMutex(&scp->mx);
250 lock_ObtainRead(&scp->bufCreateLock);
251 code = buf_Get(scp, &thyper, &bufferp);
252 lock_ReleaseRead(&scp->bufCreateLock);
254 lock_ObtainMutex(&bufferp->mx);
255 lock_ObtainMutex(&scp->mx);
259 bufferOffset = thyper;
261 /* now get the data in the cache */
263 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
264 CM_SCACHESYNC_NEEDCALLBACK
265 | CM_SCACHESYNC_WRITE
266 | CM_SCACHESYNC_BUFLOCKED);
270 cm_SyncOpDone(scp, bufferp,
271 CM_SCACHESYNC_NEEDCALLBACK
272 | CM_SCACHESYNC_WRITE
273 | CM_SCACHESYNC_BUFLOCKED);
275 /* If we're overwriting the entire buffer, or
276 * if we're writing at or past EOF, mark the
277 * buffer as current so we don't call
278 * cm_GetBuffer. This skips the fetch from the
279 * server in those cases where we're going to
280 * obliterate all the data in the buffer anyway,
281 * or in those cases where there is no useful
282 * data at the server to start with.
284 * Use minLength instead of scp->length, since
285 * the latter has already been updated by this
288 if (LargeIntegerGreaterThanOrEqualTo(bufferp->offset, minLength) ||
289 LargeIntegerEqualTo(offset, bufferp->offset) &&
290 (count >= buf_bufferSize ||
291 LargeIntegerGreaterThanOrEqualTo(LargeIntegerAdd(offset, ConvertLongToLargeInteger(count)), minLength))) {
292 if (count < buf_bufferSize
293 && bufferp->dataVersion == -1)
294 memset(bufferp->datap, 0,
296 bufferp->dataVersion = scp->dataVersion;
299 if (cm_HaveBuffer(scp, bufferp, 1)) break;
301 /* otherwise, load the buffer and try again */
302 lock_ReleaseMutex(&bufferp->mx);
303 code = cm_GetBuffer(scp, bufferp, NULL, userp,
305 lock_ReleaseMutex(&scp->mx);
306 lock_ObtainMutex(&bufferp->mx);
307 lock_ObtainMutex(&scp->mx);
312 lock_ReleaseMutex(&bufferp->mx);
313 buf_Release(bufferp);
317 } /* if (wrong buffer) ... */
319 /* now we have the right buffer loaded. Copy out the
320 * data from here to the user's buffer.
322 bufIndex = offset.LowPart & (buf_bufferSize - 1);
324 /* and figure out how many bytes we want from this buffer */
325 nbytes = buf_bufferSize - bufIndex; /* what remains in buffer */
327 nbytes = count; /* don't go past end of request */
329 /* now copy the data */
330 memcpy(bufferp->datap + bufIndex, op, nbytes);
331 buf_SetDirty(bufferp, bufIndex, nbytes);
333 /* and record the last writer */
334 if (bufferp->userp != userp) {
337 cm_ReleaseUser(bufferp->userp);
338 bufferp->userp = userp;
341 /* adjust counters, pointers, etc. */
345 thyper.LowPart = nbytes;
347 offset = LargeIntegerAdd(thyper, offset);
351 lock_ReleaseMutex(&scp->mx);
353 lock_ReleaseMutex(&bufferp->mx);
354 buf_Release(bufferp);
357 if (code == 0 && doWriteBack) {
358 lock_ObtainMutex(&scp->mx);
359 cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE);
360 lock_ReleaseMutex(&scp->mx);
361 cm_QueueBKGRequest(scp, cm_BkgStore, writeBackOffset.LowPart,
362 writeBackOffset.HighPart, cm_chunkSize, 0, userp);
365 /* cm_SyncOpDone is called when cm_BkgStore completes */