Windows: mp target trailing dot not part vol name
[openafs.git] / src / WINNT / afsd / cm_freelance.c
index 4899ab0..d367b1b 100644 (file)
@@ -1,4 +1,7 @@
+#include <afsconfig.h>
 #include <afs/param.h>
+#include <roken.h>
+
 #include <afs/stds.h>
 
 #include <windows.h>
@@ -7,6 +10,7 @@
 #include <stdlib.h>
 #include <malloc.h>
 #include <string.h>
+#include <cm_nls.h>
 
 #include <WINNT/afsreg.h>
 #include "afsd.h"
 
 #ifdef AFS_FREELANCE_CLIENT
 #include "cm_freelance.h"
-#include "stdio.h"
+#include <stdio.h>
+#define STRSAFE_NO_DEPRECATE
+#include <strsafe.h>
 
 extern void afsi_log(char *pattern, ...);
 
-int cm_noLocalMountPoints;
+static unsigned int cm_noLocalMountPoints = 0;
 char * cm_FakeRootDir = NULL;
 int cm_fakeDirSize = 0;
-int cm_fakeDirCallback=0;
-int cm_fakeGettingCallback=0;
-cm_localMountPoint_t* cm_localMountPoints;
+static cm_localMountPoint_t* cm_localMountPoints;
 osi_mutex_t cm_Freelance_Lock;
-int cm_localMountPointChangeFlag = 0;
+static int cm_localMountPointChangeFlag = 0;
 int cm_freelanceEnabled = 1;
+int cm_freelanceImportCellServDB = 0;
 time_t FakeFreelanceModTime = 0x3b49f6e2;
 
 static int freelance_ShutdownFlag = 0;
@@ -75,7 +80,7 @@ void cm_FreelanceChangeNotifier(void * parmp) {
                 hFreelanceChangeEvent = 0;         
                 return;                            
             }                                      
-            cm_noteLocalMountPointChange();
+            cm_noteLocalMountPointChange(FALSE);
         }
     }
 }
@@ -120,7 +125,7 @@ void cm_FreelanceSymlinkChangeNotifier(void * parmp) {
                 hFreelanceSymlinkChangeEvent = 0;         
                 return;                            
             }                                      
-            cm_noteLocalMountPointChange();
+            cm_noteLocalMountPointChange(FALSE);
         }
     }
 }
@@ -133,13 +138,37 @@ cm_FreelanceShutdown(void)
         thrd_SetEvent(hFreelanceChangeEvent); 
     if (hFreelanceSymlinkChangeEvent != 0)           
         thrd_SetEvent(hFreelanceSymlinkChangeEvent); 
