Windows: Set Server Prefs recalc immediately
[openafs.git] / src / WINNT / afsd / cm_ioctl.c
index 1d78f0c..4614a7f 100644 (file)
@@ -1,15 +1,19 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  * All Rights Reserved.
- * 
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
  */
 
+#include <afsconfig.h>
 #include <afs/param.h>
+#include <roken.h>
+
 #include <afs/stds.h>
 #include <afs/cellconfig.h>
+#include <afs/afs_consts.h>
 #include <afs/ptserver.h>
 #include <ubik.h>
 
@@ -64,9 +68,9 @@ void cm_InitIoctl(void)
                           LOCK_HIERARCHY_AFSDBSBMT_GLOBAL);
 }
 
-/* 
+/*
  * Utility function.  (Not currently in use.)
- * This function forces all dirty buffers to the file server and 
+ * This function forces all dirty buffers to the file server and
  * then discards the status info.
  */
 afs_int32
@@ -74,7 +78,12 @@ cm_CleanFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
 {
     long code;
 
-    code = buf_CleanVnode(scp, userp, reqp);
+    if (RDR_Initialized &&
+        RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                             scp->fid.hash, scp->fileType, AFS_INVALIDATE_FLUSHED))
+        code = CM_ERROR_WOULDBLOCK;
+    else
+        code = cm_FSync(scp, userp, reqp, FALSE);
     if (!code) {
         lock_ObtainWrite(&scp->rw);
         cm_DiscardSCache(scp);
@@ -84,7 +93,7 @@ cm_CleanFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
     return code;
 }
 
-/* 
+/*
  * Utility function.  Used within this file.
  * scp must be held but not locked.
  */
@@ -95,24 +104,37 @@ cm_FlushFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
 
 #ifdef AFS_FREELANCE_CLIENT
     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
-       cm_noteLocalMountPointChange();
+       cm_noteLocalMountPointChange(FALSE);
        return 0;
     }
 #endif
 
-    code = buf_FlushCleanPages(scp, userp, reqp);
-        
+    /*
+     * The file system will forget all knowledge of the object
+     * when it receives this message.
+     */
+    if (RDR_Initialized &&
+        RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                             scp->fid.hash, scp->fileType, AFS_INVALIDATE_FLUSHED))
+        code = CM_ERROR_WOULDBLOCK;
+    else
+        code = buf_FlushCleanPages(scp, userp, reqp);
+
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY)
+        lock_ObtainWrite(&scp->dirlock);
     lock_ObtainWrite(&scp->rw);
     cm_DiscardSCache(scp);
-    if (scp->fileType == CM_SCACHETYPE_DIRECTORY)
-        cm_ResetSCacheDirectory(scp);
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY) {
+        cm_ResetSCacheDirectory(scp, 1);
+        lock_ReleaseWrite(&scp->dirlock);
+    }
     lock_ReleaseWrite(&scp->rw);
 
     osi_Log2(afsd_logp,"cm_FlushFile scp 0x%x returns error: [%x]",scp, code);
     return code;
 }
 
-/* 
+/*
  * Utility function.  (Not currently in use)
  * IoctlPath must be parsed or skipped prior to calling.
  * scp must be held but not locked.
@@ -122,9 +144,9 @@ cm_FlushParent(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
 {
     afs_int32 code = 0;
     cm_scache_t * pscp;
-  
+
     pscp = cm_FindSCacheParent(scp);
-  
+
     /* now flush the file */
     code = cm_FlushFile(pscp, userp, reqp);
     cm_ReleaseSCache(pscp);
@@ -132,7 +154,7 @@ cm_FlushParent(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
     return code;
 }
 
-/* 
+/*
  * Utility function.  Used within this function.
  */
 afs_int32
@@ -140,11 +162,11 @@ cm_FlushVolume(cm_user_t *userp, cm_req_t *reqp, afs_uint32 cell, afs_uint32 vol
 {
     afs_int32 code = 0;
     cm_scache_t *scp;
-    int i;
+    unsigned int i;
 
 #ifdef AFS_FREELANCE_CLIENT
     if ( cell == AFS_FAKE_ROOT_CELL_ID && volume == AFS_FAKE_ROOT_VOL_ID ) {
-       cm_noteLocalMountPointChange();
+       cm_noteLocalMountPointChange(FALSE);
        return 0;
     }
 #endif
@@ -177,14 +199,14 @@ cm_FlushVolume(cm_user_t *userp, cm_req_t *reqp, afs_uint32 cell, afs_uint32 vol
  *  The pioctl functions must match
  *  this translation for paths given via our own commands (like
  *  fs).  If we do not do this, then we will try to perform an
- *  operation on a non-translated path, which we will fail to 
+ *  operation on a non-translated path, which we will fail to
  *  find, since the path was created with the translated chars.
  *  This function performs the required translation.
  *
  *  OEM character code pages are used by the non-Unicode SMB
  *  mode.  Do not use if the CM_IOCTLFLAG_USEUTF8 is set.
  */
-void 
+void
 TranslateExtendedChars(char *str)
 {
     if (!str || !*str)
@@ -249,17 +271,17 @@ int cm_UnparseIoctlString(cm_ioctl_t *ioctlp,
 
     if ((ioctlp->flags & CM_IOCTLFLAG_USEUTF8) == CM_IOCTLFLAG_USEUTF8) {
         cchout = cm_ClientStringToUtf8(cstr, cchlen, outp,
-                                       SMB_IOCTL_MAXDATA - (outp - ioctlp->outAllocp));
+                                       (int)(SMB_IOCTL_MAXDATA - (outp - ioctlp->outAllocp)));
     } else {
         if (smb_StoreAnsiFilenames) {
             cchout = WideCharToMultiByte(CP_ACP, 0, cstr, cchlen,
                                          outp,
-                                         SMB_IOCTL_MAXDATA - (outp - ioctlp->outAllocp),
+                                         (int)(SMB_IOCTL_MAXDATA - (outp - ioctlp->outAllocp)),
                                          NULL, NULL);
         } else {
             cchout = WideCharToMultiByte(CP_OEMCP, 0, cstr, cchlen,
                                          outp,
-                                         SMB_IOCTL_MAXDATA - (outp - ioctlp->outAllocp),
+                                         (int)(SMB_IOCTL_MAXDATA - (outp - ioctlp->outAllocp)),
                                          NULL, NULL);
         }
     }
@@ -271,13 +293,13 @@ int cm_UnparseIoctlString(cm_ioctl_t *ioctlp,
     return cchout;
 }
 
-/* 
- * Must be called before XXX_ParseIoctlPath or cm_SkipIoctlPath 
+/*
+ * Must be called before XXX_ParseIoctlPath or cm_SkipIoctlPath
  */
-cm_ioctlQueryOptions_t * 
+cm_ioctlQueryOptions_t *
 cm_IoctlGetQueryOptions(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
-    afs_uint32 pathlen = strlen(ioctlp->inDatap) + 1;
+    afs_uint32 pathlen = (afs_uint32) strlen(ioctlp->inDatap) + 1;
     char *p = ioctlp->inDatap + pathlen;
     cm_ioctlQueryOptions_t * optionsp = NULL;
 
@@ -290,9 +312,9 @@ cm_IoctlGetQueryOptions(struct cm_ioctl *ioctlp, struct cm_user *userp)
     return optionsp;
 }
 
-/* 
+/*
  * Must be called after smb_ParseIoctlPath or cm_SkipIoctlPath
- * or any other time that ioctlp->inDatap points at the 
+ * or any other time that ioctlp->inDatap points at the
  * cm_ioctlQueryOptions_t object.
  */
 void
@@ -300,6 +322,7 @@ cm_IoctlSkipQueryOptions(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
     cm_ioctlQueryOptions_t * optionsp = (cm_ioctlQueryOptions_t *)ioctlp->inDatap;
     ioctlp->inDatap += optionsp->size;
+    ioctlp->inCopied -= optionsp->size;
 }
 
 /* format the specified path to look like "/afs/<cellname>/usr", by
@@ -309,19 +332,19 @@ cm_IoctlSkipQueryOptions(struct cm_ioctl *ioctlp, struct cm_user *userp)
  * easier (because we can always jump past the initial "/afs" to find
  * the AFS path that should be written into afsdsbmt.ini).
  */
-void 
+void
 cm_NormalizeAfsPath(clientchar_t *outpathp, long cchlen, clientchar_t *inpathp)
 {
     clientchar_t *cp;
     clientchar_t bslash_mountRoot[256];
-       
+
     cm_ClientStrCpy(bslash_mountRoot, lengthof(bslash_mountRoot), cm_mountRootC);
     bslash_mountRoot[0] = '\\';
 
     if (!cm_ClientStrCmpNI(inpathp, cm_mountRootC, cm_mountRootCLen))
         cm_ClientStrCpy(outpathp, cchlen, inpathp);
     else if (!cm_ClientStrCmpNI(inpathp, bslash_mountRoot,
-                                cm_ClientStrLen(bslash_mountRoot)))
+                                (int)cm_ClientStrLen(bslash_mountRoot)))
         cm_ClientStrCpy(outpathp, cchlen, inpathp);
     else if ((inpathp[0] == '/') || (inpathp[0] == '\\'))
         cm_ClientStrPrintfN(outpathp, cchlen, _C("%s%s"), cm_mountRootC, inpathp);
@@ -346,10 +369,10 @@ void cm_NormalizeAfsPathAscii(char *outpathp, long outlen, char *inpathp)
 {
     char *cp;
     char bslash_mountRoot[256];
-       
+
     strncpy(bslash_mountRoot, cm_mountRoot, sizeof(bslash_mountRoot) - 1);
     bslash_mountRoot[0] = '\\';
-       
+
     if (!strnicmp (inpathp, cm_mountRoot, strlen(cm_mountRoot)))
         StringCbCopy(outpathp, outlen, inpathp);
     else if (!strnicmp (inpathp, bslash_mountRoot, strlen(bslash_mountRoot)))
@@ -362,25 +385,25 @@ void cm_NormalizeAfsPathAscii(char *outpathp, long outlen, char *inpathp)
     for (cp = outpathp; *cp != 0; ++cp) {
         if (*cp == '\\')
             *cp = '/';
-    }       
+    }
 
     if (strlen(outpathp) && (outpathp[strlen(outpathp)-1] == '/')) {
         outpathp[strlen(outpathp)-1] = 0;
     }
 
-    if (!strcmpi (outpathp, cm_mountRoot)) {
+    if (!_stricmp (outpathp, cm_mountRoot)) {
         StringCbCopy(outpathp, outlen, cm_mountRoot);
     }
 }
 
 
-/* 
+/*
  * VIOCGETAL internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetACL(cm_ioctl_t *ioctlp, cm_user_t *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code;
@@ -392,6 +415,8 @@ cm_IoctlGetACL(cm_ioctl_t *ioctlp, cm_user_t *userp, cm_scache_t *scp, cm_req_t
     int tlen;
     struct rx_connection * rxconnp;
 
+    memset(&volSync, 0, sizeof(volSync));
+
     /* now make the get acl call */
 #ifdef AFS_FREELANCE_CLIENT
     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
@@ -407,17 +432,17 @@ cm_IoctlGetACL(cm_ioctl_t *ioctlp, cm_user_t *userp, cm_scache_t *scp, cm_req_t
             acl.AFSOpaque_val = ioctlp->outDatap;
             acl.AFSOpaque_len = 0;
             code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
-            if (code) 
+            if (code)
                 continue;
 
             rxconnp = cm_GetRxConn(connp);
             code = RXAFS_FetchACL(rxconnp, &afid, &acl, &fileStatus, &volSync);
             rx_PutConnection(rxconnp);
 
-        } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync, NULL, NULL, code));
+        } while (cm_Analyze(connp, userp, reqp, &scp->fid, NULL, 0, &volSync, NULL, NULL, code));
         code = cm_MapRPCError(code, reqp);
 
