Example #1
0
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;
}
Example #2
0
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;
}