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
10 #include <afsconfig.h>
11 #include <afs/param.h>
17 #define CM_BUF_SIZE 4096
18 long buf_bufferSize = CM_BUF_SIZE;
20 long ReadData(cm_scache_t *scp, osi_hyper_t offset, long count, char *op,
21 cm_user_t *userp, long *readp)
26 osi_hyper_t fileLength;
29 osi_hyper_t bufferOffset;
30 long bufIndex, nbytes;
38 lock_ObtainWrite(&scp->rw);
40 /* start by looking up the file's end */
41 code = cm_SyncOp(scp, NULL, userp, &req, 0,
42 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
46 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
48 /* now we have the entry locked, look up the length */
49 fileLength = scp->length;
51 /* adjust count down so that it won't go past EOF */
52 thyper.LowPart = count;
54 thyper = LargeIntegerAdd(offset, thyper); /* where read should end */
56 if (LargeIntegerGreaterThan(thyper, fileLength)) {
57 /* we'd read past EOF, so just stop at fileLength bytes.
58 * Start by computing how many bytes remain in the file.
60 thyper = LargeIntegerSubtract(fileLength, offset);
62 /* if we are past EOF, read 0 bytes */
63 if (LargeIntegerLessThanZero(thyper))
66 count = thyper.LowPart;
71 /* now, copy the data one buffer at a time,
72 * until we've filled the request packet
75 /* if we've copied all the data requested, we're done */
76 if (count <= 0) break;
78 /* otherwise, load up a buffer of data */
79 thyper.HighPart = offset.HighPart;
80 thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
81 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
87 lock_ReleaseWrite(&scp->rw);
89 code = buf_Get(scp, &thyper, &req, &bufferp);
91 lock_ObtainWrite(&scp->rw);
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_ReleaseWrite(&scp->rw);
140 //lock_ReleaseMutex(&fidp->mx);
142 buf_Release(bufferp);
144 if (code == 0 && sequential)
145 cm_ConsiderPrefetch(scp, &lastByte, *readp, 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_ObtainWrite(&scp->rw);
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_ReleaseWrite(&scp->rw);
250 code = buf_Get(scp, &thyper, &req, &bufferp);
252 lock_ObtainMutex(&bufferp->mx);
253 lock_ObtainWrite(&scp->rw);
257 bufferOffset = thyper;
259 /* now get the data in the cache */
261 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
262 CM_SCACHESYNC_NEEDCALLBACK
263 | CM_SCACHESYNC_WRITE
264 | CM_SCACHESYNC_BUFLOCKED);
268 cm_SyncOpDone(scp, bufferp,
269 CM_SCACHESYNC_NEEDCALLBACK
270 | CM_SCACHESYNC_WRITE
271 | CM_SCACHESYNC_BUFLOCKED);
273 /* If we're overwriting the entire buffer, or
274 * if we're writing at or past EOF, mark the
275 * buffer as current so we don't call
276 * cm_GetBuffer. This skips the fetch from the
277 * server in those cases where we're going to
278 * obliterate all the data in the buffer anyway,
279 * or in those cases where there is no useful
280 * data at the server to start with.
282 * Use minLength instead of scp->length, since
283 * the latter has already been updated by this
286 if (LargeIntegerGreaterThanOrEqualTo(bufferp->offset, minLength) ||
287 LargeIntegerEqualTo(offset, bufferp->offset) &&
288 (count >= buf_bufferSize ||
289 LargeIntegerGreaterThanOrEqualTo(LargeIntegerAdd(offset, ConvertLongToLargeInteger(count)), minLength))) {
290 if (count < buf_bufferSize
291 && bufferp->dataVersion == CM_BUF_VERSION_BAD)
292 memset(bufferp->datap, 0,
294 bufferp->dataVersion = scp->dataVersion;
297 if (cm_HaveBuffer(scp, bufferp, 1)) break;
299 /* otherwise, load the buffer and try again */
300 lock_ReleaseMutex(&bufferp->mx);
301 code = cm_GetBuffer(scp, bufferp, NULL, userp,
303 lock_ReleaseWrite(&scp->rw);
304 lock_ObtainMutex(&bufferp->mx);
305 lock_ObtainWrite(&scp->rw);
310 lock_ReleaseMutex(&bufferp->mx);
311 buf_Release(bufferp);
315 } /* if (wrong buffer) ... */
317 /* now we have the right buffer loaded. Copy out the
318 * data from here to the user's buffer.
320 bufIndex = offset.LowPart & (buf_bufferSize - 1);
322 /* and figure out how many bytes we want from this buffer */
323 nbytes = buf_bufferSize - bufIndex; /* what remains in buffer */
325 nbytes = count; /* don't go past end of request */
327 /* now copy the data */
328 memcpy(bufferp->datap + bufIndex, op, nbytes);
329 buf_SetDirty(bufferp, bufIndex, nbytes, userp);
331 /* adjust counters, pointers, etc. */
335 thyper.LowPart = nbytes;
337 offset = LargeIntegerAdd(thyper, offset);
341 lock_ReleaseWrite(&scp->rw);
343 lock_ReleaseMutex(&bufferp->mx);
344 buf_Release(bufferp);
347 if (code == 0 && doWriteBack) {
348 lock_ObtainWrite(&scp->rw);
349 code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE);
350 lock_ReleaseWrite(&scp->rw);
352 cm_QueueBKGRequest(scp, cm_BkgStore, writeBackOffset.LowPart,
353 writeBackOffset.HighPart, cm_chunkSize, 0, userp);
356 /* cm_SyncOpDone is called when cm_BkgStore completes */