-}                                             
+}
+
+static long
+cm_FreelanceAddCSDBMountPoints(void *rockp, char *cellNamep)
+{
+    char szCellName[CELL_MAXNAMELEN+1] = ".";
+
+    cm_FsStrCpy( &szCellName[1], CELL_MAXNAMELEN, cellNamep);
+    /* create readonly mount point */
+    cm_FreelanceAddMount( cellNamep, cellNamep, "root.cell", 0, NULL);
+
+    /* create read/write mount point */
+    cm_FreelanceAddMount( szCellName, szCellName, "root.cell", 1, NULL);
+
+    return 0;
+}
+
+void
+cm_FreelanceImportCellServDB(void)
+{
+    cm_EnumerateCellRegistry( TRUE, cm_FreelanceAddCSDBMountPoints, NULL);
+    cm_EnumerateCellFile( TRUE, cm_FreelanceAddCSDBMountPoints, NULL);
+}
 
 void cm_InitFreelance() {
     thread_t phandle;
     int lpid;
 
-    lock_InitializeMutex(&cm_Freelance_Lock, "Freelance Lock");
+    lock_InitializeMutex(&cm_Freelance_Lock, "Freelance Lock", LOCK_HIERARCHY_FREELANCE_GLOBAL);
+
+    lock_ObtainMutex(&cm_Freelance_Lock);
 
     // yj: first we make a call to cm_initLocalMountPoints
     // to read all the local mount points from the registry
@@ -148,7 +177,12 @@ void cm_InitFreelance() {
     // then we make a call to InitFakeRootDir to create
     // a fake root directory based on the local mount points
     cm_InitFakeRootDir();
+
+    // increment the fakeDirVersion to force status updates for
+    // all cached Freelance objects
+    cm_data.fakeDirVersion++;
     // --- end of yj code
+    lock_ReleaseMutex(&cm_Freelance_Lock);
 
     /* Start the registry monitor */
     phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceChangeNotifier,
@@ -163,7 +197,7 @@ void cm_InitFreelance() {
 }
 
 /* yj: Initialization of the fake root directory */
-/* to be called while holding freelance lock unless during init. */
+/* to be called while holding freelance lock. */
 void cm_InitFakeRootDir() {
     int i, t1, t2;
     char* currentPos;
@@ -192,7 +226,7 @@ void cm_InitFakeRootDir() {
     int curChunk = 13; // chunks 0 - 12 are used for header stuff
                         // of the first page in the directory
     int curPage = 0;
-    int curDirEntry = 0;
+    unsigned int curDirEntry = 0;
     int curDirEntryInPage = 0;
     int sizeOfCurEntry;
     int dirSize;
@@ -217,7 +251,7 @@ void cm_InitFakeRootDir() {
     if (cm_fakeDirSize != dirSize) {
         if (cm_FakeRootDir)
             free(cm_FakeRootDir);
-        cm_FakeRootDir = malloc(dirSize);
+        cm_FakeRootDir = calloc(dirSize, 1);
         cm_fakeDirSize = dirSize;
     }
 
@@ -274,7 +308,9 @@ void cm_InitFakeRootDir() {
     {       
 
         noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
-        fakeEntry.fid.vnode = htonl(curDirEntry + 2);
+        /* enforce the rule that only directories have odd vnode values */
+        fakeEntry.fid.vnode = htonl((curDirEntry + 1) * 2);
+        fakeEntry.fid.unique = htonl(curDirEntry + 1);
         currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
 
         memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
@@ -316,7 +352,9 @@ void cm_InitFakeRootDir() {
             // add an entry to this page
 
             noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
-            fakeEntry.fid.vnode=htonl(curDirEntry+2);
+            /* enforce the rule that only directories have odd vnode values */
+            fakeEntry.fid.vnode = htonl((curDirEntry + 1) * 2);
+            fakeEntry.fid.unique = htonl(curDirEntry + 1);
             currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
             memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
             strcpy(currentPos + 12, (cm_localMountPoints+curDirEntry)->namep);
@@ -335,8 +373,7 @@ void cm_InitFakeRootDir() {
     }
 
     // we know the fakeDir is setup properly, so we claim that we have callback
-    osi_Log0(afsd_logp,"cm_InitFakeRootDir fakeDirCallback=1");
-    cm_fakeDirCallback=1;
+    osi_Log0(afsd_logp,"cm_InitFakeRootDir completed!");
 
     // when we get here, we've set up everything! done!
 }
@@ -352,11 +389,13 @@ int cm_FakeRootFid(cm_fid_t *fidp)
   
 /* called directly from ioctl */
 /* called while not holding freelance lock */
-int cm_noteLocalMountPointChange(void) {
-    lock_ObtainMutex(&cm_Freelance_Lock);
+int cm_noteLocalMountPointChange(afs_int32 locked) {
+    if (!locked)
+        lock_ObtainMutex(&cm_Freelance_Lock);
     cm_data.fakeDirVersion++;
     cm_localMountPointChangeFlag = 1;
-    lock_ReleaseMutex(&cm_Freelance_Lock);
+    if (!locked)
+        lock_ReleaseMutex(&cm_Freelance_Lock);
     return 1;
 }
 
@@ -371,8 +410,11 @@ int cm_clearLocalMountPointChange() {
 
 int cm_reInitLocalMountPoints() {
     cm_fid_t aFid;
-    int i, hash;
+    unsigned int i, hash;
     cm_scache_t *scp, **lscpp, *tscp;
+    cm_req_t req;
+
+    cm_InitReq(&req);
        
     osi_Log0(afsd_logp,"----- freelance reinitialization starts ----- ");
 
@@ -381,17 +423,19 @@ int cm_reInitLocalMountPoints() {
 
     osi_Log0(afsd_logp,"Invalidating local mount point scp...  ");
 
-
     lock_ObtainWrite(&cm_scacheLock);
     lock_ObtainMutex(&cm_Freelance_Lock);  /* always scache then freelance lock */
-    for (i=1; i<=cm_noLocalMountPoints; i++) {
-        cm_SetFid(&aFid, AFS_FAKE_ROOT_CELL_ID, AFS_FAKE_ROOT_VOL_ID, i, 1);
+    for (i=0; i<=cm_noLocalMountPoints; i++) {
+        if (i == 0)
+            cm_SetFid(&aFid, AFS_FAKE_ROOT_CELL_ID, AFS_FAKE_ROOT_VOL_ID, 1, 1);
+        else
+            cm_SetFid(&aFid, AFS_FAKE_ROOT_CELL_ID, AFS_FAKE_ROOT_VOL_ID, i*2, i);
         hash = CM_SCACHE_HASH(&aFid);
         for (scp=cm_data.scacheHashTablep[hash]; scp; scp=scp->nextp) {
             if (scp != cm_data.rootSCachep && cm_FidCmp(&scp->fid, &aFid) == 0) {
                 // mark the scp to be reused
                 cm_HoldSCacheNoLock(scp);
-                lock_ReleaseWrite(&cm_Freelance_Lock);
+                lock_ReleaseMutex(&cm_Freelance_Lock);
                 lock_ReleaseWrite(&cm_scacheLock);
                 lock_ObtainWrite(&scp->rw);
                 cm_DiscardSCache(scp);
@@ -419,12 +463,17 @@ int cm_reInitLocalMountPoints() {
         }
     }
     lock_ReleaseWrite(&cm_scacheLock);
+    lock_ReleaseMutex(&cm_Freelance_Lock);
     osi_Log0(afsd_logp,"\tall old scp cleared!");
 
+    lock_ObtainWrite(&cm_data.rootSCachep->rw);
+    lock_ObtainMutex(&cm_Freelance_Lock);
     // we must free the memory that was allocated in the prev
     // cm_InitLocalMountPoints call
     osi_Log0(afsd_logp,"Removing old localmountpoints...  ");
     free(cm_localMountPoints);
+    cm_localMountPoints = NULL;
+    cm_noLocalMountPoints = 0;
     osi_Log0(afsd_logp,"\tall old localmountpoints cleared!");
 
     // now re-init the localmountpoints
@@ -439,18 +488,68 @@ int cm_reInitLocalMountPoints() {
 
     lock_ReleaseMutex(&cm_Freelance_Lock);
 
+    cm_GetCallback(cm_data.rootSCachep, cm_rootUserp, &req, 0);
+    lock_ReleaseWrite(&cm_data.rootSCachep->rw);
+
     osi_Log0(afsd_logp,"----- freelance reinit complete -----");
     return 0;
 }
 
+/*
+ * cm_enforceTrailingDot
+ *
+ * return 0 on failure, non-zero on success
+ *
+ */
+static int
+cm_enforceTrailingDot(char * line, size_t cchLine, DWORD *pdwSize)
+{
+    /* trailing white space first. */
+    if (line[(*pdwSize)-1] == '\0') {
+        while (isspace(line[(*pdwSize)-2])) {
+            line[(*pdwSize)-2] = '\0';
+            (*pdwSize)--;
+        }
+    } else {
+        while (isspace(line[(*pdwSize)-1])) {
+            line[(*pdwSize)-1] = '\0';
+            (*pdwSize)--;
+        }
+    }
+
+    /* then enforce the trailing dot requirement */
+    if (line[(*pdwSize)-1] == '\0' && line[(*pdwSize)-2] != '.') {
+        if ((*pdwSize) >= cchLine) {
+            afsi_log("no room for trailing dot");
+            return 0;
+        }
+        line[(*pdwSize)-1] = '.';
+        line[(*pdwSize)] = '\0';
+    } else if (line[(*pdwSize)-1] != '\0' && line[(*pdwSize)-1] != '.') {
+        if ((*pdwSize) >= cchLine) {
+            afsi_log("no room for trailing dot and nul");
+            return 0;
+        }
+        line[(*pdwSize)] = '.';
+        line[(*pdwSize)+1] = '\0';
+    } else if (line[(*pdwSize)-1] != '\0') {
+        if ((*pdwSize) >= cchLine) {
+            afsi_log("no room for trailing nul");
+            return 0;
+        }
+        line[(*pdwSize)] = '\0';
+    }
+    return 1;
+}
+
 
 // yj: open up the registry and read all the local mount 
 // points that are stored there. Part of the initialization
 // process for the freelance client.
-/* to be called while holding freelance lock unless during init. */
+/* to be called while holding freelance lock. */
 long cm_InitLocalMountPoints() {
     FILE *fp;
-    int i;
+    unsigned int i;
     char line[512];
     char*t, *t2;
     cm_localMountPoint_t* aLocalMountPoint;
@@ -484,15 +583,17 @@ long cm_InitLocalMountPoints() {
                          &ftLastWriteTime /* lpftLastWriteTime */
                          );
 
-        smb_UnixTimeFromLargeSearchTime(&FakeFreelanceModTime, &ftLastWriteTime);
+        cm_UnixTimeFromLargeSearchTime(&FakeFreelanceModTime, &ftLastWriteTime);
 
         if ( dwMountPoints == 0 ) {
             rootCellName[0] = '.';
             code = cm_GetRootCellName(&rootCellName[1]);
             if (code == 0) {
-                cm_FreelanceAddMount(&rootCellName[1], &rootCellName[1], "root.cell.", 0, NULL);
-                cm_FreelanceAddMount(rootCellName, &rootCellName[1], "root.cell.", 1, NULL);
-                cm_FreelanceAddMount(".root", &rootCellName[1], "root.afs.", 1, NULL);
+                lock_ReleaseMutex(&cm_Freelance_Lock);
+                cm_FreelanceAddMount(&rootCellName[1], &rootCellName[1], "root.cell", 0, NULL);
+                cm_FreelanceAddMount(rootCellName, &rootCellName[1], "root.cell", 1, NULL);
+                cm_FreelanceAddMount(".root", &rootCellName[1], "root.afs", 1, NULL);
+                lock_ObtainMutex(&cm_Freelance_Lock);
                 dwMountPoints = 3;
             }
         }
@@ -549,12 +650,13 @@ long cm_InitLocalMountPoints() {
                 continue;
             }
 
-            afsi_log("Mountpoint[%d] = %s",dwIndex, line);
+            /* make sure there is a trailing dot and a nul terminator */
+            if (!cm_enforceTrailingDot(line, sizeof(line), &dwSize)) {
+                cm_noLocalMountPoints--;
+                continue;
+            }
 
-            /* find the trailing dot; null terminate after it */
-            t2 = strrchr(line, '.');
-            if (t2)
-                *(t2+1) = '\0';
+            afsi_log("Mountpoint[%d] = %s", dwIndex, line);
 
             for ( t=line;*t;t++ ) {
                 if ( !isprint(*t) ) {
@@ -606,12 +708,13 @@ long cm_InitLocalMountPoints() {
                 continue;
             }
 
-            afsi_log("Symlink[%d] = %s",dwIndex, line);
+            /* make sure there is a trailing dot and a nul terminator */
+            if (!cm_enforceTrailingDot(line, sizeof(line), &dwSize)) {
+                cm_noLocalMountPoints--;
+                continue;
+            }
 
-            /* find the trailing dot; null terminate after it */
-            t2 = strrchr(line, '.');
-            if (t2)
-                *(t2+1) = '\0';
+            afsi_log("Symlink[%d] = %s", dwIndex, line);
 
             for ( t=line;*t;t++ ) {
                 if ( !isprint(*t) ) {
@@ -687,9 +790,11 @@ long cm_InitLocalMountPoints() {
         rootCellName[0] = '.';
        code = cm_GetRootCellName(&rootCellName[1]);
         if (code == 0) {
-            cm_FreelanceAddMount(&rootCellName[1], &rootCellName[1], "root.cell.", 0, NULL);
-            cm_FreelanceAddMount(rootCellName, &rootCellName[1], "root.cell.", 1, NULL);
-            cm_FreelanceAddMount(".root", &rootCellName[1], "root.afs.", 1, NULL);
+            lock_ReleaseMutex(&cm_Freelance_Lock);
+            cm_FreelanceAddMount(&rootCellName[1], &rootCellName[1], "root.cell", 0, NULL);
+            cm_FreelanceAddMount(rootCellName, &rootCellName[1], "root.cell", 1, NULL);
+            cm_FreelanceAddMount(".root", &rootCellName[1], "root.afs", 1, NULL);
+            lock_ObtainMutex(&cm_Freelance_Lock);
         }
         return 0;
     }
@@ -957,6 +1062,7 @@ long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw,
     DWORD dwType, dwSize;
     DWORD dwMountPoints;
     DWORD dwIndex;
+    afs_uint32 code = 0;
 
     /* before adding, verify the cell name; if it is not a valid cell,
        don't add the mount point.
@@ -969,20 +1075,22 @@ long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw,
               rw ? "rw" : "ro");
 
     if ( filename[0] == '\0' || cellname[0] == '\0' || volume[0] == '\0' )
-        return -1;
+        return CM_ERROR_INVAL;
+
+    if ( cm_FreelanceMountPointExists(filename, 0) ||
+         cm_FreelanceSymlinkExists(filename, 0) ) {
+        code = CM_ERROR_EXISTS;
+        goto done;
+    }
 
     if (cellname[0] == '.') {
         if (!cm_GetCell_Gen(&cellname[1], fullname, CM_FLAG_CREATE))
-            return -1;
+            return CM_ERROR_INVAL;
     } else {
         if (!cm_GetCell_Gen(cellname, fullname, CM_FLAG_CREATE))
-            return -1;
+            return CM_ERROR_INVAL;
     }
 
-    if ( cm_FreelanceMountPointExists(filename, 0) ||
-         cm_FreelanceSymlinkExists(filename, 0) )
-        return -1;
-    
     osi_Log1(afsd_logp,"Freelance Adding Mount for Cell: %s", 
               osi_LogSaveString(afsd_logp,cellname));
 
@@ -1009,9 +1117,9 @@ long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw,
                          );
 
         if (rw)
-            sprintf(line, "%s%%%s:%s", filename, fullname, volume);
+            sprintf(line, "%s%%%s:%s.", filename, fullname, volume);
         else
-            sprintf(line, "%s#%s:%s", filename, fullname, volume);
+            sprintf(line, "%s#%s:%s.", filename, fullname, volume);
 
         /* If we are adding a new value, there must be an unused name
          * within the range 0 to dwMountPoints 
@@ -1055,18 +1163,44 @@ long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw,
         fprintf(fp, "%d", n);
         fseek(fp, 0, SEEK_END);
         if (rw)
-            fprintf(fp, "%s%%%s:%s\n", filename, fullname, volume);
+            fprintf(fp, "%s%%%s:%s.\n", filename, fullname, volume);
         else
-            fprintf(fp, "%s#%s:%s\n", filename, fullname, volume);
+            fprintf(fp, "%s#%s:%s.\n", filename, fullname, volume);
         fclose(fp);
     }
+
+    /* Do this while we are holding the lock */
+    cm_noteLocalMountPointChange(TRUE);
     lock_ReleaseMutex(&cm_Freelance_Lock);
 
-    /* cm_reInitLocalMountPoints(); */
-    if (fidp)
-        cm_SetFid(fidp, fidp->cell, fidp->volume, ++cm_noLocalMountPoints, 1);
-    cm_noteLocalMountPointChange();
-    return 0;
+  done:
+    if (fidp) {
+        cm_req_t req;
+        cm_scache_t *scp;
+        clientchar_t *cpath;
+
+        cm_InitReq(&req);
+
+        cpath = cm_FsStringToClientStringAlloc(filename, -1, NULL);        
+        if (!cpath)
+            return CM_ERROR_NOSUCHPATH;
+
+        if (cm_getLocalMountPointChange()) {   // check for changes
+            cm_clearLocalMountPointChange();    // clear the changefile
+            cm_reInitLocalMountPoints();       // start reinit
+        }
+
+        code = cm_NameI(cm_RootSCachep(cm_rootUserp, &req), cpath,
+                        CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD | CM_FLAG_DFS_REFERRAL,
+                        cm_rootUserp, NULL, &req, &scp);
+        free(cpath);
+        if (code)
+            return code;
+        *fidp = scp->fid;
+        cm_ReleaseSCache(scp);
+    }
+    
+    return code;
 }
 
 long cm_FreelanceRemoveMount(char *toremove)
@@ -1167,26 +1301,28 @@ long cm_FreelanceRemoveMount(char *toremove)
             rename(hfile2, hfile);
         }
     }
-    
-    lock_ReleaseMutex(&cm_Freelance_Lock);
+
     if (found) {
-        cm_noteLocalMountPointChange();
-        return 0;
-    } else
-        return CM_ERROR_NOSUCHFILE;
+        /* Do this while we are holding the lock */
+        cm_noteLocalMountPointChange(TRUE);
+    }
+    lock_ReleaseMutex(&cm_Freelance_Lock);
+    return (found ? 0 : CM_ERROR_NOSUCHFILE);
 }
 
 long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
 {
     char line[512];
-    char fullname[CELL_MAXNAMELEN];
+    char fullname[CELL_MAXNAMELEN] = "";
     int alias = 0;
     HKEY hkFreelanceSymlinks = 0;
     DWORD dwType, dwSize;
     DWORD dwSymlinks;
     DWORD dwIndex;
+    afs_uint32 code = 0;
 
-    /* before adding, verify the filename.  If it is already in use, either as 
+    /*
+     * before adding, verify the filename.  If it is already in use, either as
      * as mount point or a cellname, do not permit the creation of the symlink.
      */
     osi_Log2(afsd_logp,"Freelance Add Symlink request: filename=%s destination=%s",
@@ -1196,21 +1332,30 @@ long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
     if ( filename[0] == '\0' || destination[0] == '\0' )
         return CM_ERROR_INVAL;
 
-    fullname[0] = '\0';
+    /* Do not create the symlink if the name ends in a dot */
+    if ( filename[strlen(filename)-1] == '.')
+        return CM_ERROR_INVAL;
+
+    if ( cm_FreelanceMountPointExists(filename, 0) ||
+         cm_FreelanceSymlinkExists(filename, 0) ) {
+        code = CM_ERROR_EXISTS;
+        goto done;
+    }
+
     if (filename[0] == '.') {
         cm_GetCell_Gen(&filename[1], fullname, CM_FLAG_CREATE);
-        if (cm_stricmp_utf8(&filename[1],fullname) == 0)
-            return CM_ERROR_EXISTS;
+        if (cm_stricmp_utf8(&filename[1],fullname) == 0) {
+            code = CM_ERROR_EXISTS;
+            goto done;
+        }
     } else {
         cm_GetCell_Gen(filename, fullname, CM_FLAG_CREATE);
-        if (cm_stricmp_utf8(filename,fullname) == 0)
-            return CM_ERROR_EXISTS;
+        if (cm_stricmp_utf8(filename,fullname) == 0) {
+            code = CM_ERROR_EXISTS;
+            goto done;
+        }
     }
 
-    if ( cm_FreelanceMountPointExists(filename, 0) ||
-         cm_FreelanceSymlinkExists(filename, 0) )
-        return CM_ERROR_EXISTS;
-
     lock_ObtainMutex(&cm_Freelance_Lock);
 
     if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
@@ -1267,13 +1412,40 @@ long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
         }
         RegCloseKey(hkFreelanceSymlinks);
     } 
+
+    /* Do this while we are holding the lock */
+    cm_noteLocalMountPointChange(TRUE);
     lock_ReleaseMutex(&cm_Freelance_Lock);
 
-    /* cm_reInitLocalMountPoints(); */
-    if (fidp)
-        cm_SetFid(fidp, fidp->cell, fidp->volume, ++cm_noLocalMountPoints, 1);
-    cm_noteLocalMountPointChange();
-    return 0;
+  done:
+    if (fidp) {
+        cm_req_t req;
+        cm_scache_t *scp;
+        clientchar_t *cpath;
+
+        cm_InitReq(&req);
+
+        cpath = cm_FsStringToClientStringAlloc(filename, -1, NULL);        
+        if (!cpath) {
+            code = CM_ERROR_NOSUCHPATH;
+        } else {
+            if (cm_getLocalMountPointChange()) {       // check for changes
+                cm_clearLocalMountPointChange();    // clear the changefile
+                cm_reInitLocalMountPoints();   // start reinit
+            }
+
+            code = cm_NameI(cm_RootSCachep(cm_rootUserp, &req), cpath,
+                             CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD | CM_FLAG_DFS_REFERRAL,
+                             cm_rootUserp, NULL, &req, &scp);
+            free(cpath);
+            if (code == 0) {
+                *fidp = scp->fid;
+                cm_ReleaseSCache(scp);
+            }
+        }
+    }
+
+    return code;
 }
 
 long cm_FreelanceRemoveSymlink(char *toremove)
@@ -1329,11 +1501,50 @@ long cm_FreelanceRemoveSymlink(char *toremove)
         RegCloseKey(hkFreelanceSymlinks);
     }
     
-    lock_ReleaseMutex(&cm_Freelance_Lock);
     if (found) {
-        cm_noteLocalMountPointChange();
-        return 0;
-    } else
-        return CM_ERROR_NOSUCHFILE;
+        /* Do this while we are holding the lock */
+        cm_noteLocalMountPointChange(TRUE);
+    }
+    lock_ReleaseMutex(&cm_Freelance_Lock);
+    return (found ? 0 : CM_ERROR_NOSUCHFILE);
+}
+
+long
+cm_FreelanceFetchMountPointString(cm_scache_t *scp)
+{
+    lock_ObtainMutex(&cm_Freelance_Lock);
+    if (!scp->mountPointStringp[0] && 
+        scp->fid.cell == AFS_FAKE_ROOT_CELL_ID &&
+        scp->fid.volume == AFS_FAKE_ROOT_VOL_ID && 
+        scp->fid.unique <= cm_noLocalMountPoints) {
+        strncpy(scp->mountPointStringp, cm_localMountPoints[scp->fid.unique-1].mountPointStringp, MOUNTPOINTLEN);
+        scp->mountPointStringp[MOUNTPOINTLEN-1] = 0;   /* null terminate */
+    }
+    lock_ReleaseMutex(&cm_Freelance_Lock);
+
+    return 0;
+}
+
+long 
+cm_FreelanceFetchFileType(cm_scache_t *scp)
+{
+    lock_ObtainMutex(&cm_Freelance_Lock);
+    if (scp->fid.cell == AFS_FAKE_ROOT_CELL_ID &&
+        scp->fid.volume == AFS_FAKE_ROOT_VOL_ID && 
+        scp->fid.unique <= cm_noLocalMountPoints) 
+    {
+        scp->fileType = cm_localMountPoints[scp->fid.unique-1].fileType;
+    
+        if ( scp->fileType == CM_SCACHETYPE_SYMLINK &&
+             !strnicmp(cm_localMountPoints[scp->fid.unique-1].mountPointStringp, "msdfs:", strlen("msdfs:")) )
+        {
+            scp->fileType = CM_SCACHETYPE_DFSLINK;
+        } 
+    } else {
+        scp->fileType = CM_SCACHETYPE_INVALID;
+    }
+    lock_ReleaseMutex(&cm_Freelance_Lock);
+
+    return 0;
 }
 #endif /* AFS_FREELANCE_CLIENT */