-        if (code) 
+        if (code)
             return code;
     }
     /* skip over return data */
@@ -429,37 +454,41 @@ cm_IoctlGetACL(cm_ioctl_t *ioctlp, cm_user_t *userp, cm_scache_t *scp, cm_req_t
 }
 
 
-/* 
+/*
  * VIOC_FILE_CELL_NAME internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetFileCellName(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code;
     cm_cell_t *cellp;
 
 #ifdef AFS_FREELANCE_CLIENT
-    if ( cm_freelanceEnabled && 
+    if ( cm_freelanceEnabled &&
          scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
          scp->fid.volume==AFS_FAKE_ROOT_VOL_ID &&
          scp->fid.vnode==0x1 && scp->fid.unique==0x1 ) {
         StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), "Freelance.Local.Root");
         ioctlp->outDatap += strlen(ioctlp->outDatap) + 1;
         code = 0;
-    } else 
+    } else
 #endif /* AFS_FREELANCE_CLIENT */
     {
         cellp = cm_FindCellByID(scp->fid.cell, CM_FLAG_NOPROBE);
         if (cellp) {
             clientchar_t * cellname;
 
-            cellname = cm_FsStringToClientStringAlloc(cellp->name, -1, NULL); 
-            cm_UnparseIoctlString(ioctlp, NULL, cellname, -1);
-            free(cellname);
-            code = 0;
+            cellname = cm_FsStringToClientStringAlloc(cellp->name, -1, NULL);
+            if (cellname == NULL) {
+                code = CM_ERROR_NOSUCHCELL;
+            } else {
+                cm_UnparseIoctlString(ioctlp, NULL, cellname, -1);
+                free(cellname);
+                code = 0;
+            }
         } else
             code = CM_ERROR_NOSUCHCELL;
     }
@@ -467,14 +496,14 @@ cm_IoctlGetFileCellName(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
     return code;
 }
 
-       
-/* 
+
+/*
  * VIOCSETAL internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlSetACL(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code;
@@ -485,6 +514,8 @@ cm_IoctlSetACL(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp,
     AFSFid fid;
     struct rx_connection * rxconnp;
 
+    memset(&volSync, 0, sizeof(volSync));
+
 #ifdef AFS_FREELANCE_CLIENT
     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
        code = CM_ERROR_NOACCESS;
@@ -499,36 +530,40 @@ cm_IoctlSetACL(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp,
             acl.AFSOpaque_val = ioctlp->inDatap;
             acl.AFSOpaque_len = (u_int)strlen(ioctlp->inDatap)+1;
             code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
-            if (code) 
+            if (code)
                 continue;
 
             rxconnp = cm_GetRxConn(connp);
             code = RXAFS_StoreACL(rxconnp, &fid, &acl, &fileStatus, &volSync);
             rx_PutConnection(rxconnp);
 
-        } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync, NULL, NULL, code));
+        } while (cm_Analyze(connp, userp, reqp, &scp->fid, NULL, 1, &volSync, NULL, NULL, code));
         code = cm_MapRPCError(code, reqp);
 
         /* invalidate cache info, since we just trashed the ACL cache */
         lock_ObtainWrite(&scp->rw);
         cm_DiscardSCache(scp);
         lock_ReleaseWrite(&scp->rw);
+
+        if (RDR_Initialized)
+            RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                 scp->fid.hash, scp->fileType, AFS_INVALIDATE_CREDS);
     }
 
     return code;
 }
 
-/* 
+/*
  * VIOC_FLUSHALL internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlFlushAllVolumes(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_req_t *reqp)
 {
     afs_int32 code;
     cm_scache_t *scp;
-    int i;
+    unsigned int i;
 
     lock_ObtainWrite(&cm_scacheLock);
     for (i=0; i<cm_data.scacheHashTableSize; i++) {
@@ -547,13 +582,13 @@ cm_IoctlFlushAllVolumes(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_req_t
     return code;
 }
 
-/* 
+/*
  * VIOC_FLUSHVOLUME internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlFlushVolume(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code;
@@ -573,13 +608,13 @@ cm_IoctlFlushVolume(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t
     return code;
 }
 
-/* 
+/*
  * VIOCFLUSH internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlFlushFile(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code;
@@ -596,13 +631,13 @@ cm_IoctlFlushFile(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *s
 }
 
 
-/* 
+/*
  * VIOCSETVOLSTAT internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlSetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code;
@@ -630,9 +665,9 @@ cm_IoctlSetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
         if (scp->flags & CM_SCACHEFLAG_RO)
             return CM_ERROR_READONLY;
 
-        code = cm_FindVolumeByID(cellp, scp->fid.volume, userp, reqp, 
+        code = cm_FindVolumeByID(cellp, scp->fid.volume, userp, reqp,
                                  CM_GETVOL_FLAG_CREATE, &tvp);
-        if (code) 
+        if (code)
             return code;
 
         cm_PutVolume(tvp);
@@ -675,10 +710,10 @@ cm_IoctlSetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
                                          &storeStat, volName, offLineMsg, motd);
             rx_PutConnection(rxconnp);
 
-        } while (cm_Analyze(tcp, userp, reqp, &scp->fid, NULL, NULL, NULL, code));
+        } while (cm_Analyze(tcp, userp, reqp, &scp->fid, NULL, 1, NULL, NULL, NULL, code));
         code = cm_MapRPCError(code, reqp);
     }
-    
+
     /* return on failure */
     if (code)
         return code;
@@ -701,16 +736,16 @@ cm_IoctlSetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
     ioctlp->outDatap = cp;
 
     return 0;
-}       
+}
 
 
-/* 
+/*
  * VIOCGETVOLSTAT internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code;
@@ -739,23 +774,43 @@ cm_IoctlGetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
     } else
 #endif
     {
-       Name = volName;
-       OfflineMsg = offLineMsg;
-       MOTD = motd;
-       do {
-           code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
-           if (code) continue;
+        cm_fid_t    vfid;
+        cm_scache_t *vscp;
+
+        cm_SetFid(&vfid, scp->fid.cell, scp->fid.volume, 1, 1);
+        code = cm_GetSCache(&vfid, NULL, &vscp, userp, reqp);
+        if (code)
+            return code;
+
+        lock_ObtainWrite(&vscp->rw);
+        code = cm_SyncOp(vscp, NULL, userp, reqp, PRSFS_READ,
+                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        lock_ReleaseWrite(&vscp->rw);
+        if (code)
+            return code;
+
+        Name = volName;
+        OfflineMsg = offLineMsg;
+        MOTD = motd;
+        do {
+            code = cm_ConnFromFID(&vfid, userp, reqp, &connp);
+            if (code) continue;
+
+            rxconnp = cm_GetRxConn(connp);
+            code = RXAFS_GetVolumeStatus(rxconnp, vfid.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, reqp, &vfid, NULL, 0, NULL, NULL, NULL, code));
+        code = cm_MapRPCError(code, reqp);
 
-       } while (cm_Analyze(connp, userp, reqp, &scp->fid, NULL, NULL, NULL, code));
-       code = cm_MapRPCError(code, reqp);
+        lock_ObtainWrite(&vscp->rw);
+        cm_SyncOpDone(vscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        lock_ReleaseWrite(&vscp->rw);
+        cm_ReleaseSCache(vscp);
     }
 
-    if (code) 
+    if (code)
         return code;
 
     /* Copy all this junk into msg->im_data, keeping track of the lengths. */
@@ -775,13 +830,13 @@ cm_IoctlGetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
     return 0;
 }
 
