Windows: Direct IO Support for Service
[openafs.git] / src / WINNT / afsrdr / user / RDRFunction.c
index 1b56c59..8ee0ed8 100644 (file)
@@ -114,11 +114,39 @@ RDR_SetInitParams( OUT AFSRedirectorInitInfo **ppRedirInitInfo, OUT DWORD * pRed
     extern char cm_CachePath[];
     extern cm_config_data_t cm_data;
     extern int smb_hideDotFiles;
-    size_t cm_CachePathLen = strlen(cm_CachePath);
+    size_t CachePathLen;
+    DWORD TempPathLen;
     size_t err;
-    DWORD TempPathLen = ExpandEnvironmentStringsW(L"%TEMP%", NULL, 0);
     MEMORYSTATUSEX memStatus;
     DWORD maxMemoryCacheSize;
+    char FullCachePath[MAX_PATH];
+    char TempPath[MAX_PATH];
+    char FullTempPath[MAX_PATH];
+
+    /*
+     * The %TEMP% environment variable may be relative instead
+     * of absolute which can result in the redirector referring
+     * to a different directory than the service.  The full path
+     * must therefore be obtained first.
+     */
+
+    CachePathLen = GetFullPathNameA(cm_CachePath, MAX_PATH, FullCachePath, NULL);
+    if (CachePathLen == 0) {
+        osi_Log0(afsd_logp, "RDR_SetInitParams Unable to obtain Full Cache Path");
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
+    TempPathLen = ExpandEnvironmentStringsA("%TEMP%", TempPath, MAX_PATH);
+    if (TempPathLen == 0) {
+        osi_Log0(afsd_logp, "RDR_SetInitParams Unable to expand %%TEMP%%");
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
+    TempPathLen = GetFullPathNameA(TempPath, MAX_PATH, FullTempPath, NULL);
+    if (TempPathLen == 0) {
+        osi_Log0(afsd_logp, "RDR_SetInitParams Unable to obtain Full Temp Path");
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
 
     memStatus.dwLength = sizeof(memStatus);
     if (GlobalMemoryStatusEx(&memStatus)) {
@@ -139,10 +167,11 @@ RDR_SetInitParams( OUT AFSRedirectorInitInfo **ppRedirInitInfo, OUT DWORD * pRed
         maxMemoryCacheSize = 65536;
     }
 
-    *pRedirInitInfoLen = (DWORD) (sizeof(AFSRedirectorInitInfo) + (cm_CachePathLen + TempPathLen) * sizeof(WCHAR));
+    *pRedirInitInfoLen = (DWORD) (sizeof(AFSRedirectorInitInfo) + (CachePathLen + TempPathLen) * sizeof(WCHAR));
     *ppRedirInitInfo = (AFSRedirectorInitInfo *)malloc(*pRedirInitInfoLen);
     (*ppRedirInitInfo)->Flags = smb_hideDotFiles ? AFS_REDIR_INIT_FLAG_HIDE_DOT_FILES : 0;
     (*ppRedirInitInfo)->Flags |= cm_shortNames ? 0 : AFS_REDIR_INIT_FLAG_DISABLE_SHORTNAMES;
+    (*ppRedirInitInfo)->Flags |= cm_directIO ? AFS_REDIR_INIT_PERFORM_SERVICE_IO : 0;
     (*ppRedirInitInfo)->MaximumChunkLength = cm_data.chunkSize;
     (*ppRedirInitInfo)->GlobalFileId.Cell   = cm_data.rootFid.cell;
     (*ppRedirInitInfo)->GlobalFileId.Volume = cm_data.rootFid.volume;
@@ -162,8 +191,8 @@ RDR_SetInitParams( OUT AFSRedirectorInitInfo **ppRedirInitInfo, OUT DWORD * pRed
     } else {
         (*ppRedirInitInfo)->MemoryCacheOffset.QuadPart = 0;
         (*ppRedirInitInfo)->MemoryCacheLength.QuadPart = 0;
-        (*ppRedirInitInfo)->CacheFileNameLength = (ULONG) (cm_CachePathLen * sizeof(WCHAR));
-        err = mbstowcs((*ppRedirInitInfo)->CacheFileName, cm_CachePath, (cm_CachePathLen + 1) *sizeof(WCHAR));
+        (*ppRedirInitInfo)->CacheFileNameLength = (ULONG) (CachePathLen * sizeof(WCHAR));
+        err = mbstowcs((*ppRedirInitInfo)->CacheFileName, FullCachePath, (CachePathLen + 1) *sizeof(WCHAR));
         if (err == -1) {
             free(*ppRedirInitInfo);
             osi_Log0(afsd_logp, "RDR_SetInitParams Invalid Object Name");
@@ -173,9 +202,14 @@ RDR_SetInitParams( OUT AFSRedirectorInitInfo **ppRedirInitInfo, OUT DWORD * pRed
     }
     (*ppRedirInitInfo)->DumpFileLocationOffset = FIELD_OFFSET(AFSRedirectorInitInfo, CacheFileName) + (*ppRedirInitInfo)->CacheFileNameLength;
     (*ppRedirInitInfo)->DumpFileLocationLength = (TempPathLen - 1) * sizeof(WCHAR);
-    ExpandEnvironmentStringsW(L"%TEMP%",
-                              (LPWSTR)(((PBYTE)(*ppRedirInitInfo)) + (*ppRedirInitInfo)->DumpFileLocationOffset),
-                              TempPathLen);
+
+    err = mbstowcs((((PBYTE)(*ppRedirInitInfo)) + (*ppRedirInitInfo)->DumpFileLocationOffset),
+                   FullTempPath, (TempPathLen + 1) *sizeof(WCHAR));
+    if (err == -1) {
+        free(*ppRedirInitInfo);
+        osi_Log0(afsd_logp, "RDR_SetInitParams Invalid Object Name");
+        return STATUS_OBJECT_NAME_INVALID;
+    }
 
     osi_Log0(afsd_logp,"RDR_SetInitParams Success");
     return 0;
@@ -955,6 +989,7 @@ RDR_EvaluateNodeByName( IN cm_user_t *userp,
                         IN WCHAR   *FileNameCounted,
                         IN DWORD    FileNameLength,
                         IN BOOL     CaseSensitive,
+                        IN BOOL     LastComponent,
                         IN BOOL     bWow64,
                         IN BOOL     bHoldFid,
                         IN BOOL     bNoFollow,
@@ -975,6 +1010,7 @@ RDR_EvaluateNodeByName( IN cm_user_t *userp,
     size_t        cbName;
     BOOL          bVol = FALSE;
     wchar_t       FileName[260];
+    afs_uint32    lookupFlags;
 
     StringCchCopyNW(FileName, 260, FileNameCounted, FileNameLength / sizeof(WCHAR));
 
@@ -984,7 +1020,7 @@ RDR_EvaluateNodeByName( IN cm_user_t *userp,
              ParentID.Cell, ParentID.Volume, ParentID.Vnode, ParentID.Unique);
 
     /* Allocate enough room to add a volume prefix if necessary */
-    cbName = FileNameLength + (CM_PREFIX_VOL_CCH + 1) * sizeof(WCHAR);
+    cbName = FileNameLength + (CM_PREFIX_VOL_CCH + 64) * sizeof(WCHAR);
     wszName = malloc(cbName);
     if (!wszName) {
         osi_Log0(afsd_logp, "RDR_EvaluateNodeByName Out of Memory");
@@ -1059,18 +1095,52 @@ RDR_EvaluateNodeByName( IN cm_user_t *userp,
         return;
     }
 
-    code = cm_Lookup(dscp, wszName, CM_FLAG_CHECKPATH, userp, &req, &scp);
+    lookupFlags = CM_FLAG_NOMOUNTCHASE;
+
+    if ( !LastComponent )
+        lookupFlags |= CM_FLAG_CHECKPATH;
+    code = cm_Lookup(dscp, wszName, lookupFlags, userp, &req, &scp);
+
+    if (!CaseSensitive &&
+        (code == CM_ERROR_NOSUCHPATH || code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)) {
+        lookupFlags |= CM_FLAG_CASEFOLD;
+        code = cm_Lookup(dscp, wszName, lookupFlags, userp, &req, &scp);
+    }
 
     if ((code == CM_ERROR_NOSUCHPATH || code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
-         (wcschr(wszName, '%') != NULL || wcschr(wszName, '#') != NULL)) {
-        /*
-         * A volume reference:  <cell>{%,#}<volume> -> @vol:<cell>{%,#}<volume>
-         */
-        StringCchCopyNW(wszName, cbName, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
-        StringCbCatNW(wszName, cbName, FileName, FileNameLength);
-        bVol = TRUE;
+         dscp == cm_data.rootSCachep) {
+
+        if (wcschr(wszName, '%') != NULL || wcschr(wszName, '#') != NULL) {
+            /*
+             * A volume reference:  <cell>{%,#}<volume> -> @vol:<cell>{%,#}<volume>
+             */
+            StringCchCopyNW(wszName, cbName, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
+            StringCbCatNW(wszName, cbName, FileName, FileNameLength);
+            bVol = TRUE;
 
-        code = cm_EvaluateVolumeReference(wszName, CM_FLAG_CHECKPATH, userp, &req, &scp);
+            code = cm_EvaluateVolumeReference(wszName, CM_FLAG_CHECKPATH, userp, &req, &scp);
+        }
+#ifdef AFS_FREELANCE_CLIENT
+        else if (dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID && dscp->fid.volume == AFS_FAKE_ROOT_VOL_ID &&
+                 dscp->fid.vnode == 1 && dscp->fid.unique == 1) {
+            /*
+             * If this is the Freelance volume root directory then treat unrecognized
+             * names as cell names and attempt to find the appropriate "root.cell".
+             */
+            StringCchCopyNW(wszName, cbName, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
+            if (FileName[0] == L'.') {
+                StringCbCatNW(wszName, cbName, &FileName[1], FileNameLength);
+                StringCbCatNW(wszName, cbName, L"%", sizeof(WCHAR));
+            } else {
+                StringCbCatNW(wszName, cbName, FileName, FileNameLength);
+                StringCbCatNW(wszName, cbName, L"#", sizeof(WCHAR));
+            }
+            StringCbCatNW(wszName, cbName, L"root.cell", 9 * sizeof(WCHAR));
+            bVol = TRUE;
+
+            code = cm_EvaluateVolumeReference(wszName, CM_FLAG_CHECKPATH, userp, &req, &scp);
+        }
+#endif
     }
 
     if (code == 0 && scp) {
@@ -1772,7 +1842,7 @@ RDR_CleanupFileEntry( IN cm_user_t *userp,
     }
     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
 
-    if ((bLastHandle || bFlushFile) &&
+    if (bLastHandle && (scp->fileType == CM_SCACHETYPE_FILE) &&
         scp->redirBufCount > 0)
     {
         LARGE_INTEGER heldExtents;
@@ -1838,21 +1908,30 @@ RDR_CleanupFileEntry( IN cm_user_t *userp,
 
     /* If not a readonly object, flush dirty data and update metadata */
     if (!(scp->flags & CM_SCACHEFLAG_RO)) {
-        if ((bLastHandle || bFlushFile) &&
-             buf_DirtyBuffersExist(&scp->fid)) {
-            if (!bScpLocked) {
-                lock_ObtainWrite(&scp->rw);
-                bScpLocked = TRUE;
-            }
-            code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_WRITE,
-                             CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        if ((scp->fileType == CM_SCACHETYPE_FILE) && (bLastHandle || bFlushFile)) {
+            /* Serialize with any outstanding AsyncStore operation */
+            code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE);
             if (code == 0) {
-                if (bScpLocked) {
-                    lock_ReleaseWrite(&scp->rw);
-                    bScpLocked = FALSE;
+                cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_ASYNCSTORE);
+
+                code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_WRITE,
+                                 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+                /*
+                 * If we only have 'i' bits, then we should still be able to
+                 * set flush the file.
+                 */
+                if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
+                    code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_INSERT,
+                                     CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
                 }
+                if (code == 0) {
+                    if (bScpLocked) {
+                        lock_ReleaseWrite(&scp->rw);
+                        bScpLocked = FALSE;
+                    }
 
-                code = cm_FSync(scp, userp, &req, bScpLocked);
+                    code = cm_FSync(scp, userp, &req, bScpLocked);
+                }
             }
             if (bLastHandle && code)
                 goto unlock;
@@ -3066,6 +3145,8 @@ RDR_OpenFileEntry( IN cm_user_t *userp,
                                   userp, &req, &ldp);
             if (code == 0)
                 code = RDR_CheckAccess(scp, userp, &req, OpenCB->DesiredAccess, &pResultCB->GrantedAccess);
+
+
             cm_CheckNTOpenDone(scp, userp, &req, &ldp);
         } while (count < 100 && (code == CM_ERROR_RETRY || code == CM_ERROR_WOULDBLOCK));
     }
@@ -3268,8 +3349,7 @@ HexCheckSum(unsigned char * buf, int buflen, unsigned char * md5cksum)
 
 /* do the background fetch. */
 afs_int32
-RDR_BkgFetch(cm_scache_t *scp, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4,
-             cm_user_t *userp, cm_req_t *reqp)
+RDR_BkgFetch(cm_scache_t *scp, void *rockp, cm_user_t *userp, cm_req_t *reqp)
 {
     osi_hyper_t length;
     osi_hyper_t base;
@@ -3297,15 +3377,12 @@ RDR_BkgFetch(cm_scache_t *scp, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_
     fetched.LowPart = 0;
     fetched.HighPart = 0;
     tblocksize = ConvertLongToLargeInteger(cm_data.buf_blockSize);
-    base.LowPart = p1;
-    base.HighPart = p2;
-    length.LowPart = p3;
-    length.HighPart = p4;
-
+    base = ((rock_BkgFetch_t *)rockp)->base;
+    length = ((rock_BkgFetch_t *)rockp)->length;
     end = LargeIntegerAdd(base, length);
 
     osi_Log5(afsd_logp, "Starting BKG Fetch scp 0x%p offset 0x%x:%x length 0x%x:%x",
-             scp, p2, p1, p4, p3);
+             scp, base.HighPart, base.LowPart, length.HighPart, length.LowPart);
 
     /*
      * Make sure we have a callback.
@@ -3344,7 +3421,7 @@ RDR_BkgFetch(cm_scache_t *scp, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_
             rwheld = 0;
         }
 
-        code = buf_Get(scp, &offset, reqp, &bufp);
+        code = buf_Get(scp, &offset, reqp, 0, &bufp);
         if (code) {
             /*
              * any error from buf_Get() is non-fatal.
@@ -3581,7 +3658,7 @@ RDR_RequestFileExtentsAsync( IN cm_user_t *userp,
         QueueLength = 0;
         thyper.QuadPart = ByteOffset.QuadPart;
 
-        code = buf_Get(scp, &thyper, &req, &bufp);
+        code = buf_Get(scp, &thyper, &req, 0,  &bufp);
         if (code == 0) {
             lock_ObtainMutex(&bufp->mx);
             bBufRelease = TRUE;
@@ -3717,12 +3794,21 @@ RDR_RequestFileExtentsAsync( IN cm_user_t *userp,
                 buf_Release(bufp);
 
             if (QueueLength) {
-                req.flags &= ~CM_REQ_NORETRY;
-                cm_QueueBKGRequest(scp, RDR_BkgFetch, QueueOffset.LowPart, QueueOffset.HighPart,
-                                   QueueLength, 0, userp, &req);
-                osi_Log3(afsd_logp, "RDR_RequestFileExtentsAsync Queued a Background Fetch offset 0x%x:%x length 0x%x",
-                         QueueOffset.HighPart, QueueOffset.LowPart, QueueLength);
-                req.flags |= CM_REQ_NORETRY;
+                rock_BkgFetch_t * rockp = malloc(sizeof(*rockp));
+
+                if (rockp) {
+                    req.flags &= ~CM_REQ_NORETRY;
+                    rockp->base = QueueOffset;
+                    rockp->length.LowPart = QueueLength;
+                    rockp->length.HighPart = 0;
+
+                    cm_QueueBKGRequest(scp, RDR_BkgFetch, rockp, userp, &req);
+                    osi_Log3(afsd_logp, "RDR_RequestFileExtentsAsync Queued a Background Fetch offset 0x%x:%x length 0x%x",
+                              QueueOffset.HighPart, QueueOffset.LowPart, QueueLength);
+                    req.flags |= CM_REQ_NORETRY;
+                } else {
+                    code = ENOMEM;
+                }
             }
         } else {
             /* No error from buf_Get() can be fatal */
@@ -3733,12 +3819,20 @@ RDR_RequestFileExtentsAsync( IN cm_user_t *userp,
 
     if (BeginOffset.QuadPart != EndOffset.QuadPart) {
         afs_uint32 length = (afs_uint32)(EndOffset.QuadPart - BeginOffset.QuadPart);
+        rock_BkgFetch_t * rockp = malloc(sizeof(*rockp));
+
+        if (rockp) {
+            req.flags &= ~CM_REQ_NORETRY;
+            rockp->base = BeginOffset;
+            rockp->length.LowPart = length;
+            rockp->length.HighPart = 0;
 
-        req.flags &= ~CM_REQ_NORETRY;
-        cm_QueueBKGRequest(scp, RDR_BkgFetch, BeginOffset.LowPart, BeginOffset.HighPart,
-                           length, 0, userp, &req);
-        osi_Log3(afsd_logp, "RDR_RequestFileExtentsAsync Queued a Background Fetch offset 0x%x:%x length 0x%x",
-                  BeginOffset.HighPart, BeginOffset.LowPart, length);
+            cm_QueueBKGRequest(scp, RDR_BkgFetch, rockp, userp, &req);
+            osi_Log3(afsd_logp, "RDR_RequestFileExtentsAsync Queued a Background Fetch offset 0x%x:%x length 0x%x",
+                     BeginOffset.HighPart, BeginOffset.LowPart, length);
+        } else {
+            code = ENOMEM;
+        }
     }
     cm_ReleaseSCache(scp);
 
@@ -3773,6 +3867,7 @@ RDR_ReleaseFileExtents( IN cm_user_t *userp,
     int         released = 0;
     int         deleted = 0;
     DWORD       status;
+    rock_BkgStore_t *rockp;
 #ifdef ODS_DEBUG
 #ifdef VALIDATE_CHECK_SUM
     char md5dbg[33], md5dbg2[33], md5dbg3[33];
@@ -4202,6 +4297,10 @@ RDR_ReleaseFileExtents( IN cm_user_t *userp,
             lock_ObtainWrite(&scp->rw);
             code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_WRITE,
                              CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+            if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
+                code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_INSERT,
+                                 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+            }
             lock_ReleaseWrite(&scp->rw);
             if (code == 0)
                 code = cm_FSync(scp, userp, &req, FALSE);
@@ -4214,6 +4313,10 @@ RDR_ReleaseFileExtents( IN cm_user_t *userp,
             lock_ObtainWrite(&scp->rw);
             code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_WRITE,
                              CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+            if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
+                code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_INSERT,
+                                  CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+            }
             lock_ReleaseWrite(&scp->rw);
             if (code == 0) {
                 /*
@@ -4226,15 +4329,32 @@ RDR_ReleaseFileExtents( IN cm_user_t *userp,
                     {
                         length += cm_data.buf_blockSize;
                     } else {
-                        if (!(offset.QuadPart == 0 && length == 0))
-                            cm_QueueBKGRequest(scp, cm_BkgStore, offset.LowPart, offset.HighPart,
-                                                length, 0, userp, &req);
+                        if (!(offset.QuadPart == 0 && length == 0)) {
+                            rockp = malloc(sizeof(*rockp));
+                            if (rockp) {
+                                rockp->length = length;
+                                rockp->offset = offset;
+
+                                cm_QueueBKGRequest(scp, cm_BkgStore, rockp, userp, &req);
+
+                                /* rock is freed by cm_BkgStore */
+                            }
+                        }
                         offset.QuadPart = ReleaseExtentsCB->FileExtents[count].FileOffset.QuadPart;
                         length = cm_data.buf_blockSize;
                     }
                 }
-                cm_QueueBKGRequest(scp, cm_BkgStore, offset.LowPart, offset.HighPart,
-                                   length, 0, userp, &req);
+
+                /* Store whatever is left */
+                rockp = malloc(sizeof(*rockp));
+                if (rockp) {
+                    rockp->length = length;
+                    rockp->offset = offset;
+
+                    cm_QueueBKGRequest(scp, cm_BkgStore, rockp, userp, &req);
+
+                    /* rock is freed by cm_BkgStore */
+                }
             }
         }
         cm_ReleaseSCache(scp);
@@ -4267,6 +4387,7 @@ RDR_ProcessReleaseFileExtentsResult( IN AFSReleaseFileExtentsResultCB *ReleaseFi
     cm_buf_t    *bufp;
     unsigned int fileno, extentno, total_extents = 0;
     AFSReleaseFileExtentsResultFileCB *pNextFileCB;
+    rock_BkgStore_t *rockp;
 #ifdef ODS_DEBUG
 #ifdef VALIDATE_CHECK_SUM
     char md5dbg[33], md5dbg2[33], md5dbg3[33];
@@ -4673,15 +4794,32 @@ RDR_ProcessReleaseFileExtentsResult( IN AFSReleaseFileExtentsResultCB *ReleaseFi
                      length < cm_chunkSize) {
                     length += cm_data.buf_blockSize;
                 } else {
-                    if (!(offset.QuadPart == 0 && length == 0))
-                        cm_QueueBKGRequest(scp, cm_BkgStore, offset.LowPart, offset.HighPart,
-                                            length, 0, userp, &req);
+                    if (!(offset.QuadPart == 0 && length == 0)) {
+                        rockp = malloc(sizeof(*rockp));
+                        if (rockp) {
+                            rockp->offset = offset;
+                            rockp->length = length;
+
+                            cm_QueueBKGRequest(scp, cm_BkgStore, rockp, userp, &req);
+                        } else {
+                            code = ENOMEM;
+                        }
+                    }
                     offset.QuadPart = pExtent->FileOffset.QuadPart;
                     length = cm_data.buf_blockSize;
                 }
             }
-            cm_QueueBKGRequest(scp, cm_BkgStore, offset.LowPart, offset.HighPart,
-                                length, 0, userp, &req);
+
+            /* Background store the rest */
+            rockp = malloc(sizeof(*rockp));
+            if (rockp) {
+                rockp->offset = offset;
+                rockp->length = length;
+
+                cm_QueueBKGRequest(scp, cm_BkgStore, rockp, userp, &req);
+            } else {
+                code = ENOMEM;
+            }
         }
 
         osi_Log5(afsd_logp, "RDR_ProcessReleaseFileExtentsResult File FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x Released %d",
@@ -5270,7 +5408,6 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
     cm_scache_t *scp = NULL;
     cm_volume_t *volp = NULL;
     afs_uint32   volType;
-    cm_cell_t   *cellp = NULL;
     cm_fid_t    Fid;
     afs_uint32  code;
     cm_req_t    req;
@@ -5347,12 +5484,17 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
         memcpy(&pResultCB->VolumeCreationTime, &ft, sizeof(ft));
 
         pResultCB->AvailableAllocationUnits.QuadPart = 0;
-        pResultCB->Characteristics |= FILE_READ_ONLY_DEVICE;
+        pResultCB->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
 
         pResultCB->VolumeLabelLength = cm_Utf8ToUtf16( "Freelance.Local.Root", -1, pResultCB->VolumeLabel,
                                                        (sizeof(pResultCB->VolumeLabel) / sizeof(WCHAR)) + 1);
         if ( pResultCB->VolumeLabelLength )
             pResultCB->VolumeLabelLength--;
+
+        pResultCB->CellLength = cm_Utf8ToUtf16( "Freelance.Local", -1, pResultCB->Cell,
+                                                (sizeof(pResultCB->Cell) / sizeof(WCHAR)) + 1);
+        if ( pResultCB->CellLength )
+            pResultCB->CellLength--;
     } else {
         memcpy(&pResultCB->VolumeCreationTime, &ft, sizeof(ft));
 
@@ -5363,7 +5505,8 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
         }
         volType = cm_VolumeType(volp, scp->fid.volume);
 
-        pResultCB->Characteristics |= ((volType == ROVOL || volType == BACKVOL) ? FILE_READ_ONLY_DEVICE : 0);
+        if (volType == ROVOL || volType == BACKVOL)
+            pResultCB->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
 
         code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_READ,
                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
@@ -5391,26 +5534,19 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
         }
 
         if (code == 0) {
-            if (volStat.MaxQuota)
-            {
-                pResultCB->TotalAllocationUnits.QuadPart = volStat.MaxQuota;
-                if (volType == ROVOL || volType == BACKVOL) {
-                    pResultCB->AvailableAllocationUnits.QuadPart = 0;
-                }
-                else
+            if (volType == ROVOL || volType == BACKVOL) {
+                pResultCB->TotalAllocationUnits.QuadPart = volStat.BlocksInUse;
+                pResultCB->AvailableAllocationUnits.QuadPart = 0;
+            } else {
+                if (volStat.MaxQuota)
                 {
+                    pResultCB->TotalAllocationUnits.QuadPart = volStat.MaxQuota;
                     pResultCB->AvailableAllocationUnits.QuadPart =
                         min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
                 }
-            }
-            else
-            {
-                pResultCB->TotalAllocationUnits.QuadPart = volStat.PartMaxBlocks;
-                if (volType == ROVOL || volType == BACKVOL) {
-                    pResultCB->AvailableAllocationUnits.QuadPart = 0;
-                }
                 else
                 {
+                    pResultCB->TotalAllocationUnits.QuadPart = volStat.PartMaxBlocks;
                     pResultCB->AvailableAllocationUnits.QuadPart = volStat.PartBlocksAvail;
                 }
             }
@@ -5426,9 +5562,38 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
 
         pResultCB->VolumeLabelLength = cm_Utf8ToUtf16( volp->namep, -1, pResultCB->VolumeLabel,
                                                        (sizeof(pResultCB->VolumeLabel) / sizeof(WCHAR)) + 1);
+
+        if ( pResultCB->VolumeLabelLength) {
+
+            /* add .readonly and .backup if appropriate */
+            switch ( volType) {
+            case ROVOL:
+                pResultCB->VolumeLabelLength--;
+                pResultCB->VolumeLabelLength += cm_Utf8ToUtf16( ".readonly", -1,
+                                                                &pResultCB->VolumeLabel[ pResultCB->VolumeLabelLength],
+                                                                (sizeof(pResultCB->VolumeLabel) / sizeof(WCHAR)) - pResultCB->VolumeLabelLength + 1);
+                break;
+
+            case BACKVOL:
+                pResultCB->VolumeLabelLength--;
+                pResultCB->VolumeLabelLength += cm_Utf8ToUtf16( ".backup", -1,
+                                                                &pResultCB->VolumeLabel[ pResultCB->VolumeLabelLength],
+                                                                (sizeof(pResultCB->VolumeLabel) / sizeof(WCHAR)) - pResultCB->VolumeLabelLength + 1);
+                break;
+            }
+        }
+
+        /* do not include the trailing nul */
         if ( pResultCB->VolumeLabelLength )
             pResultCB->VolumeLabelLength--;
 
+        pResultCB->CellLength = cm_Utf8ToUtf16( volp->cellp->name, -1, pResultCB->Cell,
+                                                (sizeof(pResultCB->Cell) / sizeof(WCHAR)) + 1);
+
+        /* do not include the trailing nul */
+        if ( pResultCB->CellLength )
+            pResultCB->CellLength--;
+
         if (sync_done) {
             if (!scp_locked) {
                 lock_ObtainWrite(&scp->rw);
@@ -5438,6 +5603,7 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
         }
     }
     pResultCB->VolumeLabelLength *= sizeof(WCHAR);  /* convert to bytes from chars */
+    pResultCB->CellLength *= sizeof(WCHAR);         /* convert to bytes from chars */
 
   _done:
     if (scp_locked)
@@ -5464,7 +5630,6 @@ RDR_GetVolumeSizeInfo( IN cm_user_t     *userp,
     cm_scache_t *scp = NULL;
     cm_volume_t *volp = NULL;
     afs_uint32   volType;
-    cm_cell_t   *cellp = NULL;
     cm_fid_t    Fid;
     afs_uint32  code;
     cm_req_t    req;
@@ -5567,26 +5732,19 @@ RDR_GetVolumeSizeInfo( IN cm_user_t     *userp,
         }
 
         if (code == 0) {
-            if (volStat.MaxQuota)
-            {
-                pResultCB->TotalAllocationUnits.QuadPart = volStat.MaxQuota;
-                if (volType == ROVOL || volType == BACKVOL) {
-                    pResultCB->AvailableAllocationUnits.QuadPart = 0;
-                }
-                else
+            if (volType == ROVOL || volType == BACKVOL) {
+                pResultCB->TotalAllocationUnits.QuadPart = volStat.BlocksInUse;
+                pResultCB->AvailableAllocationUnits.QuadPart = 0;
+            } else {
+                if (volStat.MaxQuota)
                 {
+                    pResultCB->TotalAllocationUnits.QuadPart = volStat.MaxQuota;
                     pResultCB->AvailableAllocationUnits.QuadPart =
                         min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
                 }
-            }
-            else
-            {
-                pResultCB->TotalAllocationUnits.QuadPart = volStat.PartMaxBlocks;
-                if (volType == ROVOL || volType == BACKVOL) {
-                    pResultCB->AvailableAllocationUnits.QuadPart = 0;
-                }
                 else
                 {
+                    pResultCB->TotalAllocationUnits.QuadPart = volStat.PartMaxBlocks;
                     pResultCB->AvailableAllocationUnits.QuadPart = volStat.PartBlocksAvail;
                 }
             }
@@ -6056,3 +6214,307 @@ RDR_PipeTransceive( IN cm_user_t     *userp,
     (*ResultCB)->ResultStatus = 0;
     osi_Log0(afsd_logp, "RDR_Pipe_Transceive SUCCESS");
 }
+
+void
+RDR_ReadFile( IN cm_user_t     *userp,
+              IN AFSFileID      FileID,
+              IN LARGE_INTEGER *Offset,
+              IN ULONG          BytesToRead,
+              IN PVOID          Buffer,
+              IN BOOL           bWow64,
+              IN BOOL           bCacheBypass,
+              IN DWORD          ResultBufferLength,
+              IN OUT AFSCommResult **ResultCB)
+{
+    AFSFileIOResultCB * pFileIOResultCB;
+    DWORD         status;
+    ULONG         Length;
+    ULONG         ulBytesRead = 0;
+    afs_uint32    code = 0;
+    cm_fid_t      fid;
+    cm_scache_t * scp = NULL;
+    cm_req_t      req;
+
+    RDR_InitReq(&req, bWow64);
+
+    osi_Log4(afsd_logp, "RDR_ReadFile FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+             FileID.Cell, FileID.Volume, FileID.Vnode, FileID.Unique);
+
+    Length = sizeof(AFSFileIOResultCB);
+    if (Length > ResultBufferLength) {
+        *ResultCB = (AFSCommResult *)malloc(sizeof(AFSCommResult) );
+        if (!(*ResultCB))
+            return;
+        memset( *ResultCB, 0, sizeof(AFSCommResult));
+        (*ResultCB)->ResultStatus = STATUS_BUFFER_OVERFLOW;
+        return;
+    }
+    *ResultCB = (AFSCommResult *)malloc( Length + sizeof( AFSCommResult) );
+    if (!(*ResultCB))
+       return;
+    memset( *ResultCB, '\0', Length );
+    (*ResultCB)->ResultBufferLength = Length;
+    pFileIOResultCB = (AFSFileIOResultCB *)(*ResultCB)->ResultData;
+
+    if ( Buffer == NULL) {
+        (*ResultCB)->ResultStatus = STATUS_INVALID_PARAMETER;
+        osi_Log0(afsd_logp, "RDR_ReadFile Null IOctl Buffer");
+        return;
+    }
+
+    if (FileID.Cell != 0) {
+        fid.cell   = FileID.Cell;
+        fid.volume = FileID.Volume;
+        fid.vnode  = FileID.Vnode;
+        fid.unique = FileID.Unique;
+        fid.hash   = FileID.Hash;
+
+        code = cm_GetSCache(&fid, NULL, &scp, userp, &req);
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            osi_Log2(afsd_logp, "RDR_ReadFile cm_GetSCache failure code=0x%x status=0x%x",
+                      code, status);
+            return;
+        }
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_OBJECT_NAME_INVALID;
+        osi_Log0(afsd_logp, "RDR_ReadFile Object Name Invalid - Cell = 0");
+        return;
+    }
+
+    /* Ensure that the caller can access this file */
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_READ,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&scp->rw);
+        cm_ReleaseSCache(scp);
+        osi_Log2(afsd_logp, "RDR_ReadFile cm_SyncOp failure code=0x%x status=0x%x",
+                  code, status);
+        return;
+    }
+
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY) {
+        (*ResultCB)->ResultStatus = STATUS_FILE_IS_A_DIRECTORY;
+        lock_ReleaseWrite(&scp->rw);
+        cm_ReleaseSCache(scp);
+        osi_Log1(afsd_logp, "RDR_ReadFile File is a Directory scp=0x%p",
+                 scp);
+        return;
+    }
+
+    if (scp->fileType != CM_SCACHETYPE_FILE) {
+        (*ResultCB)->ResultStatus = STATUS_REPARSE_POINT_NOT_RESOLVED;
+        lock_ReleaseWrite(&scp->rw);
+        cm_ReleaseSCache(scp);
+        osi_Log1(afsd_logp, "RDR_ReadFile File is a MountPoint or Link scp=0x%p",
+                 scp);
+        return;
+    }
+
+    if ( bCacheBypass) {
+        //
+        // Read the file directly into the buffer bypassing the AFS Cache
+        //
+        code = cm_GetData( scp, Offset, Buffer, BytesToRead, &ulBytesRead, userp, &req);
+    } else {
+        //
+        // Read the file via the AFS Cache
+        //
+        code = raw_ReadData( scp, Offset, BytesToRead, Buffer, &ulBytesRead, userp, &req);
+    }
+
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_ReadFile failure code=0x%x status=0x%x",
+                 code, status);
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_SUCCESS;
+        pFileIOResultCB->Length = ulBytesRead;
+        pFileIOResultCB->DataVersion.QuadPart = scp->dataVersion;
+        pFileIOResultCB->Expiration.QuadPart = scp->cbExpires;
+    }
+
+    lock_ReleaseWrite(&scp->rw);
+    cm_ReleaseSCache(scp);
+    return;
+}
+
+void
+RDR_WriteFile( IN cm_user_t     *userp,
+               IN AFSFileID      FileID,
+               IN AFSFileIOCB   *FileIOCB,
+               IN LARGE_INTEGER *Offset,
+               IN ULONG          BytesToWrite,
+               IN PVOID          Buffer,
+               IN BOOL           bWow64,
+               IN BOOL           bCacheBypass,
+               IN DWORD          ResultBufferLength,
+               IN OUT AFSCommResult **ResultCB)
+{
+    AFSFileIOResultCB * pFileIOResultCB;
+    DWORD         status;
+    ULONG         Length;
+    ULONG         ulBytesWritten = 0;
+    afs_uint32    code = 0;
+    cm_fid_t      fid;
+    cm_scache_t * scp = NULL;
+    cm_req_t      req;
+
+    RDR_InitReq(&req, bWow64);
+
+    osi_Log4(afsd_logp, "RDR_WriteFile FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+             FileID.Cell, FileID.Volume, FileID.Vnode, FileID.Unique);
+
+    Length = sizeof(AFSFileIOResultCB);
+    if (Length > ResultBufferLength) {
+        *ResultCB = (AFSCommResult *)malloc(sizeof(AFSCommResult) );
+        if (!(*ResultCB))
+            return;
+        memset( *ResultCB, 0, sizeof(AFSCommResult));
+        (*ResultCB)->ResultStatus = STATUS_BUFFER_OVERFLOW;
+        return;
+    }
+    *ResultCB = (AFSCommResult *)malloc( Length + sizeof( AFSCommResult) );
+    if (!(*ResultCB))
+       return;
+    memset( *ResultCB, '\0', Length );
+    (*ResultCB)->ResultBufferLength = Length;
+    pFileIOResultCB = (AFSFileIOResultCB *)(*ResultCB)->ResultData;
+
+    if ( Buffer == NULL) {
+        (*ResultCB)->ResultStatus = STATUS_INVALID_PARAMETER;
+        osi_Log0(afsd_logp, "RDR_WriteFile Null IOctl Buffer");
+        return;
+    }
+
+    if (FileID.Cell != 0) {
+        fid.cell   = FileID.Cell;
+        fid.volume = FileID.Volume;
+        fid.vnode  = FileID.Vnode;
+        fid.unique = FileID.Unique;
+        fid.hash   = FileID.Hash;
+
+        code = cm_GetSCache(&fid, NULL, &scp, userp, &req);
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            osi_Log2(afsd_logp, "RDR_WriteFile cm_GetSCache failure code=0x%x status=0x%x",
+                      code, status);
+            return;
+        }
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_OBJECT_NAME_INVALID;
+        osi_Log0(afsd_logp, "RDR_WriteFile Object Name Invalid - Cell = 0");
+        return;
+    }
+
+    /* Ensure that the caller can access this file */
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_WRITE,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
+        code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_INSERT,
+                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    }
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&scp->rw);
+        cm_ReleaseSCache(scp);
+        osi_Log2(afsd_logp, "RDR_WriteFile cm_SyncOp failure code=0x%x status=0x%x",
+                  code, status);
+        return;
+    }
+
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY) {
+        (*ResultCB)->ResultStatus = STATUS_FILE_IS_A_DIRECTORY;
+        lock_ReleaseWrite(&scp->rw);
+        cm_ReleaseSCache(scp);
+        osi_Log1(afsd_logp, "RDR_WriteFile File is a Directory scp=0x%p",
+                 scp);
+        return;
+    }
+
+    if (scp->fileType != CM_SCACHETYPE_FILE) {
+        (*ResultCB)->ResultStatus = STATUS_REPARSE_POINT_NOT_RESOLVED;
+        lock_ReleaseWrite(&scp->rw);
+        cm_ReleaseSCache(scp);
+        osi_Log1(afsd_logp, "RDR_WriteFile File is a MountPoint or Link scp=0x%p",
+                 scp);
+        return;
+    }
+
+    if (FileIOCB->EndOfFile.QuadPart != scp->length.QuadPart)
+    {
+        cm_attr_t setAttr;
+
+        memset(&setAttr, 0, sizeof(cm_attr_t));
+        if (FileIOCB->EndOfFile.QuadPart != scp->length.QuadPart) {
+            osi_Log4(afsd_logp, "RDR_WriteFile new length fid vol 0x%x vno 0x%x length 0x%x:%x",
+                     scp->fid.volume, scp->fid.vnode,
+                     FileIOCB->EndOfFile.HighPart,
+                     FileIOCB->EndOfFile.LowPart);
+
+            setAttr.mask |= CM_ATTRMASK_LENGTH;
+            setAttr.length.LowPart = FileIOCB->EndOfFile.LowPart;
+            setAttr.length.HighPart = FileIOCB->EndOfFile.HighPart;
+            lock_ReleaseWrite(&scp->rw);
+            code = cm_SetAttr(scp, &setAttr, userp, &req);
+            osi_Log2(afsd_logp, "RDR_WriteFile cm_SetAttr failure scp=0x%p code 0x%x",
+                     scp, code);
+            code = 0;       /* ignore failure */
+            lock_ObtainWrite(&scp->rw);
+        }
+    }
+
+    /*
+     * The input buffer may contain data beyond the end of the file.
+     * Such data must be discarded.
+     */
+    if ( Offset->QuadPart + BytesToWrite > scp->length.QuadPart)
+    {
+        if ( Offset->QuadPart > scp->length.QuadPart) {
+            (*ResultCB)->ResultStatus = STATUS_SUCCESS;
+            lock_ReleaseWrite(&scp->rw);
+            cm_ReleaseSCache(scp);
+            osi_Log1(afsd_logp, "RDR_WriteFile Nothing to do scp=0x%p",
+                     scp);
+            return;
+        }
+
+        BytesToWrite -= (afs_uint32)(Offset->QuadPart + BytesToWrite - scp->length.QuadPart);
+    }
+
+    if (bCacheBypass) {
+        code = cm_DirectWrite( scp, Offset, BytesToWrite,
+                               CM_DIRECT_SCP_LOCKED,
+                               userp, &req, Buffer, &ulBytesWritten);
+    } else {
+        code = raw_WriteData( scp, Offset, BytesToWrite, Buffer, userp, &req, &ulBytesWritten);
+    }
+
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_WriteFile failure code=0x%x status=0x%x",
+                 code, status);
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_SUCCESS;
+        pFileIOResultCB->Length = ulBytesWritten;
+        pFileIOResultCB->DataVersion.QuadPart = scp->dataVersion;
+        pFileIOResultCB->Expiration.QuadPart = scp->cbExpires;
+    }
+
+    lock_ReleaseWrite(&scp->rw);
+    cm_ReleaseSCache(scp);
+    return;
+}