Windows: RDR_DeleteFileEntry test for empty directory
[openafs.git] / src / WINNT / afsrdr / user / RDRFunction.c
index a32cac3..0ecfdae 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2008 Secure Endpoints, Inc.
- * Copyright (c) 2009-2011 Your File System, Inc.
+ * Copyright (c) 2009-2013 Your File System, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -108,6 +108,38 @@ RDR_FID2fid( AFSFileID *FileId, cm_fid_t *fid)
     fid->hash = FileId->Hash;
 }
 
+unsigned long
+RDR_ExtAttributes(cm_scache_t *scp)
+{
+    unsigned long attrs;
+
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY ||
+        scp->fid.vnode & 0x1)
+    {
+        attrs = SMB_ATTR_DIRECTORY;
+#ifdef SPECIAL_FOLDERS
+        attrs |= SMB_ATTR_SYSTEM;              /* FILE_ATTRIBUTE_SYSTEM */
+#endif /* SPECIAL_FOLDERS */
+    } else if ( scp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
+                scp->fileType == CM_SCACHETYPE_DFSLINK ||
+                scp->fileType == CM_SCACHETYPE_INVALID)
+    {
+        attrs = SMB_ATTR_DIRECTORY | SMB_ATTR_REPARSE_POINT;
+    } else if ( scp->fileType == CM_SCACHETYPE_SYMLINK) {
+        attrs = SMB_ATTR_REPARSE_POINT;
+    } else {
+        attrs = 0;
+    }
+
+    if ((scp->unixModeBits & 0200) == 0)
+        attrs |= SMB_ATTR_READONLY;            /* Read-only */
+
+    if (attrs == 0)
+        attrs = SMB_ATTR_NORMAL;               /* FILE_ATTRIBUTE_NORMAL */
+
+    return attrs;
+}
+
 DWORD
 RDR_SetInitParams( OUT AFSRedirectorInitInfo **ppRedirInitInfo, OUT DWORD * pRedirInitInfoLen )
 {
@@ -171,6 +203,7 @@ RDR_SetInitParams( OUT AFSRedirectorInitInfo **ppRedirInitInfo, OUT DWORD * pRed
     *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;
@@ -507,6 +540,7 @@ RDR_PopulateCurrentEntry( IN  AFSDirEnumEntry * pCurrentEntry,
             break;
         case CM_SCACHETYPE_MOUNTPOINT:
         case CM_SCACHETYPE_INVALID:
+        case CM_SCACHETYPE_DFSLINK:
             pCurrentEntry->FileAttributes = SMB_ATTR_DIRECTORY | SMB_ATTR_REPARSE_POINT;
             break;
         case CM_SCACHETYPE_SYMLINK:
@@ -529,7 +563,7 @@ RDR_PopulateCurrentEntry( IN  AFSDirEnumEntry * pCurrentEntry,
                 pCurrentEntry->FileAttributes = SMB_ATTR_NORMAL;
         }
     } else
-        pCurrentEntry->FileAttributes = smb_ExtAttributes(scp);
+        pCurrentEntry->FileAttributes = RDR_ExtAttributes(scp);
     pCurrentEntry->EaSize = 0;
     pCurrentEntry->Links = scp->linkCount;
 
@@ -605,38 +639,110 @@ RDR_PopulateCurrentEntry( IN  AFSDirEnumEntry * pCurrentEntry,
             wtarget = (WCHAR *)((PBYTE)pCurrentEntry + pCurrentEntry->TargetNameOffset);
 
             if (dwFlags & RDR_POP_EVALUATE_SYMLINKS) {
-                char * mp;
 
                 code2 = cm_HandleLink(scp, userp, reqp);
                 if (code2 == 0) {
-                    mp = scp->mountPointStringp;
-                    len = strlen(mp);
-                    if ( len != 0 ) {
-                        /* Strip off the msdfs: prefix from the target name for the file system */
-                        if (scp->fileType == CM_SCACHETYPE_DFSLINK) {
-                            osi_Log0(afsd_logp, "RDR_PopulateCurrentEntry DFSLink Detected");
-                            pCurrentEntry->FileType = scp->fileType;
-
-                            if (!strncmp("msdfs:", mp, 6)) {
-                                mp += 6;
-                                len -= 6;
-                            }
+                    size_t wtarget_len = 0;
+
+                    if (scp->mountPointStringp[0]) {
+                        char * mp;
+                        char * s;
+                        size_t offset = 0;
+
+                        len = strlen(scp->mountPointStringp) + 1;
+                        mp = strdup(scp->mountPointStringp);
+
+                        for (s=mp; *s; s++) {
+                            if (*s == '/')
+                                *s = '\\';
                         }
-                        /* only send one slash to the redirector */
-                        if (mp[0] == '\\' && mp[1] == '\\') {
-                            mp++;
-                            len--;
+
+                        if (strncmp("msdfs:", mp, 6) == 0) {
+                            offset = 6;
                         }
+
+
+                        if ( mp[offset + 1] == ':' && mp[offset] != '\\') {
+                            /* Local drive letter.  Must return <drive>:\<path> */
+                            pCurrentEntry->FileType = CM_SCACHETYPE_DFSLINK;
+                            wtarget_len = len - offset;
+#ifdef UNICODE
+                            cch = MultiByteToWideChar( CP_UTF8, 0, &mp[offset],
+                                                       wtarget_len * sizeof(char),
+                                                       wtarget,
+                                                       wtarget_len * sizeof(WCHAR));
+#else
+                            mbstowcs(wtarget, &mp[offset], wtarget_len);
+#endif
+                        } else if (mp[offset] == '\\') {
+                            size_t nbNameLen = strlen(cm_NetbiosName);
+
+                            if ( strnicmp(&mp[offset + 1], cm_NetbiosName, nbNameLen) == 0 &&
+                                 mp[offset + nbNameLen + 1] == '\\')
+                            {
+                                /* an AFS symlink */
+                                pCurrentEntry->FileType = CM_SCACHETYPE_SYMLINK;
+                                wtarget_len = len - offset;
 #ifdef UNICODE
-                        cch = MultiByteToWideChar( CP_UTF8, 0, mp,
-                                                   len * sizeof(char),
-                                                   wtarget,
-                                                   len * sizeof(WCHAR));
+                                cch = MultiByteToWideChar( CP_UTF8, 0, &mp[offset],
+                                                           wtarget_len * sizeof(char),
+                                                           wtarget,
+                                                           wtarget_len * sizeof(WCHAR));
 #else
-                        mbstowcs(wtarget, mp, len);
+                                mbstowcs(wtarget, &mp[offset], wtarget_len);
 #endif
+                            } else if ( mp[offset + 1] == '\\' &&
+                                        strnicmp(&mp[offset + 2], cm_NetbiosName, strlen(cm_NetbiosName)) == 0 &&
+                                        mp[offset + nbNameLen + 2] == '\\')
+                            {
+                                /* an AFS symlink */
+                                pCurrentEntry->FileType = CM_SCACHETYPE_SYMLINK;
+                                wtarget_len = len - offset - 1;
+#ifdef UNICODE
+                                cch = MultiByteToWideChar( CP_UTF8, 0, &mp[offset + 1],
+                                                           wtarget_len * sizeof(char),
+                                                           wtarget,
+                                                           wtarget_len * sizeof(WCHAR));
+#else
+                                mbstowcs(wtarget, &mp[offset + 1], wtarget_len);
+#endif
+                            } else {
+                                /*
+                                 * treat as a UNC path. Needs to be \<server>\<share\<path>
+                                 */
+                                pCurrentEntry->FileType = CM_SCACHETYPE_DFSLINK;
+
+                                if ( mp[offset] == '\\' && mp[offset + 1] == '\\')
+                                     offset++;
+
+                                wtarget_len = len - offset;
+#ifdef UNICODE
+                                cch = MultiByteToWideChar( CP_UTF8, 0, &mp[offset],
+                                                           wtarget_len * sizeof(char),
+                                                           wtarget,
+                                                           wtarget_len * sizeof(WCHAR));
+#else
+                                mbstowcs(wtarget, &mp[offset], wtarget_len);
+#endif
+                            }
+                        } else {
+                            /* Relative AFS Symlink */
+                            pCurrentEntry->FileType = CM_SCACHETYPE_SYMLINK;
+                            wtarget_len = len - offset;
+#ifdef UNICODE
+                            cch = MultiByteToWideChar( CP_UTF8, 0, &mp[offset],
+                                                       wtarget_len * sizeof(char),
+                                                       wtarget,
+                                                       wtarget_len * sizeof(WCHAR));
+#else
+                            mbstowcs(wtarget, &mp[offset], wtarget_len);
+#endif
+                        }
+
+                        free(mp);
                     }
-                    pCurrentEntry->TargetNameLength = (ULONG)(sizeof(WCHAR) * len);
+
+                    pCurrentEntry->TargetNameLength = (ULONG)(sizeof(WCHAR) * (wtarget_len - 1));
                 } else {
                     osi_Log2(afsd_logp, "RDR_PopulateCurrentEntry cm_HandleLink failed scp=0x%p code=0x%x",
                              scp, code2);
@@ -717,8 +823,6 @@ RDR_PopulateCurrentEntryNoScp( IN  AFSDirEnumEntry * pCurrentEntry,
     pCurrentEntry->FileId.Unique = fidp->unique;
     pCurrentEntry->FileId.Hash = fidp->hash;
 
-    pCurrentEntry->FileType = CM_SCACHETYPE_UNKNOWN;
-
     pCurrentEntry->DataVersion.QuadPart = CM_SCACHE_VERSION_BAD;
 
     cm_LargeSearchTimeFromUnixTime(&ft, 0);
@@ -734,8 +838,14 @@ RDR_PopulateCurrentEntryNoScp( IN  AFSDirEnumEntry * pCurrentEntry,
 
     pCurrentEntry->EndOfFile.QuadPart = 0;
     pCurrentEntry->AllocationSize.QuadPart = 0;
-    pCurrentEntry->FileAttributes = 0;
+    if (fidp->vnode & 0x1) {
+        pCurrentEntry->FileType = CM_SCACHETYPE_DIRECTORY;
+        pCurrentEntry->FileAttributes = SMB_ATTR_DIRECTORY;
+    } else {
+        pCurrentEntry->FileType = CM_SCACHETYPE_UNKNOWN;
+        pCurrentEntry->FileAttributes = SMB_ATTR_NORMAL;
     pCurrentEntry->EaSize = 0;
+    }
     pCurrentEntry->Links = 0;
 
     len = wcslen(shortName);
@@ -988,6 +1098,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,
@@ -1008,6 +1119,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));
 
@@ -1092,7 +1204,17 @@ 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) &&
          dscp == cm_data.rootSCachep) {
@@ -1566,7 +1688,6 @@ RDR_UpdateFileEntry( IN cm_user_t *userp,
     }
 
     lock_ObtainWrite(&dscp->rw);
-    bScpLocked = TRUE;
     code = cm_SyncOp(dscp, NULL, userp, &req, 0,
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
     if (code) {
@@ -1581,7 +1702,6 @@ RDR_UpdateFileEntry( IN cm_user_t *userp,
 
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
     lock_ReleaseWrite(&dscp->rw);
-    bScpLocked = FALSE;
 
     if (dscp->fileType != CM_SCACHETYPE_DIRECTORY) {
         (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
@@ -2207,6 +2327,37 @@ RDR_DeleteFileEntry( IN cm_user_t *userp,
         return;
     }
 
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY) {
+        cm_dirOp_t dirop;
+
+        lock_ReleaseWrite(&scp->rw);
+
+        code = cm_BeginDirOp(scp, userp, &req, CM_DIRLOCK_READ,
+                             CM_DIROP_FLAG_NONE, &dirop);
+        if (code == 0) {
+            /* is the directory empty? if not, CM_ERROR_NOTEMPTY */
+            afs_uint32 bEmpty;
+
+            code = cm_BPlusDirIsEmpty(&dirop, &bEmpty);
+            if (code == 0 && !bEmpty)
+                code = CM_ERROR_NOTEMPTY;
+
+            cm_EndDirOp(&dirop);
+        }
+
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            (*ResultCB)->ResultBufferLength = 0;
+            cm_ReleaseSCache(scp);
+            cm_ReleaseSCache(dscp);
+            osi_Log3(afsd_logp, "RDR_DeleteFileEntry cm_SyncOp failure scp=0x%p code=0x%x status=0x%x",
+                     scp, code, status);
+            return;
+        }
+        lock_ObtainWrite(&scp->rw);
+    }
+
     if (!bCheckOnly) {
         /* Drop all locks since the file is being deleted */
         code = cm_SyncOp(scp, NULL, userp, &req, 0,
@@ -2846,6 +2997,285 @@ RDR_HardLinkFileEntry( IN cm_user_t *userp,
     return;
 }
 
+
+void
+RDR_CreateSymlinkEntry( IN cm_user_t *userp,
+                        IN AFSFileID FileId,
+                        IN WCHAR *FileNameCounted,
+                        IN DWORD FileNameLength,
+                        IN AFSCreateSymlinkCB *SymlinkCB,
+                        IN BOOL bWow64,
+                        IN DWORD ResultBufferLength,
+                        IN OUT AFSCommResult **ResultCB)
+{
+    AFSCreateSymlinkResultCB *pResultCB = NULL;
+    size_t size = sizeof(AFSCommResult) + ResultBufferLength - 1;
+    cm_fid_t            parentFid;
+    cm_fid_t            Fid;
+    afs_uint32          code;
+    cm_scache_t *       dscp = NULL;
+    afs_uint32          flags = 0;
+    cm_attr_t           setAttr;
+    cm_scache_t *       scp = NULL;
+    cm_req_t            req;
+    DWORD               status;
+    wchar_t             FileName[260];
+    char               *TargetPath = NULL;
+
+    StringCchCopyNW(FileName, 260, FileNameCounted, FileNameLength / sizeof(WCHAR));
+    TargetPath = cm_Utf16ToUtf8Alloc( SymlinkCB->TargetName,  SymlinkCB->TargetNameLength / sizeof(WCHAR), NULL);
+
+    osi_Log4( afsd_logp, "RDR_CreateSymlinkEntry parent FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              SymlinkCB->ParentId.Cell, SymlinkCB->ParentId.Volume,
+              SymlinkCB->ParentId.Vnode, SymlinkCB->ParentId.Unique);
+    osi_Log1(afsd_logp, "... name=%S", osi_LogSaveStringW(afsd_logp, FileName));
+
+    RDR_InitReq(&req, bWow64);
+    memset(&setAttr, 0, sizeof(cm_attr_t));
+
+    *ResultCB = (AFSCommResult *)malloc(size);
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_CreateSymlinkEntry out of memory");
+        free(TargetPath);
+       return;
+    }
+
+    memset( *ResultCB,
+            '\0',
+            size);
+
+    parentFid.cell   = SymlinkCB->ParentId.Cell;
+    parentFid.volume = SymlinkCB->ParentId.Volume;
+    parentFid.vnode  = SymlinkCB->ParentId.Vnode;
+    parentFid.unique = SymlinkCB->ParentId.Unique;
+    parentFid.hash   = SymlinkCB->ParentId.Hash;
+
+    code = cm_GetSCache(&parentFid, NULL, &dscp, userp, &req);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        if ( status == STATUS_INVALID_HANDLE)
+            status = STATUS_OBJECT_PATH_INVALID;
+        osi_Log2(afsd_logp, "RDR_CreateSymlinkEntry cm_GetSCache ParentFID failure code=0x%x status=0x%x",
+                  code, status);
+        free(TargetPath);
+        return;
+    }
+
+    lock_ObtainWrite(&dscp->rw);
+    code = cm_SyncOp(dscp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&dscp->rw);
+        cm_ReleaseSCache(dscp);
+        osi_Log3(afsd_logp, "RDR_CreateSymlinkEntry cm_SyncOp failure (1) dscp=0x%p code=0x%x status=0x%x",
+                 dscp, code, status);
+        free(TargetPath);
+        return;
+    }
+
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&dscp->rw);
+
+    if (dscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+        (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+        cm_ReleaseSCache(dscp);
+        osi_Log1(afsd_logp, "RDR_CreateSymlinkEntry Not a Directory dscp=0x%p",
+                 dscp);
+        free(TargetPath);
+        return;
+    }
+
+    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;
+        if ( status == STATUS_INVALID_HANDLE)
+            status = STATUS_OBJECT_PATH_INVALID;
+        osi_Log2(afsd_logp, "RDR_CreateSymlinkEntry cm_GetSCache FID failure code=0x%x status=0x%x",
+                  code, status);
+        free(TargetPath);
+        return;
+    }
+
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                      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_Log3(afsd_logp, "RDR_CreateSymlinkEntry cm_SyncOp failure (1) scp=0x%p code=0x%x status=0x%x",
+                 scp, code, status);
+        free(TargetPath);
+        return;
+    }
+
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&scp->rw);
+
+    /* Remove the temporary object */
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY)
+        code = cm_RemoveDir(dscp, NULL, FileName, userp, &req);
+    else
+        code = cm_Unlink(dscp, NULL, FileName, userp, &req);
+    cm_ReleaseSCache(scp);
+    scp = NULL;
+    if (code && code != CM_ERROR_NOSUCHFILE) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        cm_ReleaseSCache(dscp);
+        osi_Log2(afsd_logp, "RDR_CreateSymlinkEntry Unable to delete file dscp=0x%p code=0x%x",
+                 dscp, code);
+        free(TargetPath);
+        return;
+    }
+
+    /*
+     * The target path is going to be provided by the redirector in one of the following forms:
+     *
+     * 1. Relative path.
+     * 2. Absolute path prefaced as \??\UNC\<server>\<share>\<path>
+     * 3. Absolute path prefaced as \??\<drive-letter>:\<path>
+     *
+     * Relative paths can be used with just slash conversion.  Absolute paths must be converted.
+     * UNC paths with a server name that matches cm_NetbiosName then the path is an AFS path and
+     * it must be converted to /<server>/<share>/<path>.  Other UNC paths must be converted to
+     * msdfs:\\<server>\<share>\<path>.  Local disk paths should be converted to
+     * msdfs:<drive-letter>:<path>.
+     */
+
+    if ( TargetPath[0] == '\\' ) {
+        size_t nbNameLen = strlen(cm_NetbiosName);
+        size_t len;
+        char  *s;
+
+        if ( strncmp(TargetPath, "\\??\\UNC\\", 8) == 0) {
+
+            if (strncmp(&TargetPath[8], cm_NetbiosName, nbNameLen) == 0 &&
+                TargetPath[8 + nbNameLen] == '\\')
+            {
+                /* AFS path */
+                s = strdup(&TargetPath[8 + nbNameLen]);
+                free(TargetPath);
+                TargetPath = s;
+                for (; *s; s++) {
+                    if (*s == '\\')
+                        *s = '/';
+                }
+            } else {
+                /*
+                 * non-AFS UNC path (msdfs:\\server\share\path)
+                 * strlen("msdfs:\\") == 7 + 1 for the NUL
+                 */
+                len = 8 + strlen(&TargetPath[7]);
+                s = malloc(8 + strlen(&TargetPath[7]));
+                StringCbCopy(s, len, "msdfs:\\");
+                StringCbCat(s, len, &TargetPath[7]);
+                free(TargetPath);
+                TargetPath = s;
+            }
+        } else {
+            /* non-UNC path (msdfs:<drive>:\<path> */
+            s = strdup(&TargetPath[4]);
+            free(TargetPath);
+            TargetPath = s;
+        }
+
+    } else {
+        /* relative paths require slash conversion */
+        char *s = TargetPath;
+        for (; *s; s++) {
+            if (*s == '\\')
+                *s = '/';
+        }
+    }
+
+    /* Use current time */
+    setAttr.mask = CM_ATTRMASK_UNIXMODEBITS | CM_ATTRMASK_CLIENTMODTIME;
+    setAttr.unixModeBits = 0755;
+    setAttr.clientModTime = time(NULL);
+
+    code = cm_SymLink(dscp, FileName, TargetPath, flags, &setAttr, userp, &req, &scp);
+    free(TargetPath);
+
+    if (code == 0) {
+        wchar_t shortName[13]=L"";
+        cm_dirFid_t dfid;
+        DWORD dwRemaining;
+
+        if (dscp->flags & CM_SCACHEFLAG_ANYWATCH) {
+            smb_NotifyChange(FILE_ACTION_ADDED,
+                             FILE_NOTIFY_CHANGE_DIR_NAME,
+                             dscp, FileName, NULL, TRUE);
+        }
+
+        (*ResultCB)->ResultStatus = 0;  // We will be able to fit all the data in here
+
+        (*ResultCB)->ResultBufferLength = sizeof( AFSCreateSymlinkResultCB);
+
+        pResultCB = (AFSCreateSymlinkResultCB *)(*ResultCB)->ResultData;
+
+        dwRemaining = ResultBufferLength - sizeof( AFSCreateSymlinkResultCB) + sizeof( AFSDirEnumEntry);
+
+        lock_ObtainWrite(&dscp->rw);
+        code = cm_SyncOp(dscp, NULL, userp, &req, 0,
+                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            lock_ReleaseWrite(&dscp->rw);
+            cm_ReleaseSCache(dscp);
+            cm_ReleaseSCache(scp);
+            osi_Log3(afsd_logp, "RDR_CreateSymlinkEntry cm_SyncOp failure (2) dscp=0x%p code=0x%x status=0x%x",
+                      dscp, code, status);
+            return;
+        }
+
+        pResultCB->ParentDataVersion.QuadPart = dscp->dataVersion;
+
+        cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        lock_ReleaseWrite(&dscp->rw);
+
+        if (cm_shortNames) {
+            dfid.vnode = htonl(scp->fid.vnode);
+            dfid.unique = htonl(scp->fid.unique);
+
+            if (!cm_Is8Dot3(FileName))
+                cm_Gen8Dot3NameIntW(FileName, &dfid, shortName, NULL);
+            else
+                shortName[0] = '\0';
+        }
+
+        code = RDR_PopulateCurrentEntry(&pResultCB->DirEnum, dwRemaining,
+                                        dscp, scp, userp, &req, FileName, shortName,
+                                        RDR_POP_FOLLOW_MOUNTPOINTS | RDR_POP_EVALUATE_SYMLINKS,
+                                        0, NULL, &dwRemaining);
+        cm_ReleaseSCache(scp);
+        (*ResultCB)->ResultBufferLength = ResultBufferLength - dwRemaining;
+        osi_Log0(afsd_logp, "RDR_CreateSymlinkEntry SUCCESS");
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_FILE_DELETED;
+        (*ResultCB)->ResultBufferLength = 0;
+        osi_Log2(afsd_logp, "RDR_CreateSymlinkEntry FAILURE code=0x%x status=0x%x",
+                  code, STATUS_FILE_DELETED);
+    }
+
+    cm_ReleaseSCache(dscp);
+
+    return;
+}
+
+
 void
 RDR_FlushFileEntry( IN cm_user_t *userp,
                     IN AFSFileID FileId,
@@ -3408,7 +3838,7 @@ RDR_BkgFetch(cm_scache_t *scp, void *rockp, cm_user_t *userp, cm_req_t *reqp)
             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.
@@ -3645,7 +4075,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;
@@ -5400,6 +5830,7 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
     cm_req_t    req;
     DWORD       status;
     FILETIME ft = {0x832cf000, 0x01abfcc4}; /* October 1, 1982 00:00:00 +0600 */
+    afs_uint32  flags;
 
     char volName[32]="(unknown)";
     char offLineMsg[256]="server temporarily inaccessible";
@@ -5471,7 +5902,8 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
         memcpy(&pResultCB->VolumeCreationTime, &ft, sizeof(ft));
 
         pResultCB->AvailableAllocationUnits.QuadPart = 0;
-        pResultCB->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
+        if (cm_volumeInfoReadOnlyFlag)
+            pResultCB->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
 
         pResultCB->VolumeLabelLength = cm_Utf8ToUtf16( "Freelance.Local.Root", -1, pResultCB->VolumeLabel,
                                                        (sizeof(pResultCB->VolumeLabel) / sizeof(WCHAR)) + 1);
@@ -5483,8 +5915,6 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
         if ( pResultCB->CellLength )
             pResultCB->CellLength--;
     } else {
-        memcpy(&pResultCB->VolumeCreationTime, &ft, sizeof(ft));
-
         volp = cm_GetVolumeByFID(&scp->fid);
         if (!volp) {
             code = CM_ERROR_NOSUCHVOLUME;
@@ -5492,34 +5922,65 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
         }
         volType = cm_VolumeType(volp, scp->fid.volume);
 
-        if (volType == ROVOL || volType == BACKVOL)
+        if (cm_volumeInfoReadOnlyFlag && (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);
-        if (code == 0)
+        code = -1;
+
+        if ( volType == ROVOL &&
+             (volp->flags & CM_VOLUMEFLAG_RO_SIZE_VALID))
         {
-            sync_done = 1;
+            lock_ObtainRead(&volp->rw);
+            if (volp->flags & CM_VOLUMEFLAG_RO_SIZE_VALID) {
+                volStat.BlocksInUse = volp->volumeSizeRO / 1024;
+                code = 0;
+            }
+            lock_ReleaseRead(&volp->rw);
+        }
+        
+        if (code == -1)
+        {
+            flags = CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS;
+            if (scp->volumeCreationDate == 0)
+                flags |= CM_SCACHESYNC_FORCECB;
+            code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_READ, flags);
+            if (code == 0)
+            {
+                sync_done = 1;
 
-            Name = volName;
-            OfflineMsg = offLineMsg;
-            MOTD = motd;
-            lock_ReleaseWrite(&scp->rw);
-            scp_locked = 0;
+                Name = volName;
+                OfflineMsg = offLineMsg;
+                MOTD = motd;
+                lock_ReleaseWrite(&scp->rw);
+                scp_locked = 0;
+
+                do {
+                    code = cm_ConnFromFID(&scp->fid, userp, &req, &connp);
+                    if (code) continue;
 
-            do {
-                code = cm_ConnFromFID(&scp->fid, userp, &req, &connp);
-                if (code) continue;
+                    rxconnp = cm_GetRxConn(connp);
+                    code = RXAFS_GetVolumeStatus(rxconnp, scp->fid.volume,
+                                                 &volStat, &Name, &OfflineMsg, &MOTD);
+                    rx_PutConnection(rxconnp);
 
-                rxconnp = cm_GetRxConn(connp);
-                code = RXAFS_GetVolumeStatus(rxconnp, scp->fid.volume,
-                                              &volStat, &Name, &OfflineMsg, &MOTD);
-                rx_PutConnection(rxconnp);
+                } while (cm_Analyze(connp, userp, &req, &scp->fid, NULL, 0, NULL, NULL, NULL, NULL, code));
+                code = cm_MapRPCError(code, &req);
 
-            } while (cm_Analyze(connp, userp, &req, &scp->fid, NULL, 0, NULL, NULL, NULL, NULL, code));
-            code = cm_MapRPCError(code, &req);
+                if (code == 0 && volType == ROVOL)
+                {
+
+                    lock_ObtainWrite(&volp->rw);
+                    volp->volumeSizeRO = volStat.BlocksInUse * 1024;
+                    _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_RO_SIZE_VALID);
+                    lock_ReleaseWrite(&volp->rw);
+                }
+            }
         }
 
+        if ( scp->volumeCreationDate )
+            cm_LargeSearchTimeFromUnixTime(&ft, scp->volumeCreationDate);
+        memcpy(&pResultCB->VolumeCreationTime, &ft, sizeof(ft));
+
         if (code == 0) {
             if (volType == ROVOL || volType == BACKVOL) {
                 pResultCB->TotalAllocationUnits.QuadPart = volStat.BlocksInUse;
@@ -5693,29 +6154,54 @@ RDR_GetVolumeSizeInfo( IN cm_user_t     *userp,
 
         volType = cm_VolumeType(volp, scp->fid.volume);
 
-        code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_READ,
-                         CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-        if (code == 0)
+        code = -1;
+
+        if ( volType == ROVOL &&
+             (volp->flags & CM_VOLUMEFLAG_RO_SIZE_VALID))
         {
-            sync_done = 1;
+            lock_ObtainRead(&volp->rw);
+            if (volp->flags & CM_VOLUMEFLAG_RO_SIZE_VALID) {
+                volStat.BlocksInUse = volp->volumeSizeRO / 1024;
+                code = 0;
+            }
+            lock_ReleaseRead(&volp->rw);
+        }
+        
+        if (code == -1)
+        {
+            code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_READ,
+                              CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+            if (code == 0)
+            {
+                sync_done = 1;
 
-            Name = volName;
-            OfflineMsg = offLineMsg;
-            MOTD = motd;
-            lock_ReleaseWrite(&scp->rw);
-            scp_locked = 0;
+                Name = volName;
+                OfflineMsg = offLineMsg;
+                MOTD = motd;
+                lock_ReleaseWrite(&scp->rw);
+                scp_locked = 0;
+
+                do {
+                    code = cm_ConnFromFID(&scp->fid, userp, &req, &connp);
+                    if (code) continue;
 
-            do {
-                code = cm_ConnFromFID(&scp->fid, userp, &req, &connp);
-                if (code) continue;
+                    rxconnp = cm_GetRxConn(connp);
+                    code = RXAFS_GetVolumeStatus(rxconnp, scp->fid.volume,
+                                                  &volStat, &Name, &OfflineMsg, &MOTD);
+                    rx_PutConnection(rxconnp);
 
-                rxconnp = cm_GetRxConn(connp);
-                code = RXAFS_GetVolumeStatus(rxconnp, scp->fid.volume,
-                                              &volStat, &Name, &OfflineMsg, &MOTD);
-                rx_PutConnection(rxconnp);
+                } while (cm_Analyze(connp, userp, &req, &scp->fid, NULL, 0, NULL, NULL, NULL, NULL, code));
+                code = cm_MapRPCError(code, &req);
 
-            } while (cm_Analyze(connp, userp, &req, &scp->fid, NULL, 0, NULL, NULL, NULL, NULL, code));
-            code = cm_MapRPCError(code, &req);
+                if (code == 0 && volType == ROVOL)
+                {
+
+                    lock_ObtainWrite(&volp->rw);
+                    volp->volumeSizeRO = volStat.BlocksInUse * 1024;
+                    _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_RO_SIZE_VALID);
+                    lock_ReleaseWrite(&volp->rw);
+                }
+            }
         }
 
         if (code == 0) {
@@ -6201,3 +6687,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;
+}