-/* 
+/*
  * VIOCGETFID internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetFid(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     char *cp;
@@ -804,13 +859,13 @@ cm_IoctlGetFid(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp,
     return 0;
 }
 
-/* 
+/*
  * VIOC_GETFILETYPE internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetFileType(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code = 0;
@@ -840,13 +895,13 @@ cm_IoctlGetFileType(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t
     return code;
 }
 
-/* 
+/*
  * VIOCGETOWNER internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetOwner(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code = 0;
@@ -874,13 +929,91 @@ cm_IoctlGetOwner(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *sc
 }
 
 
-/* 
+/*
+ * VIOC_SETOWNER internals.
+ *
+ * Assumes that pioctl path has been parsed or skipped
+ * and that cm_ioctlQueryOptions_t have been parsed and skipped.
+ *
+ * scp is held but not locked.
+ *
+ */
+afs_int32
+cm_IoctlSetOwner(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
+{
+    afs_int32 code = 0;
+    char *cp;
+
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, reqp, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code == 0)
+        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&scp->rw);
+
+    if (code == 0) {
+        afs_uint32 owner;
+        cm_attr_t attr;
+
+        memset(&attr, 0, sizeof(attr));
+
+        cp = ioctlp->inDatap;
+        memcpy((char *)&owner, cp, sizeof(afs_uint32));
+
+        attr.mask = CM_ATTRMASK_OWNER;
+        attr.owner = owner;
+
+        code = cm_SetAttr(scp, &attr, userp, reqp);
+    }
+    return code;
+}
+
+
+/*
+ * VIOC_SETGROUP internals.
+ *
+ * Assumes that pioctl path has been parsed or skipped
+ * and that cm_ioctlQueryOptions_t have been parsed and skipped.
+ *
+ */
+afs_int32
+cm_IoctlSetGroup(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
+{
+    afs_int32 code = 0;
+    char *cp;
+
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, reqp, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code == 0)
+        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&scp->rw);
+
+    if (code == 0) {
+        afs_uint32 group;
+        cm_attr_t attr;
+
+        memset(&attr, 0, sizeof(attr));
+
+        cp = ioctlp->inDatap;
+        memcpy((char *)&group, cp, sizeof(afs_uint32));
+
+        attr.mask = CM_ATTRMASK_GROUP;
+        attr.group = group;
+
+        code = cm_SetAttr(scp, &attr, userp, reqp);
+    }
+    return code;
+}
+
+
+/*
  * VIOCWHEREIS internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlWhereIs(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
 {
     afs_int32 code = 0;
@@ -908,7 +1041,7 @@ cm_IoctlWhereIs(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp
         addr.s_impno = 1;
 
         cp = ioctlp->outDatap;
-        
+
         memcpy(cp, (char *)&addr, sizeof(addr));
         cp += sizeof(addr);
 
@@ -918,16 +1051,16 @@ cm_IoctlWhereIs(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp
         cp += sizeof(addr);
 
         ioctlp->outDatap = cp;
-    } else 
+    } else
 #endif
     {
         code = cm_FindVolumeByID(cellp, volume, userp, reqp, CM_GETVOL_FLAG_CREATE, &tvp);
-        if (code) 
+        if (code)
             return code;
-       
+
         cp = ioctlp->outDatap;
-        
-        tsrpp = cm_GetVolServers(tvp, volume, userp, reqp);
+
+        tsrpp = cm_GetVolServers(tvp, volume, userp, reqp, NULL);
         if (tsrpp == NULL) {
             code = CM_ERROR_NOSUCHVOLUME;
         } else {
@@ -949,15 +1082,15 @@ cm_IoctlWhereIs(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp
         cm_PutVolume(tvp);
     }
     return code;
-}       
+}
 
-/* 
+/*
  * VIOC_AFS_STAT_MT_PT internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlStatMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dscp, cm_req_t *reqp)
 {
     afs_int32 code;
@@ -967,7 +1100,7 @@ cm_IoctlStatMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache
     cp = cm_ParseIoctlStringAlloc(ioctlp, NULL);
 
     code = cm_Lookup(dscp, cp[0] ? cp : L".", CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
-    if (code) 
+    if (code)
         goto done_2;
 
     lock_ObtainWrite(&scp->rw);
@@ -1002,15 +1135,15 @@ cm_IoctlStatMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache
         free(cp);
 
     return code;
-}       
+}
 
-/* 
+/*
  * VIOC_AFS_DELETE_MT_PT internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlDeleteMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dscp, cm_req_t *reqp)
 {
     afs_int32 code;
@@ -1022,15 +1155,15 @@ cm_IoctlDeleteMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
     cp = cm_ParseIoctlStringAlloc(ioctlp, NULL);
 
     code = cm_Lookup(dscp, cp[0] ? cp : L".", CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
-        
+
     /* if something went wrong, bail out now */
     if (code)
         goto done3;
-        
+
     lock_ObtainWrite(&scp->rw);
     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-    if (code)  
+    if (code)
         goto done2;
 
     /* now check that this is a real mount point */
@@ -1043,7 +1176,8 @@ cm_IoctlDeleteMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
     lock_ReleaseWrite(&scp->rw);
 
 #ifdef USE_BPLUS
-    code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
+    code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
+                         CM_DIROP_FLAG_NONE, &dirop);
     if (code == 0) {
         code = cm_BPlusDirLookupOriginalName(&dirop, cp, &originalName);
         /* The cm_Dir* functions can't be used to lookup the
@@ -1069,7 +1203,7 @@ cm_IoctlDeleteMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
          * the freelance code to do the deletion. */
         osi_Log0(afsd_logp,"IoctlDeleteMountPoint from Freelance root dir");
         code = cm_FreelanceRemoveMount(originalName);
-    } else 
+    } else
 #endif
     {
         /* easier to do it this way */
@@ -1089,6 +1223,11 @@ cm_IoctlDeleteMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
     lock_ReleaseWrite(&scp->rw);
     cm_ReleaseSCache(scp);
 
+    if (RDR_Initialized &&
+        !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                              scp->fid.hash, scp->fileType, AFS_INVALIDATE_DELETED))
+        buf_ClearRDRFlag(scp, "deleted mp");
+
   done3:
     if (originalName != NULL)
         free(originalName);
@@ -1099,12 +1238,12 @@ cm_IoctlDeleteMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
     return code;
 }
 
-/* 
+/*
  * VIOCCKSERV internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlCheckServers(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
     cm_cell_t *cellp;
@@ -1112,9 +1251,9 @@ cm_IoctlCheckServers(struct cm_ioctl *ioctlp, struct cm_user *userp)
     char *tp;
     char *cp;
     long temp;
-    cm_server_t *tsp;
+    cm_server_t *tsp, *csp;
     int haveCell;
-        
+
     tp = ioctlp->inDatap;
     haveCell = 0;
 
@@ -1143,17 +1282,17 @@ cm_IoctlCheckServers(struct cm_ioctl *ioctlp, struct cm_user *userp)
             haveCell = 1;
     }
 
-    /* 
+    /*
      * 1: fast check, don't contact servers.
      * 2: local cell only.
      */
     if (haveCell) {
         /* have cell name, too */
         cellp = cm_GetCell(cp, (temp & 1) ? CM_FLAG_NOPROBE : 0);
-        if (!cellp) 
+        if (!cellp)
             return CM_ERROR_NOSUCHCELL;
     }
-    else 
+    else
         cellp = (cm_cell_t *) 0;
     if (!cellp && (temp & 2)) {
         /* use local cell */
@@ -1170,12 +1309,38 @@ cm_IoctlCheckServers(struct cm_ioctl *ioctlp, struct cm_user *userp)
     cp = ioctlp->outDatap;
     lock_ObtainRead(&cm_serverLock);
     for (tsp = cm_allServersp; tsp; tsp=tsp->allNextp) {
-        if (cellp && tsp->cellp != cellp) 
+        if (cellp && tsp->cellp != cellp)
             continue;  /* cell spec'd and wrong */
-        if ((tsp->flags & CM_SERVERFLAG_DOWN)
-            && tsp->type == CM_SERVER_FILE) {
-            memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
-            cp += sizeof(long);
+        if (tsp->flags & CM_SERVERFLAG_DOWN) {
+            /*
+             * for a multi-homed file server, if one of the interfaces
+             * is up, do not report the server as down.
+             */
+            if (tsp->type == CM_SERVER_FILE) {
+                for (csp = cm_allServersp; csp; csp=csp->allNextp) {
+                    if (csp->type == CM_SERVER_FILE &&
+                        !(csp->flags & CM_SERVERFLAG_DOWN) &&
+                        afs_uuid_equal(&tsp->uuid, &csp->uuid)) {
+                        break;
+                    }
+                }
+                if (csp)    /* found alternate up interface */
+                    continue;
+            }
+
+            /*
+             * all server types are being reported by ipaddr.  only report
+             * a server once regardless of how many services are down.
+             */
+            for (tp = ioctlp->outDatap; tp < cp; tp += sizeof(long)) {
+                if (!memcmp(tp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long)))
+                    break;
+            }
+
+            if (tp == cp) {
+                memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
+                cp += sizeof(long);
+            }
         }
     }
     lock_ReleaseRead(&cm_serverLock);
@@ -1184,40 +1349,40 @@ cm_IoctlCheckServers(struct cm_ioctl *ioctlp, struct cm_user *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOCCKBACK internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlCheckVolumes(cm_ioctl_t *ioctlp, cm_user_t *userp)
 {
-    cm_RefreshVolumes();
+    cm_RefreshVolumes(0);
     return 0;
-}       
+}
 
-/* 
+/*
  * VIOCSETCACHESIZE internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  *
  * This function is no longer meaningful in the current day world
  * of persistent caches.  The buf_SetNBuffers() function will
  * inevitably fail.
  */
-afs_int32 
+afs_int32
 cm_IoctlSetCacheSize(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
     afs_int32 code;
     afs_uint64 temp;
 
     memcpy(&temp, ioctlp->inDatap, sizeof(temp));
-    if (temp == 0) 
+    if (temp == 0)
         temp = cm_data.buf_nOrigBuffers;
     else {
         /* temp is in 1K units, convert to # of buffers */
         temp = temp / (cm_data.buf_blockSize / 1024);
-    }       
+    }
 
     /* now adjust the cache size */
     code = buf_SetNBuffers(temp);
@@ -1225,12 +1390,12 @@ cm_IoctlSetCacheSize(struct cm_ioctl *ioctlp, struct cm_user *userp)
     return code;
 }
 
-/* 
+/*
  * VIOC_TRACECTL internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlTraceControl(cm_ioctl_t *ioctlp, cm_user_t *userp)
 {
     afs_uint32 inValue;
@@ -1242,7 +1407,7 @@ cm_IoctlTraceControl(cm_ioctl_t *ioctlp, cm_user_t *userp)
         afsd_ForceTrace(FALSE);
         buf_ForceTrace(FALSE);
     }
-        
+
     if (inValue & 2) {
         /* set tracing value to low order bit */
         if ((inValue & 1) == 0) {
@@ -1267,27 +1432,30 @@ cm_IoctlTraceControl(cm_ioctl_t *ioctlp, cm_user_t *userp)
     memcpy(ioctlp->outDatap, &inValue, sizeof(afs_uint32));
     ioctlp->outDatap += sizeof(afs_uint32);
     return 0;
-}       
+}
 
-/* 
+/*
  * VIOCGETCACHEPARMS internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetCacheParms(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
     cm_cacheParms_t parms;
 
     memset(&parms, 0, sizeof(parms));
 
-    /* first we get, in 1K units, the cache size */
+    /* the cache size */
     parms.parms[0] = cm_data.buf_nbuffers * (cm_data.buf_blockSize / 1024);
 
-    /* and then the actual # of buffers in use (not in the free list, I guess,
-     * will be what we do).
+    /*
+     * the used cache space.  this number is not available on windows.
+     * the cm_data.buf_freeCount represents all buffers eligible for recycling.
+     * so we report the entire cache in use since reporting 0 in use disturbs
+     * many users.
      */
