afs_uint32 raw_WriteData( cm_scache_t *scp, osi_hyper_t *offsetp, afs_uint32 length, char *bufferp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 *writtenp) { long code = 0; long written = 0; osi_hyper_t fileLength; /* file's length at start of write */ osi_hyper_t minLength; /* don't read past this */ afs_uint32 nbytes; /* # of bytes to transfer this iteration */ cm_buf_t *bufp = NULL; osi_hyper_t thyper; /* hyper tmp variable */ osi_hyper_t bufferOffset; afs_uint32 bufIndex; /* index in buffer where our data is */ int doWriteBack = 0; osi_hyper_t writeBackOffset;/* offset of region to write back when I/O is done */ afs_uint32 writeBackLength; DWORD filter = 0; /* start by looking up the file's end */ code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_GETSTATUS); if (code) goto done; cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_GETSTATUS); /* now we have the entry locked, look up the length */ fileLength = scp->length; minLength = fileLength; if (LargeIntegerGreaterThan(minLength, scp->serverLength)) minLength = scp->serverLength; /* adjust file length if we extend past EOF */ thyper.LowPart = length; thyper.HighPart = 0; thyper = LargeIntegerAdd(*offsetp, thyper); /* where write should end */ if (LargeIntegerGreaterThan(thyper, fileLength)) { /* we'd write past EOF, so extend the file */ scp->mask |= CM_SCACHEMASK_LENGTH; scp->length = thyper; filter |= (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE); } else filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; doWriteBack = 1; writeBackOffset = *offsetp; writeBackLength = length; /* now, copy the data one buffer at a time, until we've filled the * request packet */ while (1) { /* if we've copied all the data requested, we're done */ if (length <= 0) break; /* otherwise, load up a buffer of data */ thyper.HighPart = offsetp->HighPart; thyper.LowPart = offsetp->LowPart & ~(cm_data.blockSize-1); if (!bufp || !LargeIntegerEqualTo(thyper, bufferOffset)) { /* wrong buffer */ if (bufp) { lock_ReleaseMutex(&bufp->mx); buf_Release(bufp); bufp = NULL; } lock_ReleaseWrite(&scp->rw); code = buf_Get(scp, &thyper, reqp, 0, &bufp); if (bufp) lock_ObtainMutex(&bufp->mx); lock_ObtainWrite(&scp->rw); if (code) goto done; bufferOffset = thyper; /* now get the data in the cache */ while (1) { code = cm_SyncOp(scp, bufp, userp, reqp, 0, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_WRITE | CM_SCACHESYNC_BUFLOCKED); if (code) goto done; cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_WRITE | CM_SCACHESYNC_BUFLOCKED); /* If we're overwriting the entire buffer, or * if we're writing at or past EOF, mark the * buffer as current so we don't call * cm_GetBuffer. This skips the fetch from the * server in those cases where we're going to * obliterate all the data in the buffer anyway, * or in those cases where there is no useful * data at the server to start with. * * Use minLength instead of scp->length, since * the latter has already been updated by this * call. */ if (LargeIntegerGreaterThanOrEqualTo(bufp->offset, minLength) || LargeIntegerEqualTo(*offsetp, bufp->offset) && (length >= cm_data.blockSize || LargeIntegerGreaterThanOrEqualTo(LargeIntegerAdd(*offsetp, ConvertLongToLargeInteger(length)), minLength))) { if (length < cm_data.blockSize && bufp->dataVersion == CM_BUF_VERSION_BAD) memset(bufp->datap, 0, cm_data.blockSize); bufp->dataVersion = scp->dataVersion; } if (cm_HaveBuffer(scp, bufp, 1)) break; /* otherwise, load the buffer and try again */ lock_ReleaseMutex(&bufp->mx); code = cm_GetBuffer(scp, bufp, NULL, userp, reqp); lock_ReleaseWrite(&scp->rw); lock_ObtainMutex(&bufp->mx); lock_ObtainWrite(&scp->rw); if (code) break; } if (code) { lock_ReleaseMutex(&bufp->mx); buf_Release(bufp); bufp = NULL; goto done; } } /* if (wrong buffer) ... */ /* now we have the right buffer loaded. Copy out the * data from here to the user's buffer. */ bufIndex = offsetp->LowPart & (cm_data.blockSize - 1); /* and figure out how many bytes we want from this buffer */ nbytes = cm_data.blockSize - bufIndex; /* what remains in buffer */ if (nbytes > length) nbytes = length; /* don't go past end of request */ /* now copy the data */ memcpy(bufp->datap + bufIndex, bufferp, nbytes); buf_SetDirty(bufp, reqp, bufIndex, nbytes, userp); /* adjust lengthers, pointers, etc. */ bufferp += nbytes; length -= nbytes; written += nbytes; thyper.LowPart = nbytes; thyper.HighPart = 0; *offsetp = LargeIntegerAdd(thyper, *offsetp); } /* while 1 */ done: if (writtenp) *writtenp = written; if (bufp) { lock_ReleaseMutex(&bufp->mx); buf_Release(bufp); } if (code == 0 && doWriteBack) { rock_BkgStore_t *rockp = malloc(sizeof(*rockp)); if (rockp) { code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_ASYNCSTORE); if (code == 0) { rockp->length = writeBackLength; rockp->offset = writeBackOffset; lock_ReleaseWrite(&scp->rw); cm_QueueBKGRequest(scp, cm_BkgStore, rockp, userp, reqp); lock_ObtainWrite(&scp->rw); /* rock is freed by cm_BkgDaemon */ } else { free(rockp); } } } /* cm_SyncOpDone is called when cm_BkgStore completes */ return code; }
long WriteData(cm_scache_t *scp, osi_hyper_t offset, long count, char *op, cm_user_t *userp, long *writtenp) { long code = 0; long written = 0; osi_hyper_t fileLength; /* file's length at start of write */ osi_hyper_t minLength; /* don't read past this */ afs_uint32 nbytes; /* # of bytes to transfer this iteration */ cm_buf_t *bufferp; osi_hyper_t thyper; /* hyper tmp variable */ osi_hyper_t bufferOffset; afs_uint32 bufIndex; /* index in buffer where our data is */ int doWriteBack; osi_hyper_t writeBackOffset;/* offset of region to write back when * I/O is done */ DWORD filter = 0; cm_req_t req; cm_InitReq(&req); bufferp = NULL; doWriteBack = 0; lock_ObtainWrite(&scp->rw); /* start by looking up the file's end */ code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_GETSTATUS); if (code) goto done; cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_GETSTATUS); #if 0 /* make sure we have a writable FD */ if (!(fidp->flags & SMB_FID_OPENWRITE)) { code = CM_ERROR_BADFDOP; goto done; } #endif /* now we have the entry locked, look up the length */ fileLength = scp->length; minLength = fileLength; if (LargeIntegerGreaterThan(minLength, scp->serverLength)) minLength = scp->serverLength; /* adjust file length if we extend past EOF */ thyper.LowPart = count; thyper.HighPart = 0; thyper = LargeIntegerAdd(offset, thyper); /* where write should end */ if (LargeIntegerGreaterThan(thyper, fileLength)) { /* we'd write past EOF, so extend the file */ scp->mask |= CM_SCACHEMASK_LENGTH; scp->length = thyper; filter |= (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE); } else filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; /* now, if the new position (thyper) and the old (offset) are in * different storeback windows, remember to store back the previous * storeback window when we're done with the write. */ if ((thyper.LowPart & (-cm_chunkSize)) != (offset.LowPart & (-cm_chunkSize))) { /* they're different */ doWriteBack = 1; writeBackOffset.HighPart = offset.HighPart; writeBackOffset.LowPart = offset.LowPart & (-cm_chunkSize); } *writtenp = count; /* now, copy the data one buffer at a time, until we've filled the * request packet */ while (1) { /* if we've copied all the data requested, we're done */ if (count <= 0) break; /* handle over quota or out of space */ if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) { *writtenp = written; break; } /* otherwise, load up a buffer of data */ thyper.HighPart = offset.HighPart; thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1); if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) { /* wrong buffer */ if (bufferp) { lock_ReleaseMutex(&bufferp->mx); buf_Release(bufferp); bufferp = NULL; } lock_ReleaseWrite(&scp->rw); code = buf_Get(scp, &thyper, &bufferp); lock_ObtainMutex(&bufferp->mx); lock_ObtainWrite(&scp->rw); if (code) goto done; bufferOffset = thyper; /* now get the data in the cache */ while (1) { code = cm_SyncOp(scp, bufferp, userp, &req, 0, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_WRITE | CM_SCACHESYNC_BUFLOCKED); if (code) goto done; cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_WRITE | CM_SCACHESYNC_BUFLOCKED); /* If we're overwriting the entire buffer, or * if we're writing at or past EOF, mark the * buffer as current so we don't call * cm_GetBuffer. This skips the fetch from the * server in those cases where we're going to * obliterate all the data in the buffer anyway, * or in those cases where there is no useful * data at the server to start with. * * Use minLength instead of scp->length, since * the latter has already been updated by this * call. */ if (LargeIntegerGreaterThanOrEqualTo(bufferp->offset, minLength) || LargeIntegerEqualTo(offset, bufferp->offset) && (count >= buf_bufferSize || LargeIntegerGreaterThanOrEqualTo(LargeIntegerAdd(offset, ConvertLongToLargeInteger(count)), minLength))) { if (count < buf_bufferSize && bufferp->dataVersion == -1) memset(bufferp->datap, 0, buf_bufferSize); bufferp->dataVersion = scp->dataVersion; } if (cm_HaveBuffer(scp, bufferp, 1)) break; /* otherwise, load the buffer and try again */ lock_ReleaseMutex(&bufferp->mx); code = cm_GetBuffer(scp, bufferp, NULL, userp, &req); lock_ReleaseWrite(&scp->rw); lock_ObtainMutex(&bufferp->mx); lock_ObtainWrite(&scp->rw); if (code) break; } if (code) { lock_ReleaseMutex(&bufferp->mx); buf_Release(bufferp); bufferp = NULL; goto done; } } /* if (wrong buffer) ... */ /* now we have the right buffer loaded. Copy out the * data from here to the user's buffer. */ bufIndex = offset.LowPart & (buf_bufferSize - 1); /* and figure out how many bytes we want from this buffer */ nbytes = buf_bufferSize - bufIndex; /* what remains in buffer */ if (nbytes > count) nbytes = count; /* don't go past end of request */ /* now copy the data */ memcpy(bufferp->datap + bufIndex, op, nbytes); buf_SetDirty(bufferp, bufIndex, nbytes, userp); /* adjust counters, pointers, etc. */ op += nbytes; count -= nbytes; written += nbytes; thyper.LowPart = nbytes; thyper.HighPart = 0; offset = LargeIntegerAdd(thyper, offset); } /* while 1 */ done: lock_ReleaseWrite(&scp->rw); if (bufferp) { lock_ReleaseMutex(&bufferp->mx); buf_Release(bufferp); } if (code == 0 && doWriteBack) { lock_ObtainWrite(&scp->rw); code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE); lock_ReleaseWrite(&scp->rw); if (code == 0) cm_QueueBKGRequest(scp, cm_BkgStore, writeBackOffset.LowPart, writeBackOffset.HighPart, cm_chunkSize, 0, userp); } /* cm_SyncOpDone is called when cm_BkgStore completes */ return code; }