-    parms.parms[1] = (cm_data.buf_nbuffers - buf_CountFreeList()) * (cm_data.buf_blockSize / 1024);
+    parms.parms[1] = cm_data.buf_nbuffers * (cm_data.buf_blockSize / 1024);
 
     memcpy(ioctlp->outDatap, &parms, sizeof(parms));
     ioctlp->outDatap += sizeof(parms);
@@ -1295,12 +1463,12 @@ cm_IoctlGetCacheParms(struct cm_ioctl *ioctlp, struct cm_user *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOCGETCELL internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetCell(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
     long whichCell;
@@ -1343,8 +1511,8 @@ cm_IoctlGetCell(struct cm_ioctl *ioctlp, struct cm_user *userp)
         memset(cp, 0, max * sizeof(long));
         basep = cp;
         lock_ObtainRead(&cm_serverLock);       /* for going down server list */
-        for (i=0, serverRefp = tcellp->vlServersp; 
-             serverRefp && i<max; 
+        for (i=0, serverRefp = tcellp->vlServersp;
+             serverRefp && i<max;
              i++, serverRefp = serverRefp->next) {
             serverp = serverRefp->server;
             memcpy(cp, &serverp->addr.sin_addr.s_addr, sizeof(long));
@@ -1354,93 +1522,180 @@ cm_IoctlGetCell(struct cm_ioctl *ioctlp, struct cm_user *userp)
         ioctlp->outDatap = basep + max * sizeof(afs_int32);
 
         cellnamep = cm_FsStringToClientStringAlloc(tcellp->name, -1, NULL);
-        cm_UnparseIoctlString(ioctlp, NULL, cellnamep, -1);
-        free(cellnamep);
+        if (cellnamep) {
+            cm_UnparseIoctlString(ioctlp, NULL, cellnamep, -1);
+            free(cellnamep);
+        } else {
+            tcellp = NULL;
+        }
     }
 
-    if (tcellp) 
+    if (tcellp)
         return 0;
-    else 
+    else
         return CM_ERROR_NOMORETOKENS;  /* mapped to EDOM */
 }
 
 
-/* 
+/*
  * VIOCNEWCELL internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlNewCell(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
-    /* 
-     * All that needs to be done is to refresh server information for all cells that 
+    /*
+     * All that needs to be done is to refresh server information for all cells that
      * are already loaded.
-  
+
      * cell list will be cm_CellLock and cm_ServerLock will be held for write.
-     */  
-  
+     */
+
     cm_cell_t *cp;
     cm_cell_rock_t rock;
 
     lock_ObtainWrite(&cm_cellLock);
-  
-    for (cp = cm_data.allCellsp; cp; cp=cp->allNextp) 
+
+    for (cp = cm_data.allCellsp; cp; cp=cp->allNextp)
     {
         afs_int32 code;
 
         /* delete all previous server lists - cm_FreeServerList will ask for write on cm_ServerLock*/
         cm_FreeServerList(&cp->vlServersp, CM_FREESERVERLIST_DELETE);
-        cp->vlServersp = NULL;
         lock_ReleaseWrite(&cm_cellLock);
 
         rock.cellp = cp;
         rock.flags = 0;
-        code = cm_SearchCellFile(cp->name, cp->name, cm_AddCellProc, &rock);
-#ifdef AFS_AFSDB_ENV
+        code = cm_SearchCellRegistry(1, cp->name, cp->name, cp->linkedName, cm_AddCellProc, &rock);
+        if (code && code != CM_ERROR_FORCE_DNS_LOOKUP)
+            code = cm_SearchCellFileEx(cp->name, cp->name, cp->linkedName, cm_AddCellProc, &rock);
         if (code) {
             if (cm_dnsEnabled) {
                 int ttl;
                 code = cm_SearchCellByDNS(cp->name, cp->name, &ttl, cm_AddCellProc, &rock);
                 if ( code == 0 ) { /* got cell from DNS */
                     lock_ObtainMutex(&cp->mx);
-                    cp->flags |= CM_CELLFLAG_DNS;
-                    cp->flags &= ~CM_CELLFLAG_VLSERVER_INVALID;
+                    _InterlockedOr(&cp->flags, CM_CELLFLAG_DNS);
+                    _InterlockedAnd(&cp->flags, ~CM_CELLFLAG_VLSERVER_INVALID);
                     cp->timeout = time(0) + ttl;
                     lock_ReleaseMutex(&cp->mx);
                 }
             }
-        } 
+        }
         else {
             lock_ObtainMutex(&cp->mx);
-            cp->flags &= ~CM_CELLFLAG_DNS;
+            _InterlockedAnd(&cp->flags, ~CM_CELLFLAG_DNS);
             lock_ReleaseMutex(&cp->mx);
         }
-#endif /* AFS_AFSDB_ENV */
         if (code) {
             lock_ObtainMutex(&cp->mx);
-            cp->flags |= CM_CELLFLAG_VLSERVER_INVALID;
+            _InterlockedOr(&cp->flags, CM_CELLFLAG_VLSERVER_INVALID);
             lock_ReleaseMutex(&cp->mx);
             lock_ObtainWrite(&cm_cellLock);
         }
         else {
             lock_ObtainMutex(&cp->mx);
-            cp->flags &= ~CM_CELLFLAG_VLSERVER_INVALID;
+            _InterlockedAnd(&cp->flags, ~CM_CELLFLAG_VLSERVER_INVALID);
             lock_ReleaseMutex(&cp->mx);
             lock_ObtainWrite(&cm_cellLock);
             cm_RandomizeServer(&cp->vlServersp);
         }
     }
     lock_ReleaseWrite(&cm_cellLock);
-    return 0;       
+    return 0;
+}
+
+/*
+ * VIOCNEWCELL2 internals.
+ *
+ * Assumes that pioctl path has been parsed or skipped.
+ *
+ * The pioctl data buffer consists of the following structure:
+ *
+ *  afs_uint32 flags
+ *  afs_uint32 alternative fs port
+ *  afs_uint32 alternative vl port
+ *  afs_uint32 count of vldb servers
+ *  char[]     cellname
+ *  char[]     linkedcell
+ *  n * char[] hostnames
+ */
+afs_int32
+cm_IoctlNewCell2(struct cm_ioctl *ioctlp, struct cm_user *userp)
+{
+    afs_uint32  code = 0;
+    afs_uint32  flags = 0;
+    afs_uint32  fsport = 0;
+    afs_uint32  vlport = 0;
+    afs_uint32  i, host_count = 0;
+    char *      cellname = NULL;
+    char *      linked_cellname = NULL;
+    char *tp;
+    size_t tplen;
+    afs_uint32 *lp;
+    char * hostname[AFS_MAXHOSTS];
+    size_t len;
+
+    memset(hostname, 0, sizeof(hostname));
+
+    tp = ioctlp->inDatap;
+    tplen = ioctlp->inCopied;
+    lp = (afs_uint32 *)tp;
+
+    if (tplen >= 4 * sizeof(afs_uint32)) {
+        flags = *lp++;
+        fsport = *lp++;
+        vlport = *lp++;
+        host_count = *lp++;
+        tp = (char *)lp;
+        tplen -= 4 * sizeof(afs_uint32);
+    }
+
+    if ( FAILED(StringCbLength(tp, tplen, &len)) ||
+         len + 1 > CELL_MAXNAMELEN)
+        return CM_ERROR_INVAL;
+    cellname = tp;
+    tp += len + 1;
+    tplen -= (len + 1);
+
+    if ( FAILED(StringCbLength(tp, tplen, &len)) ||
+         len + 1 > CELL_MAXNAMELEN)
+        return CM_ERROR_INVAL;
+    linked_cellname = tp;
+    tp += len + 1;
+    tplen -= (len + 1);
+
+    if (!(flags & VIOC_NEWCELL2_FLAG_USEDNS)) {
+        for ( i=0; i<host_count; i++) {
+            if ( FAILED(StringCbLength(tp, tplen, &len)) )
+                return CM_ERROR_INVAL;
+            hostname[i] = tp;
+            tp += len + 1;
+            tplen -= (len + 1);
+        }
+    }
+
+    code = cm_CreateCellWithInfo( cellname, linked_cellname,
+                                  vlport, host_count,
+                                  hostname,
+                                  (flags & VIOC_NEWCELL2_FLAG_USEDNS) ? CM_CELLFLAG_DNS : 0);
+
+    if (code == 0 && (flags & VIOC_NEWCELL2_FLAG_USEREG)) {
+        cm_AddCellToRegistry( cellname, linked_cellname,
+                              vlport, host_count,
+                              hostname,
+                              (flags & VIOC_NEWCELL2_FLAG_USEDNS) ? CM_CELLFLAG_DNS : 0);
+    }
+    return code;
 }
 
-/* 
+/*
  * VIOC_GET_WS_CELL internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetWsCell(cm_ioctl_t *ioctlp, cm_user_t *userp)
 {
     afs_int32 code = 0;
@@ -1452,32 +1707,48 @@ cm_IoctlGetWsCell(cm_ioctl_t *ioctlp, cm_user_t *userp)
     } else if (cm_data.rootCellp) {
         clientchar_t * cellnamep = cm_FsStringToClientStringAlloc(cm_data.rootCellp->name, -1, NULL);
         /* return the default cellname to the caller */
-        cm_UnparseIoctlString(ioctlp, NULL, cellnamep, -1);
-        free(cellnamep);
+        if (cellnamep) {
+            cm_UnparseIoctlString(ioctlp, NULL, cellnamep, -1);
+            free(cellnamep);
+        } else {
+            code = CM_ERROR_NOSUCHCELL;
+        }
     } else {
         /* if we don't know our default cell, return failure */
         code = CM_ERROR_NOSUCHCELL;
-    }   
+    }
 
     return code;
 }
 
-/* 
+/*
  * VIOC_AFS_SYSNAME internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
+ *
+ * In order to support both 32-bit and 64-bit sysname lists
+ * we will treat bit-31 of the setSysName value as a flag
+ * indicating which architecture is being indicated.  If unset
+ * the architecture is 32-bit and if set the architecture is
+ * 64-bit.  This change is backward compatible with cache
+ * managers that do not support this extension.
  */
-afs_int32 
+afs_int32
 cm_IoctlSysName(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
     afs_uint32 setSysName;
     char *cp, *cp2;
     clientchar_t *inname = NULL;
-    int t, count;
+    int t;
+    unsigned int count;
+    int arch64 = 0;
 
     memcpy(&setSysName, ioctlp->inDatap, sizeof(afs_uint32));
     ioctlp->inDatap += sizeof(afs_uint32);
 
+    arch64 = (setSysName & 0x8000000) ? 1 : 0;
+    setSysName &= 0x7FFFFFF;
+
     if (setSysName) {
         /* check my args */
         if ( setSysName < 0 || setSysName > MAXNUMSYSNAMES )
@@ -1502,46 +1773,57 @@ cm_IoctlSysName(struct cm_ioctl *ioctlp, struct cm_user *userp)
     }
 
     /* Not xlating, so local case */
-    if (!cm_sysName)
-        osi_panic("cm_IoctlSysName: !cm_sysName\n", __FILE__, __LINE__);
-
     if (setSysName) {
         /* Local guy; only root can change sysname */
         /* clear @sys entries from the dnlc, once afs_lookup can
          * do lookups of @sys entries and thinks it can trust them */
         /* privs ok, store the entry, ... */
 
-        cm_ClientStrCpy(cm_sysName, lengthof(cm_sysName), inname);
-        cm_ClientStrCpy(cm_sysNameList[0], MAXSYSNAME, inname);
+        cm_ClientStrCpy(arch64 ? cm_sysName64List[0] : cm_sysNameList[0], MAXSYSNAME, inname);
 
         if (setSysName > 1) {       /* ... or list */
             for (count = 1; count < setSysName; ++count) {
                 clientchar_t * newsysname;
 
-                if (!cm_sysNameList[count])
+                if (!(arch64 ? cm_sysName64List[count] : cm_sysNameList[count]))
                     osi_panic("cm_IoctlSysName: no cm_sysNameList entry to write\n",
                               __FILE__, __LINE__);
 
                 newsysname = cm_ParseIoctlStringAlloc(ioctlp, NULL);
-                cm_ClientStrCpy(cm_sysNameList[count], MAXSYSNAME, newsysname);
+                cm_ClientStrCpy(arch64 ? cm_sysName64List[count] : cm_sysNameList[count], MAXSYSNAME, newsysname);
                 free(newsysname);
             }
         }
-        cm_sysNameCount = setSysName;
+        if ( arch64 ) {
+            cm_sysName64Count = setSysName;
+            if (cm_sysName64Count)
+                RDR_SysName( AFS_SYSNAME_ARCH_64BIT, cm_sysName64Count, cm_sysName64List );
+            else if (cm_sysNameCount)
+                RDR_SysName( AFS_SYSNAME_ARCH_64BIT, cm_sysNameCount, cm_sysNameList );
+        } else {
+            cm_sysNameCount = setSysName;
+            RDR_SysName( AFS_SYSNAME_ARCH_32BIT, cm_sysNameCount, cm_sysNameList );
+        }
     } else {
-        afs_int32 i32;
+        afs_uint32 i32;
+
+        /* return the sysname list to the caller.
+         * if there is no 64-bit list and 64-bit is requested, use the 32-bit list.
+         */
+        if ( arch64 && cm_sysName64Count == 0 )
+            arch64 = 0;
 
-        /* return the sysname to the caller */
-        i32 = cm_sysNameCount;
+        i32 = arch64 ? cm_sysName64Count : cm_sysNameCount;
         memcpy(ioctlp->outDatap, &i32, sizeof(afs_int32));
         ioctlp->outDatap += sizeof(afs_int32); /* skip found flag */
 
-        if (cm_sysNameCount) {
-            for ( count=0; count < cm_sysNameCount ; ++count) {   /* ... or list */
-                if ( !cm_sysNameList[count] || *cm_sysNameList[count] == _C('\0'))
-                    osi_panic("cm_IoctlSysName: no cm_sysNameList entry to read\n", 
+        if (i32) {
+            for ( count=0; count < i32 ; ++count) {   /* ... or list */
+                if ( !(arch64 ? cm_sysName64List[count] : cm_sysNameList[count]) ||
+                     *(arch64 ? cm_sysName64List[count] : cm_sysNameList[count]) == _C('\0'))
+                    osi_panic("cm_IoctlSysName: no cm_sysNameList entry to read\n",
                               __FILE__, __LINE__);
-                cm_UnparseIoctlString(ioctlp, NULL, cm_sysNameList[count], -1);
+                cm_UnparseIoctlString(ioctlp, NULL, arch64 ? cm_sysName64List[count] : cm_sysNameList[count], -1);
             }
         }
     }
@@ -1555,12 +1837,12 @@ cm_IoctlSysName(struct cm_ioctl *ioctlp, struct cm_user *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOC_GETCELLSTATUS internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlGetCellStatus(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
     afs_uint32 temp;
@@ -1574,7 +1856,7 @@ cm_IoctlGetCellStatus(struct cm_ioctl *ioctlp, struct cm_user *userp)
     free(fscellnamep);
     free(cellnamep);
 
-    if (!cellp) 
+    if (!cellp)
         return CM_ERROR_NOSUCHCELL;
 
     temp = 0;
@@ -1590,12 +1872,12 @@ cm_IoctlGetCellStatus(struct cm_ioctl *ioctlp, struct cm_user *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOC_SETCELLSTATUS internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlSetCellStatus(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
     afs_uint32 flags;
@@ -1609,27 +1891,27 @@ cm_IoctlSetCellStatus(struct cm_ioctl *ioctlp, struct cm_user *userp)
     free(temp);
     free(cellnamep);
 
-    if (!cellp) 
+    if (!cellp)
         return CM_ERROR_NOSUCHCELL;
 
     memcpy((char *)&flags, ioctlp->inDatap, sizeof(afs_uint32));
 
     lock_ObtainMutex(&cellp->mx);
     if (flags & CM_SETCELLFLAG_SUID)
-        cellp->flags |= CM_CELLFLAG_SUID;
+        _InterlockedOr(&cellp->flags, CM_CELLFLAG_SUID);
     else
-        cellp->flags &= ~CM_CELLFLAG_SUID;
+        _InterlockedAnd(&cellp->flags, ~CM_CELLFLAG_SUID);
     lock_ReleaseMutex(&cellp->mx);
 
     return 0;
 }
 
-/* 
+/*
  * VIOC_SETSPREFS internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
-afs_int32 
+afs_int32
 cm_IoctlSetSPrefs(struct cm_ioctl *ioctlp, struct cm_user *userp)
 {
     cm_SSetPref_t     *spin;    /* input */
@@ -1644,51 +1926,64 @@ cm_IoctlSetSPrefs(struct cm_ioctl *ioctlp, struct cm_user *userp)
     vlonly     = spin->flags;
     if ( vlonly )
         type = CM_SERVER_VLDB;
-    else    
+    else
         type = CM_SERVER_FILE;
 
-    for ( i=0; i < noServers; i++) 
+    for ( i=0; i < noServers; i++)
     {
         srvin          = &(spin->servers[i]);
         rank           = srvin->rank + (rand() & 0x000f);
         tmp.sin_addr   = srvin->host;
+        switch (type) {
+        case CM_SERVER_VLDB:
+            tmp.sin_port = htons(7003);
+            break;
+        case CM_SERVER_FILE:
+            tmp.sin_port = htons(7000);
+            break;
+        }
         tmp.sin_family = AF_INET;
 
-        tsp = cm_FindServer(&tmp, type);
+        tsp = cm_FindServer(&tmp, type, FALSE);
         if ( tsp )             /* an existing server - ref count increased */
         {
-            tsp->ipRank = rank; /* no need to protect by mutex*/
-
-            if (type == CM_SERVER_FILE)
-            {   /* fileserver */
-                /* find volumes which might have RO copy 
-                /* on server and change the ordering of 
-                 * their RO list 
+            lock_ObtainMutex(&tsp->mx);
+            tsp->adminRank = rank;
+            _InterlockedOr(&tsp->flags, CM_SERVERFLAG_PREF_SET);
+            cm_RankServer(tsp);
+            lock_ReleaseMutex(&tsp->mx);
+
+            switch (type) {
+            case CM_SERVER_FILE:
+                /*
+                 * find volumes which might have RO copy
+                 * on server and change the ordering of
+                 * their RO list
                  */
                 cm_ChangeRankVolume(tsp);
-            }
-            else       
-            {
+                break;
+            case CM_SERVER_VLDB:
                 /* set preferences for an existing vlserver */
                 cm_ChangeRankCellVLServer(tsp);
+                break;
             }
         }
         else   /* add a new server without a cell */
         {
-            tsp = cm_NewServer(&tmp, type, NULL, CM_FLAG_NOPROBE); /* refcount = 1 */
-            tsp->ipRank = rank;
+            tsp = cm_NewServer(&tmp, type, NULL, NULL, CM_FLAG_NOPROBE); /* refcount = 1 */
+            lock_ObtainMutex(&tsp->mx);
+            tsp->adminRank = rank;
+            _InterlockedOr(&tsp->flags, CM_SERVERFLAG_PREF_SET);
+            lock_ReleaseMutex(&tsp->mx);
         }
-       lock_ObtainMutex(&tsp->mx);
-       tsp->flags |= CM_SERVERFLAG_PREF_SET;
-       lock_ReleaseMutex(&tsp->mx);
        cm_PutServer(tsp);  /* decrease refcount */
     }
     return 0;
 }
 
-/* 
+/*
  * VIOC_GETSPREFS internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -1703,7 +1998,7 @@ cm_IoctlGetSPrefs(struct cm_ioctl *ioctlp, struct cm_user *userp)
     spin      = (cm_SPrefRequest_t *)ioctlp->inDatap;
     spout     = (cm_SPrefInfo_t *) ioctlp->outDatap;
     srvout    = spout->servers;
-    noServers = spin->num_servers; 
+    noServers = spin->num_servers;
     vlonly    = spin->flags & CM_SPREF_VLONLY;
     spout->num_servers = 0;
 
@@ -1714,14 +2009,14 @@ cm_IoctlGetSPrefs(struct cm_ioctl *ioctlp, struct cm_user *userp)
             continue;    /* catch up to where we left off */
         }
 
-        if ( vlonly && (tsp->type == CM_SERVER_FILE) )
+        if ( vlonly && (tsp->type != CM_SERVER_VLDB) )
             continue;   /* ignore fileserver for -vlserver option*/
-        if ( !vlonly && (tsp->type == CM_SERVER_VLDB) )
+        if ( !vlonly && (tsp->type != CM_SERVER_FILE) )
             continue;   /* ignore vlservers */
 
         srvout->host = tsp->addr.sin_addr;
-        srvout->rank = tsp->ipRank;
-        srvout++;      
+        srvout->rank = tsp->activeRank;
+        srvout++;
         spout->num_servers++;
         noServers--;
     }
@@ -1729,17 +2024,17 @@ cm_IoctlGetSPrefs(struct cm_ioctl *ioctlp, struct cm_user *userp)
 
     if ( tsp )         /* we ran out of space in the output buffer */
         spout->next_offset = i;
-    else    
-        spout->next_offset = 0; 
-    ioctlp->outDatap += sizeof(cm_SPrefInfo_t) + 
+    else
+        spout->next_offset = 0;
+    ioctlp->outDatap += sizeof(cm_SPrefInfo_t) +
         (spout->num_servers -1 ) * sizeof(cm_SPref_t) ;
     return 0;
 }
 
 
-/* 
+/*
  * VIOC_AFS_CREATE_MT_PT internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * dscp is held but not locked.
  */
@@ -1760,7 +2055,7 @@ cm_IoctlCreateMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
     cm_cell_t *cellp = NULL;
     size_t len;
 
-   /* 
+   /*
      * The fs command allows the user to specify partial cell names on NT.  These must
      * be expanded to the full cell name for mount points so that the mount points will
      * work on UNIX clients.
@@ -1785,7 +2080,7 @@ cm_IoctlCreateMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
             goto done;
         }
 
-        StringCbPrintfA(mpInfo, sizeof(mpInfo), "%c%s:%s", (char) *mpp,
+        StringCbPrintfA(mpInfo, sizeof(mpInfo), "%c%s:%s.", (char) *mpp,
                         fullCell, fsvolume);
 
     } else {
@@ -1802,10 +2097,10 @@ cm_IoctlCreateMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
 
     /* validate the target info */
     if (cm_VolNameIsID(fsvolume)) {
-        code = cm_FindVolumeByID(cellp, atoi(fsvolume), userp, reqp, 
+        code = cm_FindVolumeByID(cellp, atoi(fsvolume), userp, reqp,
                                 CM_GETVOL_FLAG_CREATE, &volp);
     } else {
-        code = cm_FindVolumeByName(cellp, fsvolume, userp, reqp, 
+        code = cm_FindVolumeByName(cellp, fsvolume, userp, reqp,
                                   CM_GETVOL_FLAG_CREATE, &volp);
     }
     if (code)
@@ -1829,13 +2124,19 @@ cm_IoctlCreateMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
         tattr.unixModeBits = 0644;
         tattr.clientModTime = time(NULL);
 
-        code = cm_SymLink(dscp, leaf, mpInfo, 0, &tattr, userp, reqp);
+        code = cm_SymLink(dscp, leaf, mpInfo, 0, &tattr, userp, reqp, NULL);
+    }
+
+    if (code == 0) {
+        if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
+            smb_NotifyChange(FILE_ACTION_ADDED,
+                             FILE_NOTIFY_CHANGE_DIR_NAME,
+                             dscp, leaf, NULL, TRUE);
+
+        if (RDR_Initialized)
+            RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode, dscp->fid.unique,
+                                 dscp->fid.hash, dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
     }
-    
-    if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-        smb_NotifyChange(FILE_ACTION_ADDED,
-                         FILE_NOTIFY_CHANGE_DIR_NAME,
-                         dscp, leaf, NULL, TRUE);
 
   done:
     if (volp)
@@ -1850,27 +2151,25 @@ cm_IoctlCreateMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
     return code;
 }
 
-/* 
+/*
  * VIOC_SYMLINK internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * dscp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlSymlink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dscp, cm_req_t *reqp, clientchar_t *leaf)
 {
     afs_int32 code;
     cm_attr_t tattr;
     char *cp;
-    char *symlp;
-    int free_syml = FALSE;
 
     if (!(ioctlp->flags & CM_IOCTLFLAG_USEUTF8)) {
         /* Translate chars for the linked to name */
         TranslateExtendedChars(ioctlp->inDatap);
     }
 
-    cp = symlp = ioctlp->inDatap;              /* contents of link */
+    cp = ioctlp->inDatap;              /* contents of link */
 
 #ifdef AFS_FREELANCE_CLIENT
     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
@@ -1878,8 +2177,8 @@ cm_IoctlSymlink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dsc
          * the freelance code to do the add. */
         fschar_t *fsleaf;
 
-        if (cp[0] == cp[1] && cp[1] == '\\' && 
-            !_strnicmp(cm_NetbiosName,cp+2,strlen(cm_NetbiosName))) 
+        if (cp[0] == cp[1] && cp[1] == '\\' &&
+            !_strnicmp(cm_NetbiosName,cp+2,strlen(cm_NetbiosName)))
         {
             /* skip \\AFS\ or \\AFS\all\ */
             char * p;
@@ -1889,7 +2188,7 @@ cm_IoctlSymlink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dsc
             cp = p;
         }
 
-        osi_Log0(afsd_logp,"IoctlCreateSymlink within Freelance root dir");
+        osi_Log0(afsd_logp,"IoctlSymlink within Freelance root dir");
         fsleaf = cm_ClientStringToFsStringAlloc(leaf, -1, NULL);
         code = cm_FreelanceAddSymlink(fsleaf, cp, NULL);
         free(fsleaf);
@@ -1900,25 +2199,32 @@ cm_IoctlSymlink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dsc
         tattr.mask = CM_ATTRMASK_UNIXMODEBITS;
         tattr.unixModeBits = 0755;
 
-        code = cm_SymLink(dscp, leaf, cp, 0, &tattr, userp, reqp);
+        code = cm_SymLink(dscp, leaf, cp, 0, &tattr, userp, reqp, NULL);
+    }
+
+    if (code == 0) {
+        if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
+            smb_NotifyChange(FILE_ACTION_ADDED,
+                             FILE_NOTIFY_CHANGE_FILE_NAME
+                             | FILE_NOTIFY_CHANGE_DIR_NAME,
+                             dscp, leaf, NULL, TRUE);
+
+        if (RDR_Initialized)
+            RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode, dscp->fid.unique,
+                                 dscp->fid.hash, dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
     }
 
-    if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-        smb_NotifyChange(FILE_ACTION_ADDED,
-                          FILE_NOTIFY_CHANGE_FILE_NAME
-                          | FILE_NOTIFY_CHANGE_DIR_NAME,
-                          dscp, leaf, NULL, TRUE);
     return code;
 }
 
 
-/* 
+/*
  * VIOC_LISTSYMLINK internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * dscp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlListlink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dscp, cm_req_t *reqp)
 {
     afs_int32 code;
@@ -1937,7 +2243,7 @@ cm_IoctlListlink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *ds
     clientp = cm_Utf8ToClientStringAlloc(cp, -1, NULL);
     code = cm_Lookup(dscp, clientp[0] ? clientp : L".", CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
     free(clientp);
-    if (code) 
+    if (code)
         return code;
 
     /* Check that it's a real symlink */
@@ -1968,7 +2274,7 @@ cm_IoctlListlink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *ds
         if (newRootScp != NULL)
             cm_ReleaseSCache(newRootScp);
         code = 0;
-    } else if (code == CM_ERROR_PATH_NOT_COVERED && 
+    } else if (code == CM_ERROR_PATH_NOT_COVERED &&
                 scp->fileType == CM_SCACHETYPE_DFSLINK ||
                code == CM_ERROR_NOSUCHPATH &&
                 scp->fileType == CM_SCACHETYPE_INVALID) {
@@ -1983,13 +2289,13 @@ cm_IoctlListlink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *ds
     return code;
 }
 
-/* 
+/*
  * VIOC_ISSYMLINK internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * dscp is held but not locked.
  */
-afs_int32 
+afs_int32
 cm_IoctlIslink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dscp, cm_req_t *reqp)
 {/*CHECK FOR VALID SYMLINK*/
     afs_int32 code;
@@ -2002,7 +2308,6 @@ cm_IoctlIslink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dscp
         TranslateExtendedChars(ioctlp->inDatap);
     }
     cp = ioctlp->inDatap;
-    osi_LogEvent("cm_IoctlListlink",NULL," name[%s]",cp);
 
     clientp = cm_Utf8ToClientStringAlloc(cp, -1, NULL);
     code = cm_Lookup(dscp, clientp[0] ? clientp : L".", CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
@@ -2019,9 +2324,9 @@ cm_IoctlIslink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *dscp
     return code;
 }
 
-/* 
+/*
  * VIOC_DELSYMLINK internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * dscp is held but not locked.
  */
@@ -2047,13 +2352,13 @@ cm_IoctlDeletelink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *
     /* if something went wrong, bail out now */
     if (code)
         goto done3;
-        
+
     lock_ObtainWrite(&scp->rw);
     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
     if (code)
         goto done2;
-       
+
     /* now check that this is a real symlink */
     if (scp->fileType != CM_SCACHETYPE_SYMLINK &&
         scp->fileType != CM_SCACHETYPE_DFSLINK &&
@@ -2061,12 +2366,13 @@ cm_IoctlDeletelink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *
         code = CM_ERROR_INVAL;
         goto done1;
     }
-       
+
     /* time to make the RPC, so drop the lock */
     lock_ReleaseWrite(&scp->rw);
-        
+
 #ifdef USE_BPLUS
-    code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
+    code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
+                         CM_DIROP_FLAG_NONE, &dirop);
     if (code == 0) {
         code = cm_BPlusDirLookupOriginalName(&dirop, clientp, &originalName);
         /* cm_Dir*() functions can't be used to lookup the original
@@ -2093,7 +2399,7 @@ cm_IoctlDeletelink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *
          * the freelance code to do the add. */
         osi_Log0(afsd_logp,"IoctlDeletelink from Freelance root dir");
         code = cm_FreelanceRemoveSymlink(originalName);
-    } else 
+    } else
 #endif
     {
         /* easier to do it this way */
@@ -2119,6 +2425,11 @@ cm_IoctlDeletelink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *
     lock_ReleaseWrite(&scp->rw);
     cm_ReleaseSCache(scp);
 
+    if (RDR_Initialized &&
+        !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                              scp->fid.hash, scp->fileType, AFS_INVALIDATE_DELETED))
+        buf_ClearRDRFlag(scp, "deleted link");
+
   done3:
     free(clientp);
 
@@ -2126,11 +2437,11 @@ cm_IoctlDeletelink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *
 }
 
 #ifdef QUERY_AFSID
-/* Utility function.  Not currently used.  
+/* Utility function.  Not currently used.
  * This function performs a PTS lookup which has traditionally
  * not been performed by the cache manager.
  */
-afs_int32 
+afs_int32
 cm_UsernameToId(char *uname, cm_ucell_t * ucellp, afs_uint32* uid)
 {
     afs_int32 code;
@@ -2145,6 +2456,7 @@ cm_UsernameToId(char *uname, cm_ucell_t * ucellp, afs_uint32* uid)
     int i;
     char * p, * r;
 
+    memset(&info, 0, sizeof(info));
     tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
     code = afsconf_GetCellInfo(tdir, ucellp->cellp->name, "afsprot", &info);
     afsconf_Close(tdir);
@@ -2153,8 +2465,8 @@ cm_UsernameToId(char *uname, cm_ucell_t * ucellp, afs_uint32* uid)
     sc[1] = 0;
     sc[2] = 0;
 
-    /* we have the token that was given to us in the settoken 
-     * call.   we just have to use it. 
+    /* we have the token that was given to us in the settoken
+     * call.   we just have to use it.
      */
     scIndex = 2;       /* kerberos ticket */
     sc[2] = rxkad_NewClientSecurityObject(rxkad_clear, &ucellp->sessionKey,
@@ -2170,6 +2482,8 @@ cm_UsernameToId(char *uname, cm_ucell_t * ucellp, afs_uint32* uid)
 
     code = ubik_ClientInit(serverconns, &pruclient);
     if (code) {
+        if (info.linkedCell)
+            free(info.linkedCell);
        return code;
     }
 
@@ -2203,13 +2517,15 @@ cm_UsernameToId(char *uname, cm_ucell_t * ucellp, afs_uint32* uid)
        pruclient = NULL;
     }
 
+    if (info.linkedCell)
+        free(info.linkedCell);
     return 0;
 }
 #endif /* QUERY_AFSID */
 
 #if 0
 /* This has been copied to smb_IoctlSetToken in its entirety.
- * An equivalent version will need to be produced for the 
+ * An equivalent version will need to be produced for the
  * redirector and some extensive refactoring might be required.
  */
 afs_int32
@@ -2267,7 +2583,7 @@ cm_IoctlSetToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
 
         /* cell name */
         cellp = cm_GetCell(tp, CM_FLAG_CREATE | CM_FLAG_NOPROBE);
-        if (!cellp) 
+        if (!cellp)
             return CM_ERROR_NOSUCHCELL;
         tp += strlen(tp) + 1;
 
@@ -2297,8 +2613,14 @@ cm_IoctlSetToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
     }
 
     if (flags & PIOCTL_LOGON) {
-        userp = smb_FindCMUserByName(smbname, ioctlp->fidp->vcp->rname,
+        clientchar_t *cname;
+
+        cname = cm_FsStringToClientStringAlloc(smbname, -1, NULL);
+
+        userp = smb_FindCMUserByName(cname, ioctlp->fidp->vcp->rname,
                                     SMB_FLAG_CREATE|SMB_FLAG_AFSLOGON);
+        if (cname)
+            free(cname);
        release_userp = 1;
     }
 
@@ -2330,14 +2652,14 @@ cm_IoctlSetToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
        cm_UsernameToId(uname, ucellp, &ucellp->uid);
 #endif
     }
-    ucellp->flags |= CM_UCELLFLAG_RXKAD;
+    _InterlockedOr(&ucellp->flags, CM_UCELLFLAG_RXKAD);
     lock_ReleaseMutex(&userp->mx);
 
     if (flags & PIOCTL_LOGON) {
         ioctlp->flags |= CM_IOCTLFLAG_LOGON;
     }
 
-    cm_ResetACLCache(userp);
+    cm_ResetACLCache(cellp, userp);
 
     if (release_userp)
        cm_ReleaseUser(userp);
@@ -2346,9 +2668,9 @@ cm_IoctlSetToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
 }
 #endif
 
-/* 
+/*
  * VIOC_GETTOK internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2378,7 +2700,7 @@ cm_IoctlGetTokenIter(struct cm_ioctl *ioctlp, struct cm_user *userp)
         }
         if (ucellp->flags & CM_UCELLFLAG_RXKAD)
             break;
-    }       
+    }
 
     /* new iterator */
     temp = ucellp->iterator + 1;
@@ -2403,7 +2725,7 @@ cm_IoctlGetTokenIter(struct cm_ioctl *ioctlp, struct cm_user *userp)
 
     /*
      * This field is supposed to hold the session key
-     * but we don't want to make it easier for someone 
+     * but we don't want to make it easier for someone
      * to attack the cache.  The user gave us the session
      * key in the first place.
      */
@@ -2434,9 +2756,9 @@ cm_IoctlGetTokenIter(struct cm_ioctl *ioctlp, struct cm_user *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOC_NEWGETTOK internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2456,7 +2778,7 @@ cm_IoctlGetToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
 
     /* cell name is right here */
     cellp = cm_GetCell(tp, 0);
-    if (!cellp) 
+    if (!cellp)
         return CM_ERROR_NOSUCHCELL;
     tp += strlen(tp) + 1;
 
@@ -2512,14 +2834,14 @@ cm_IoctlGetToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
 
     lock_ReleaseMutex(&userp->mx);
 
-    cm_RegisterNewTokenEvent(uuid, ucellp->sessionKey.data);
+    cm_RegisterNewTokenEvent(uuid, ucellp->sessionKey.data, NULL);
 
     return 0;
 }
 
-/* 
+/*
  * VIOCDELTOK internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2533,7 +2855,7 @@ cm_IoctlDelToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
 
     /* cell name is right here */
     cellp = cm_GetCell(ioctlp->inDatap, 0);
-    if (!cellp) 
+    if (!cellp)
         return CM_ERROR_NOSUCHCELL;
 
     lock_ObtainMutex(&userp->mx);
@@ -2555,19 +2877,19 @@ cm_IoctlDelToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
     ucellp->kvno = 0;
     ucellp->expirationTime = 0;
     ucellp->userName[0] = '\0';
-    ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
+    _InterlockedAnd(&ucellp->flags, ~CM_UCELLFLAG_RXKAD);
     ucellp->gen++;
 
     lock_ReleaseMutex(&userp->mx);
 
-    cm_ResetACLCache(userp);
+    cm_ResetACLCache(cellp, userp);
 
     return 0;
 }
 
-/* 
+/*
  * VIOCDELALLTOK internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2589,20 +2911,20 @@ cm_IoctlDelAllToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
        ucellp->kvno = 0;
        ucellp->expirationTime = 0;
        ucellp->userName[0] = '\0';
-        ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
+        _InterlockedAnd(&ucellp->flags, ~CM_UCELLFLAG_RXKAD);
         ucellp->gen++;
     }
 
     lock_ReleaseMutex(&userp->mx);
 
-    cm_ResetACLCache(userp);
+    cm_ResetACLCache(NULL, userp);
 
     return 0;
 }
 
-/* 
+/*
  * VIOC_MAKESUBMOUNT internals.  (This function should be deprecated)
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2633,13 +2955,13 @@ cm_IoctlMakeSubmount(cm_ioctl_t *ioctlp, cm_user_t *userp)
      * has to match our path.
      */
 
-    RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
+    RegCreateKeyEx( HKEY_LOCAL_MACHINE,
                     AFSREG_CLT_OPENAFS_SUBKEY "\\Submounts",
-                    0, 
-                    "AFS", 
+                    0,
+                    "AFS",
                     REG_OPTION_NON_VOLATILE,
                     KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
-                    NULL, 
+                    NULL,
                     &hkSubmounts,
                     NULL );
 
@@ -2658,7 +2980,7 @@ cm_IoctlMakeSubmount(cm_ioctl_t *ioctlp, cm_user_t *userp)
              * leading "/afs" when writing out the submount.
              */
             RegSetValueEx( hkSubmounts, submountreqp, 0,
-                           REG_EXPAND_SZ, 
+                           REG_EXPAND_SZ,
                            (strlen(&afspath[strlen(cm_mountRoot)])) ?
                            &afspath[strlen(cm_mountRoot)]:"/",
                            (strlen(&afspath[strlen(cm_mountRoot)])) ?
@@ -2731,7 +3053,7 @@ cm_IoctlMakeSubmount(cm_ioctl_t *ioctlp, cm_user_t *userp)
             thisAutoSubmount = atoi (&submountName[strlen("auto")]);
             nextAutoSubmount = max (nextAutoSubmount,
                                      thisAutoSubmount+1);
-        }       
+        }
 
         if ((submountPathLen == 0) ||
              (submountPathLen == sizeof(submountPath) - 1)) {
@@ -2760,10 +3082,10 @@ cm_IoctlMakeSubmount(cm_ioctl_t *ioctlp, cm_user_t *userp)
 
     StringCbPrintfA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), "auto%ld", nextAutoSubmount);
 
-    RegSetValueEx( hkSubmounts, 
+    RegSetValueEx( hkSubmounts,
                    ioctlp->outDatap,
                    0,
-                   REG_EXPAND_SZ, 
+                   REG_EXPAND_SZ,
                    (strlen(&afspath[strlen(cm_mountRoot)])) ?
                    &afspath[strlen(cm_mountRoot)]:"/",
                    (strlen(&afspath[strlen(cm_mountRoot)])) ?
@@ -2775,9 +3097,9 @@ cm_IoctlMakeSubmount(cm_ioctl_t *ioctlp, cm_user_t *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOC_GETRXKCRYPT internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2789,9 +3111,9 @@ cm_IoctlGetRxkcrypt(cm_ioctl_t *ioctlp, cm_user_t *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOC_SETRXKCRYPT internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2812,9 +3134,9 @@ cm_IoctlSetRxkcrypt(cm_ioctl_t *ioctlp, cm_user_t *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOC_RXSTAT_PROC internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2839,9 +3161,9 @@ cm_IoctlRxStatProcess(struct cm_ioctl *ioctlp, struct cm_user *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOC_RXSTAT_PEER internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2866,9 +3188,9 @@ cm_IoctlRxStatPeer(struct cm_ioctl *ioctlp, struct cm_user *userp)
     return 0;
 }
 
-/* 
+/*
  * VIOC_UNICODECTL internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2900,9 +3222,9 @@ cm_IoctlUnicodeControl(struct cm_ioctl *ioctlp, struct cm_user * userp)
     return 0;
 }
 
-/* 
+/*
  * VIOC_UUIDCTL internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -2927,16 +3249,16 @@ cm_IoctlUUIDControl(struct cm_ioctl * ioctlp, struct cm_user *userp)
 
 
 
-/* 
- * functions to dump contents of various structures. 
+/*
+ * functions to dump contents of various structures.
  * In debug build (linked with crt debug library) will dump allocated but not freed memory
  */
 extern int cm_DumpSCache(FILE *outputFile, char *cookie, int lock);
 extern int cm_DumpBufHashTable(FILE *outputFile, char *cookie, int lock);
 
-/* 
+/*
  * VIOC_TRACEMEMDUMP internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * dscp is held but not locked.
  */
@@ -2948,13 +3270,13 @@ cm_IoctlMemoryDump(struct cm_ioctl *ioctlp, struct cm_user *userp)
     char logfileName[MAX_PATH+1];
     char *cookie;
     DWORD dwSize;
-  
-#ifdef _DEBUG  
+
+#ifdef _DEBUG
     static _CrtMemState memstate;
 #endif
-  
+
     memcpy(&inValue, ioctlp->inDatap, sizeof(afs_int32));
-  
+
     dwSize = GetEnvironmentVariable("TEMP", logfileName, sizeof(logfileName));
     if ( dwSize == 0 || dwSize > sizeof(logfileName) )
     {
@@ -2963,26 +3285,26 @@ cm_IoctlMemoryDump(struct cm_ioctl *ioctlp, struct cm_user *userp)
     strncat(logfileName, "\\afsd_alloc.log", sizeof(logfileName));
 
     hLogFile = CreateFile(logfileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-  
+
     if (!hLogFile)
     {
       /* error */
       inValue = -1;
       memcpy(ioctlp->outDatap, &inValue, sizeof(afs_int32));
       ioctlp->outDatap += sizeof(afs_int32);
-      
-      return 0;               
+
+      return 0;
     }
-  
+
     SetFilePointer(hLogFile, 0, NULL, FILE_END);
-  
+
     cookie = inValue ? "b" : "e";
-  
-#ifdef _DEBUG  
-  
+
+#ifdef _DEBUG
+
     if (inValue)
     {
-      _CrtMemCheckpoint(&memstate);           
+      _CrtMemCheckpoint(&memstate);
     }
     else
     {
@@ -2991,22 +3313,25 @@ cm_IoctlMemoryDump(struct cm_ioctl *ioctlp, struct cm_user *userp)
         _CrtMemDumpAllObjectsSince(&memstate);
     }
 #endif
-  
+
     /* dump all interesting data */
     cm_MemDumpDirStats(hLogFile, cookie, 1);
     cm_MemDumpBPlusStats(hLogFile, cookie, 1);
-    cm_DumpCells(hLogFile, cookie, 1);
-    cm_DumpVolumes(hLogFile, cookie, 1);
-    cm_DumpSCache(hLogFile, cookie, 1);
-    cm_DumpBufHashTable(hLogFile, cookie, 1);
+    cm_DumpCells(hLogFile, cookie, !RDR_Initialized);
+    cm_DumpVolumes(hLogFile, cookie, !RDR_Initialized);
+    cm_DumpSCache(hLogFile, cookie, !RDR_Initialized);
+    cm_DumpBufHashTable(hLogFile, cookie, !RDR_Initialized);
+    cm_DumpServers(hLogFile, cookie, !RDR_Initialized);
     smb_DumpVCP(hLogFile, cookie, 1);
+    rx_DumpCalls(hLogFile, cookie);
+    rx_DumpPackets(hLogFile, cookie);
+
+    CloseHandle(hLogFile);
 
-    CloseHandle(hLogFile);                          
-  
     inValue = 0;       /* success */
     memcpy(ioctlp->outDatap, &inValue, sizeof(long));
     ioctlp->outDatap += sizeof(long);
-  
+
     return 0;
 }
 
@@ -3017,20 +3342,20 @@ cm_CheckServersStatus(cm_serverRef_t *serversp)
     afs_int32 code = 0;
     cm_serverRef_t *tsrp;
     cm_server_t *tsp;
-    int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
+    int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1, allDeleted = 1;
 
     if (serversp == NULL) {
-       osi_Log1(afsd_logp, "cm_CheckServersStatus returning 0x%x", CM_ERROR_ALLDOWN);
-       return CM_ERROR_ALLDOWN;
+       osi_Log1(afsd_logp, "cm_CheckServersStatus returning 0x%x", CM_ERROR_EMPTY);
+       return CM_ERROR_EMPTY;
     }
 
     lock_ObtainRead(&cm_serverLock);
     for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
         if (tsrp->status == srv_deleted)
             continue;
+        allDeleted = 0;
         if (tsp = tsrp->server) {
             cm_GetServerNoLock(tsp);
-            lock_ReleaseRead(&cm_serverLock);
             if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
                 allDown = 0;
                 if (tsrp->status == srv_busy) {
@@ -3042,19 +3367,20 @@ cm_CheckServersStatus(cm_serverRef_t *serversp)
                 } else {
                     allOffline = 0;
                     allBusy = 0;
-                    cm_PutServer(tsp);
+                    cm_PutServerNoLock(tsp);
                     goto done;
                 }
             }
-            lock_ObtainRead(&cm_serverLock);
             cm_PutServerNoLock(tsp);
         }
-    }   
+    }
     lock_ReleaseRead(&cm_serverLock);
 
-    if (allDown) 
+    if (allDeleted)
+        code = CM_ERROR_EMPTY;
+    else if (allDown)
         code = CM_ERROR_ALLDOWN;
-    else if (allBusy) 
+    else if (allBusy)
         code = CM_ERROR_ALLBUSY;
     else if (allOffline || (someBusy && someOffline))
         code = CM_ERROR_ALLOFFLINE;
@@ -3064,9 +3390,9 @@ cm_CheckServersStatus(cm_serverRef_t *serversp)
     return code;
 }
 
-/* 
+/*
  * VIOC_PATH_AVAILABILITY internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  * scp is held but not locked.
  */
@@ -3078,7 +3404,7 @@ cm_IoctlPathAvailability(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
     cm_volume_t *tvp;
     cm_vol_state_t *statep;
     afs_uint32 volume;
-        
+
 #ifdef AFS_FREELANCE_CLIENT
     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
        code = 0;
@@ -3093,9 +3419,9 @@ cm_IoctlPathAvailability(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
             return CM_ERROR_NOSUCHCELL;
 
         code = cm_FindVolumeByID(cellp, volume, userp, reqp, CM_GETVOL_FLAG_CREATE, &tvp);
-        if (code) 
+        if (code)
             return code;
-       
+
         statep = cm_VolumeStateByID(tvp, volume);
         switch (statep->state) {
         case vl_online:
@@ -3115,11 +3441,11 @@ cm_IoctlPathAvailability(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
         cm_PutVolume(tvp);
     }
     return code;
-}       
+}
 
-/* 
+/*
  * VIOC_VOLSTAT_TEST internals.
- * 
+ *
  * Assumes that pioctl path has been parsed or skipped.
  */
 afs_int32
@@ -3135,7 +3461,7 @@ cm_IoctlVolStatTest(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_req_t *re
     testp = (struct VolStatTest *)ioctlp->inDatap;
 
 #ifdef AFS_FREELANCE_CLIENT
-    if (testp->fid.cell == -1) 
+    if (testp->fid.cell == -1)
         return CM_ERROR_NOACCESS;
 #endif
 
@@ -3190,7 +3516,7 @@ cm_IoctlVolStatTest(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_req_t *re
 
     if (code)
         return code;
-       
+
     if (testp->fid.volume)
         statep = cm_VolumeStateByID(volp, testp->fid.volume);
     else
@@ -3204,4 +3530,102 @@ cm_IoctlVolStatTest(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_req_t *re
     cm_PutVolume(volp);
 
     return code;
-}       
+}
+
+/*
+ * VIOC_GETUNIXMODE internals.
+ *
+ * Assumes that pioctl path has been parsed or skipped.
+ * scp is held but not locked.
+ */
+afs_int32
+cm_IoctlGetUnixMode(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
+{
+    afs_int32 code = 0;
+    char *cp;
+
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, reqp, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code == 0)
+        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&scp->rw);
+
+    if (code == 0) {
+        /* Copy all this junk into msg->im_data, keeping track of the lengths. */
+        cp = ioctlp->outDatap;
+        memcpy(cp, (char *)&scp->unixModeBits, sizeof(afs_uint32));
+        cp += sizeof(afs_uint32);
+
+        /* return new size */
+        ioctlp->outDatap = cp;
+    }
+    return code;
+}
+
+
+/*
+ * VIOC_SETUNIXMODE internals.
+ *
+ * Assumes that pioctl path has been parsed or skipped
+ * and that cm_ioctlQueryOptions_t have been parsed and skipped.
+ *
+ * scp is held but not locked.
+ */
+afs_int32
+cm_IoctlSetUnixMode(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
+{
+    afs_int32 code = 0;
+    char *cp;
+
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, reqp, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code == 0)
+        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&scp->rw);
+
+    if (code == 0) {
+        afs_uint32 unixModeBits;
+        cm_attr_t attr;
+
+        memset(&attr, 0, sizeof(attr));
+
+        cp = ioctlp->inDatap;
+        memcpy((char *)&unixModeBits, cp, sizeof(afs_uint32));
+
+        attr.mask = CM_ATTRMASK_UNIXMODEBITS;
+        attr.unixModeBits = unixModeBits;
+
+        code = cm_SetAttr(scp, &attr, userp, reqp);
+    }
+    return code;
+}
+
+/*
+ * VIOC_GETVERIFYDATA internals.
+ *
+ * Assumes that pioctl path has been parsed or skipped.
+ */
+afs_int32
+cm_IoctlGetVerifyData(cm_ioctl_t *ioctlp)
+{
+    memcpy(ioctlp->outDatap, &cm_verifyData, sizeof(cm_verifyData));
+    ioctlp->outDatap += sizeof(cm_verifyData);
+
+    return 0;
+}
+
+/*
+ * VIOC_SETVERIFYDATA internals.
+ *
+ * Assumes that pioctl path has been parsed or skipped.
+ */
+afs_int32
+cm_IoctlSetVerifyData(cm_ioctl_t *ioctlp)
+{
+    memcpy(&cm_verifyData, ioctlp->inDatap, sizeof(cm_verifyData));
+
+    return 0;
+}
+