Windows: Update root.afs scache dv when Freelance dir bufs are regenerated
[openafs.git] / src / WINNT / afsd / cm_freelance.c
index e6b0a85..2868878 100644 (file)
@@ -1,46 +1,49 @@
 #include <afs/param.h>
 #include <afs/stds.h>
 
-#ifndef DJGPP
 #include <windows.h>
 #include <winreg.h>
 #include <winsock2.h>
-#else
-#include <netdb.h>
-#endif /* !DJGPP */
 #include <stdlib.h>
 #include <malloc.h>
 #include <string.h>
+#include <cm_nls.h>
 
+#include <WINNT/afsreg.h>
+#include "afsd.h"
 #include <rx/rx.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;
-int cm_fakeDirSize;
+static unsigned int cm_noLocalMountPoints = 0;
+char * cm_FakeRootDir = NULL;
+int cm_fakeDirSize = 0;
 int cm_fakeDirCallback=0;
 int cm_fakeGettingCallback=0;
-int cm_fakeDirVersion = 0x8;
-cm_localMountPoint_t* cm_localMountPoints;
+static cm_localMountPoint_t* cm_localMountPoints;
 osi_mutex_t cm_Freelance_Lock;
-int cm_localMountPointChangeFlag = 0;
-int cm_freelanceEnabled = 0;
+static int cm_localMountPointChangeFlag = 0;
+int cm_freelanceEnabled = 1;
+int cm_freelanceImportCellServDB = 0;
 time_t FakeFreelanceModTime = 0x3b49f6e2;
 
+static int freelance_ShutdownFlag = 0;
+static HANDLE hFreelanceChangeEvent = 0;
+static HANDLE hFreelanceSymlinkChangeEvent = 0;
+
 void cm_InitFakeRootDir();
 
-#if !defined(DJGPP)
 void cm_FreelanceChangeNotifier(void * parmp) {
-    HANDLE hFreelanceChangeEvent = 0;
     HKEY   hkFreelance = 0;
 
     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
-                      "SOFTWARE\\OpenAFS\\Client\\Freelance",
+                      AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
                       0,
                       KEY_NOTIFY,
                       &hkFreelance) == ERROR_SUCCESS) {
@@ -64,22 +67,28 @@ void cm_FreelanceChangeNotifier(void * parmp) {
                                      ) != ERROR_SUCCESS) {
             RegCloseKey(hkFreelance);
             CloseHandle(hFreelanceChangeEvent);
+            hFreelanceChangeEvent = 0;
             return;
         }
 
         if (WaitForSingleObject(hFreelanceChangeEvent, INFINITE) == WAIT_OBJECT_0)
         {
-            cm_noteLocalMountPointChange();
+            if (freelance_ShutdownFlag == 1) {     
+                RegCloseKey(hkFreelance);          
+                CloseHandle(hFreelanceChangeEvent);
+                hFreelanceChangeEvent = 0;         
+                return;                            
+            }                                      
+            cm_noteLocalMountPointChange(FALSE);
         }
     }
 }
 
 void cm_FreelanceSymlinkChangeNotifier(void * parmp) {
-    HANDLE hFreelanceSymlinkChangeEvent = 0;
     HKEY   hkFreelance = 0;
 
     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
-                      "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
+                      AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
                       0,
                       KEY_NOTIFY,
                       &hkFreelance) == ERROR_SUCCESS) {
@@ -103,50 +112,87 @@ void cm_FreelanceSymlinkChangeNotifier(void * parmp) {
                                      ) != ERROR_SUCCESS) {
             RegCloseKey(hkFreelance);
             CloseHandle(hFreelanceSymlinkChangeEvent);
+            hFreelanceSymlinkChangeEvent = 0;
             return;
         }
 
         if (WaitForSingleObject(hFreelanceSymlinkChangeEvent, INFINITE) == WAIT_OBJECT_0)
         {
-            cm_noteLocalMountPointChange();
+            if (freelance_ShutdownFlag == 1) {     
+                RegCloseKey(hkFreelance);          
+                CloseHandle(hFreelanceSymlinkChangeEvent);
+                hFreelanceSymlinkChangeEvent = 0;         
+                return;                            
+            }                                      
+            cm_noteLocalMountPointChange(FALSE);
         }
     }
 }
-#endif
+
+void                                          
+cm_FreelanceShutdown(void)                    
+{                                             
+    freelance_ShutdownFlag = 1;               
+    if (hFreelanceChangeEvent != 0)           
+        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() {
-#if !defined(DJGPP)
     thread_t phandle;
     int lpid;
-#endif
 
-    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 an ini file
+    // to read all the local mount points from the registry
     cm_InitLocalMountPoints();
 
     // then we make a call to InitFakeRootDir to create
     // a fake root directory based on the local mount points
     cm_InitFakeRootDir();
     // --- end of yj code
+    lock_ReleaseMutex(&cm_Freelance_Lock);
 
-#if !defined(DJGPP)
     /* Start the registry monitor */
     phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceChangeNotifier,
                           NULL, 0, &lpid, "cm_FreelanceChangeNotifier");
-    osi_assert(phandle != NULL);
+    osi_assertx(phandle != NULL, "cm_FreelanceChangeNotifier thread create failure");
     thrd_CloseHandle(phandle);
 
     phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceSymlinkChangeNotifier,
                           NULL, 0, &lpid, "cm_FreelanceSymlinkChangeNotifier");
-    osi_assert(phandle != NULL);
+    osi_assertx(phandle != NULL, "cm_FreelanceSymlinkChangeNotifier thread create failure");
     thrd_CloseHandle(phandle);
-#endif
 }
 
 /* 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;
@@ -175,7 +221,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;
@@ -183,7 +229,7 @@ void cm_InitFakeRootDir() {
     /* Reserve 2 directory chunks for "." and ".." */
     curChunk += 2;
 
-    while (curDirEntry!=cm_noLocalMountPoints) {
+    while (curDirEntry<cm_noLocalMountPoints) {
         sizeOfCurEntry = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
         if ((curChunk + sizeOfCurEntry >= CPP) ||
              (curDirEntryInPage + 1 >= CM_DIR_EPP)) {
@@ -197,8 +243,12 @@ void cm_InitFakeRootDir() {
     }
 
     dirSize = (curPage+1) *  CM_DIR_PAGESIZE;
-    cm_FakeRootDir = malloc(dirSize);
-    cm_fakeDirSize = dirSize;
+    if (cm_fakeDirSize != dirSize) {
+        if (cm_FakeRootDir)
+            free(cm_FakeRootDir);
+        cm_FakeRootDir = calloc(dirSize, 1);
+        cm_fakeDirSize = dirSize;
+    }
 
     // yj: when we get here, we've figured out how much memory we need and 
     // allocated the appropriate space for it. we now prceed to fill
@@ -232,7 +282,6 @@ void cm_InitFakeRootDir() {
     curChunk = 13;
 
     // stick the first 2 entries "." and ".." in
-    fakeEntry.fid.unique = htonl(1);
     fakeEntry.fid.vnode = htonl(1);
     strcpy(fakeEntry.name, ".");
     currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
@@ -248,13 +297,15 @@ void cm_InitFakeRootDir() {
     // 2. we have less than CM_DIR_EPP entries in page 0
     // 3. we're not out of chunks in page 0
 
-    while( (curDirEntry!=cm_noLocalMountPoints) && 
+    while( (curDirEntry<cm_noLocalMountPoints) && 
            (curDirEntryInPage < CM_DIR_EPP) &&
            (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP)) 
     {       
 
         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);
@@ -277,7 +328,7 @@ void cm_InitFakeRootDir() {
     curPage++;
 
     // ok, page 0's done. Move on to the next page.
-    while (curDirEntry!=cm_noLocalMountPoints) {
+    while (curDirEntry<cm_noLocalMountPoints) {
         // setup a new page
         curChunk = 1;                  // the zeroth chunk is reserved for page header
         curDirEntryInPage = 0; 
@@ -289,14 +340,16 @@ void cm_InitFakeRootDir() {
         fakePageHeader.tag = htons(1234);
 
         // while we're on the same page...
-        while ( (curDirEntry!=cm_noLocalMountPoints) &&
+        while ( (curDirEntry<cm_noLocalMountPoints) &&
                 (curDirEntryInPage < CM_DIR_EPP) &&
                 (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP))
         {
             // 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);
@@ -323,20 +376,22 @@ void cm_InitFakeRootDir() {
 
 int cm_FakeRootFid(cm_fid_t *fidp)
 {
-    fidp->cell = AFS_FAKE_ROOT_CELL_ID;            /* root cell */
-    fidp->volume = AFS_FAKE_ROOT_VOL_ID;   /* root.afs ? */
-    fidp->vnode = 0x1;
-    fidp->unique = 0x1;
+    cm_SetFid(fidp, 
+              AFS_FAKE_ROOT_CELL_ID,            /* root cell */
+              AFS_FAKE_ROOT_VOL_ID,            /* root.afs ? */
+              1, 1);
     return 0;
 }
   
 /* called directly from ioctl */
 /* called while not holding freelance lock */
-int cm_noteLocalMountPointChange() {
-    lock_ObtainMutex(&cm_Freelance_Lock);
-    cm_fakeDirVersion++;
+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;
 }
 
@@ -351,8 +406,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 ----- ");
 
@@ -361,48 +419,57 @@ int cm_reInitLocalMountPoints() {
 
     osi_Log0(afsd_logp,"Invalidating local mount point scp...  ");
 
-    aFid.cell = AFS_FAKE_ROOT_CELL_ID;
-    aFid.volume=AFS_FAKE_ROOT_VOL_ID;
-    aFid.unique=0x1;
-    aFid.vnode=0x2;
-
     lock_ObtainWrite(&cm_scacheLock);
     lock_ObtainMutex(&cm_Freelance_Lock);  /* always scache then freelance lock */
-    for (i=0; i<cm_noLocalMountPoints; i++) {
+    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_hashTablep[hash]; scp; scp=scp->nextp) {
-            if (scp->fid.volume == aFid.volume &&
-                 scp->fid.vnode == aFid.vnode &&
-                 scp->fid.unique == aFid.unique 
-                 ) {
-
+        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_ReleaseMutex(&cm_Freelance_Lock);
                 lock_ReleaseWrite(&cm_scacheLock);
-                lock_ObtainMutex(&scp->mx);
+                lock_ObtainWrite(&scp->rw);
                 cm_DiscardSCache(scp);
-                lock_ReleaseMutex(&scp->mx);
-                cm_CallbackNotifyChange(scp);
-                lock_ObtainWrite(&cm_scacheLock);
-                scp->refCount--;
 
                 // take the scp out of the hash
-                lscpp = &cm_hashTablep[hash];
-                for (tscp=*lscpp; tscp; lscpp = &tscp->nextp, tscp = *lscpp) {
-                    if (tscp == scp) break;
+                lock_ObtainWrite(&cm_scacheLock);
+                for (lscpp = &cm_data.scacheHashTablep[hash], tscp = cm_data.scacheHashTablep[hash]; 
+                     tscp; 
+                     lscpp = &tscp->nextp, tscp = tscp->nextp) {
+                    if (tscp == scp) {
+                        *lscpp = scp->nextp;
+                        scp->nextp = NULL;
+                        scp->flags &= ~CM_SCACHEFLAG_INHASH;
+                        break;
+                    }
                 }
-                *lscpp = scp->nextp;
-                scp->flags &= ~CM_SCACHEFLAG_INHASH;
+
+                lock_ReleaseWrite(&scp->rw);
+                lock_ReleaseWrite(&cm_scacheLock);
+                cm_CallbackNotifyChange(scp);
+                lock_ObtainWrite(&cm_scacheLock);
+                cm_ReleaseSCacheNoLock(scp);
+                lock_ObtainMutex(&cm_Freelance_Lock);
             }
         }
-        aFid.vnode = aFid.vnode + 1;
     }
     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
@@ -410,11 +477,6 @@ int cm_reInitLocalMountPoints() {
     cm_InitLocalMountPoints();
     osi_Log0(afsd_logp,"\tcreated new set of localmountpoints!");
 
-    // now we have to free the memory allocated in cm_initfakerootdir
-    osi_Log0(afsd_logp,"Removing old fakedir...  ");
-    free(cm_FakeRootDir);
-    osi_Log0(afsd_logp,"\t\told fakedir removed!");
-
     // then we re-create that dir
     osi_Log0(afsd_logp,"Creating new fakedir...  ");
     cm_InitFakeRootDir();
@@ -422,39 +484,39 @@ 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;
 }
 
 
-// yj: open up the ini file and read all the local mount 
+// 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;
-    char hdir[120];
+    char hdir[260];
     long code;
     char rootCellName[256];
-#if !defined(DJGPP)
     HKEY hkFreelance = 0, hkFreelanceSymlinks = 0;
     DWORD dwType, dwSize;
     DWORD dwMountPoints = 0;
     DWORD dwIndex;
     DWORD dwSymlinks = 0;
     FILETIME ftLastWriteTime;
-#endif
 
-#if !defined(DJGPP)
     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
-                        "SOFTWARE\\OpenAFS\\Client\\Freelance",
-                                               0,
-                        KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
-                        &hkFreelance) == ERROR_SUCCESS) {
+                      AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
+                      0,
+                      KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
+                      &hkFreelance) == ERROR_SUCCESS) {
 
         RegQueryInfoKey( hkFreelance,
                          NULL,  /* lpClass */
@@ -470,21 +532,23 @@ long cm_InitLocalMountPoints() {
                          &ftLastWriteTime /* lpftLastWriteTime */
                          );
 
-        smb_UnixTimeFromLargeSearchTime(&FakeFreelanceModTime, &ftLastWriteTime);
+        cm_UnixTimeFromLargeSearchTime(&FakeFreelanceModTime, &ftLastWriteTime);
 
         if ( dwMountPoints == 0 ) {
-            sprintf(line,"%s#%s:root.cell.\n",rootCellName,rootCellName);
-            dwType = REG_SZ;
-            dwSize = strlen(line) + 1;
-            RegSetValueEx( hkFreelance, "0", 0, dwType, line, dwSize);
-            sprintf(line,".%s%%%s:root.cell.\n",rootCellName,rootCellName);
-            dwSize = strlen(line) + 1;
-            RegSetValueEx( hkFreelance, "1", 0, dwType, line, dwSize);
-            dwMountPoints = 2;
+            rootCellName[0] = '.';
+            code = cm_GetRootCellName(&rootCellName[1]);
+            if (code == 0) {
+                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;
+            }
         }
 
         if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
-                          "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
+                          AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
                           0,
                           NULL,
                           REG_OPTION_NON_VOLATILE,
@@ -527,22 +591,38 @@ long cm_InitLocalMountPoints() {
             TCHAR szValueName[16];
             DWORD dwValueSize = 16;
             dwSize = sizeof(line);
-            RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
-                          &dwType, line, &dwSize);
+            if (RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
+                          &dwType, line, &dwSize))
+            {
+                afsi_log("RegEnumValue(hkFreelance) failed");
+                cm_noLocalMountPoints--;
+                continue;
+            }
+
+            afsi_log("Mountpoint[%d] = %s",dwIndex, line);
 
             /* find the trailing dot; null terminate after it */
             t2 = strrchr(line, '.');
             if (t2)
                 *(t2+1) = '\0';
 
+            for ( t=line;*t;t++ ) {
+                if ( !isprint(*t) ) {
+                    afsi_log("error occurred while parsing mountpoint entry [%d]: non-printable character", dwIndex);
+                    fprintf(stderr, "error occurred while parsing mountpoint entry [%d]: non-printable character", dwIndex);
+                    cm_noLocalMountPoints--;
+                    continue;
+                }
+            }
+
             // line is not empty, so let's parse it
             t = strchr(line, '#');
             if (!t)
                 t = strchr(line, '%');
             // make sure that there is a '#' or '%' separator in the line
             if (!t) {
-                afsi_log("error occurred while parsing entry in %s: no # or %% separator in line %d", AFS_FREELANCE_INI, dwIndex);
-                fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: no # or %% separator in line %d", dwIndex);
+                afsi_log("error occurred while parsing mountpoint entry [%d]: no # or %% separator", dwIndex);
+                fprintf(stderr, "error occurred while parsing mountpoint entry [%d]: no # or %% separator", dwIndex);
                 cm_noLocalMountPoints--;
                 continue;
             }
@@ -568,21 +648,37 @@ long cm_InitLocalMountPoints() {
             TCHAR szValueName[16];
             DWORD dwValueSize = 16;
             dwSize = sizeof(line);
-            RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
-                          &dwType, line, &dwSize);
+            if (RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
+                              &dwType, line, &dwSize))
+            {
+                afsi_log("RegEnumValue(hkFreelanceSymlinks) failed");
+                cm_noLocalMountPoints--;
+                continue;
+            }
+
+            afsi_log("Symlink[%d] = %s",dwIndex, line);
 
             /* find the trailing dot; null terminate after it */
             t2 = strrchr(line, '.');
             if (t2)
                 *(t2+1) = '\0';
 
+            for ( t=line;*t;t++ ) {
+                if ( !isprint(*t) ) {
+                    afsi_log("error occurred while parsing symlink entry [%d]: non-printable character", dwIndex);
+                    fprintf(stderr, "error occurred while parsing symlink entry [%d]: non-printable character", dwIndex);
+                    cm_noLocalMountPoints--;
+                    continue;
+                }
+            }
+
             // line is not empty, so let's parse it
             t = strchr(line, ':');
 
             // make sure that there is a ':' separator in the line
             if (!t) {
-                afsi_log("error occurred while parsing symlink entry: no ':' separator in line %d", dwIndex);
-                fprintf(stderr, "error occurred while parsing symlink entry: no ':' separator in line %d", dwIndex);
+                afsi_log("error occurred while parsing symlink entry [%d]: no ':' separator", dwIndex);
+                fprintf(stderr, "error occurred while parsing symlink entry [%d]: no ':' separator", dwIndex);
                 cm_noLocalMountPoints--;
                 continue;
             }
@@ -609,39 +705,24 @@ long cm_InitLocalMountPoints() {
         RegCloseKey(hkFreelance);
         return 0;
     }
-#endif
 
     /* What follows is the old code to read freelance mount points 
      * out of a text file modified to copy the data into the registry
      */
-    cm_GetConfigDir(hdir);
+    cm_GetConfigDir(hdir, sizeof(hdir));
     strcat(hdir, AFS_FREELANCE_INI);
     // open the ini file for reading
     fp = fopen(hdir, "r");
-
-    // if we fail to open the file, create an empty one
     if (!fp) {
-        fp = fopen(hdir, "w");
-       code = cm_GetRootCellName(rootCellName);
-        if (code == 0) {
-            fputs("1\n", fp);
-            fprintf(fp,"%s#%s:root.cell.\n",rootCellName,rootCellName);
-            fprintf(fp,".%s%%%s:root.cell.\n",rootCellName,rootCellName);
-            fclose(fp);
-            fp = fopen(hdir, "r");
-        } else {
-            fputs("0\n", fp);
-            fclose(fp);
-            return 0;  /* success */
-        }
+        /* look in the Windows directory where we used to store the file */
+        GetWindowsDirectory(hdir, sizeof(hdir));
+        strcat(hdir,"\\");
+        strcat(hdir, AFS_FREELANCE_INI);
+        fp = fopen(hdir, "r");
     }
 
-    // we successfully opened the file
-    osi_Log0(afsd_logp,"opened afs_freelance.ini");
-       
-#if !defined(DJGPP)
     RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
-                    "SOFTWARE\\OpenAFS\\Client\\Freelance",
+                    AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
                     0,
                     NULL,
                     REG_OPTION_NON_VOLATILE,
@@ -650,8 +731,24 @@ long cm_InitLocalMountPoints() {
                     &hkFreelance,
                     NULL);
     dwIndex = 0;
-#endif
 
+    if (!fp) {
+        RegCloseKey(hkFreelance);
+        rootCellName[0] = '.';
+       code = cm_GetRootCellName(&rootCellName[1]);
+        if (code == 0) {
+            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;
+    }
+
+    // we successfully opened the file
+    osi_Log0(afsd_logp,"opened afs_freelance.ini");
+       
     // now we read the first line to see how many entries
     // there are
     fgets(line, sizeof(line), fp);
@@ -668,9 +765,11 @@ long cm_InitLocalMountPoints() {
     // that we read
     cm_noLocalMountPoints = atoi(line);
 
-    // create space to store the local mount points
-    cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
-    aLocalMountPoint = cm_localMountPoints;
+    if (cm_noLocalMountPoints > 0) {
+        // create space to store the local mount points
+        cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
+        aLocalMountPoint = cm_localMountPoints;
+    }
 
     // now we read n lines and parse them into local mount points
     // where n is the number of local mount points there are, as
@@ -688,16 +787,19 @@ long cm_InitLocalMountPoints() {
             return -1;
         }
 
-#if !defined(DJGPP)
+        /* find the trailing dot; null terminate after it */
+        t2 = strrchr(line, '.');
+        if (t2)
+            *(t2+1) = '\0';
+
         if ( hkFreelance ) {
             char szIndex[16];
             /* we are migrating to the registry */
             sprintf(szIndex,"%d",dwIndex++);
             dwType = REG_SZ;
-            dwSize = strlen(line) + 1;
+            dwSize = (DWORD)strlen(line) + 1;
             RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
         }
-#endif 
 
         // line is not empty, so let's parse it
         t = strchr(line, '#');
@@ -714,8 +816,8 @@ long cm_InitLocalMountPoints() {
         *(aLocalMountPoint->namep + (t-line)) = 0;
 
         aLocalMountPoint->mountPointStringp=malloc(strlen(line) - (t-line) + 1);
-        memcpy(aLocalMountPoint->mountPointStringp, t, strlen(line)-(t-line)-2);
-        *(aLocalMountPoint->mountPointStringp + (strlen(line)-(t-line)-2)) = 0;
+        memcpy(aLocalMountPoint->mountPointStringp, t, strlen(line)-(t-line)-1);
+        *(aLocalMountPoint->mountPointStringp + (strlen(line)-(t-line)-1)) = 0;
 
         osi_Log2(afsd_logp,"found mount point: name %s, string %s",
                   aLocalMountPoint->namep,
@@ -724,12 +826,10 @@ long cm_InitLocalMountPoints() {
         aLocalMountPoint++;
     }
     fclose(fp);
-#if !defined(DJGPP)
     if ( hkFreelance ) {
         RegCloseKey(hkFreelance);
         DeleteFile(hdir);
     }
-#endif
     return 0;
 }
 
@@ -737,20 +837,179 @@ int cm_getNoLocalMountPoints() {
     return cm_noLocalMountPoints;
 }
 
+long cm_FreelanceMountPointExists(char * filename, int prefix_ok)
+{
+    char* cp;
+    char line[512];
+    char shortname[200];
+    int found = 0;
+    HKEY hkFreelance = 0;
+    DWORD dwType, dwSize;
+    DWORD dwMountPoints;
+    DWORD dwIndex;
+        
+    lock_ObtainMutex(&cm_Freelance_Lock);
+
+    if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
+                      AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
+                      0,
+                      KEY_READ|KEY_QUERY_VALUE,
+                      &hkFreelance) == ERROR_SUCCESS) 
+    {
+        RegQueryInfoKey( hkFreelance,
+                         NULL,  /* lpClass */
+                         NULL,  /* lpcClass */
+                         NULL,  /* lpReserved */
+                         NULL,  /* lpcSubKeys */
+                         NULL,  /* lpcMaxSubKeyLen */
+                         NULL,  /* lpcMaxClassLen */
+                         &dwMountPoints, /* lpcValues */
+                         NULL,  /* lpcMaxValueNameLen */
+                         NULL,  /* lpcMaxValueLen */
+                         NULL,  /* lpcbSecurityDescriptor */
+                         NULL   /* lpftLastWriteTime */
+                         );
+
+        for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
+            TCHAR szValueName[16];
+            DWORD dwValueSize = 16;
+            dwSize = sizeof(line);
+            RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
+                          &dwType, line, &dwSize);
+
+            cp=strchr(line, '#');
+            if (!cp)
+                cp=strchr(line, '%');
+            memcpy(shortname, line, cp-line);
+            shortname[cp-line]=0;
+
+            if (!strcmp(shortname, filename)) {
+                found = 1;
+                break;
+            }
+        }
+        for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
+            TCHAR szValueName[16];
+            DWORD dwValueSize = 16;
+            dwSize = sizeof(line);
+            RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
+                          &dwType, line, &dwSize);
+
+            cp=strchr(line, '#');
+            if (!cp)
+                cp=strchr(line, '%');
+            memcpy(shortname, line, cp-line);
+            shortname[cp-line]=0;
+
+            if (!cm_stricmp_utf8(shortname, filename)) {
+                found = 1;
+                break;
+            }
+
+            if (prefix_ok && strlen(shortname) - strlen(filename) == 1 && !strncmp(shortname, filename, strlen(filename))) {
+                found = 1;
+                break;
+            }
+        }
+        RegCloseKey(hkFreelance);
+    }
+
+    lock_ReleaseMutex(&cm_Freelance_Lock);
+
+    return found;
+}
+
+long cm_FreelanceSymlinkExists(char * filename, int prefix_ok)
+{
+    char* cp;
+    char line[512];
+    char shortname[200];
+    int found = 0;
+    HKEY hkFreelance = 0;
+    DWORD dwType, dwSize;
+    DWORD dwSymlinks;
+    DWORD dwIndex;
+        
+    lock_ObtainMutex(&cm_Freelance_Lock);
+
+    if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
+                      AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
+                      0,
+                      KEY_READ|KEY_QUERY_VALUE,
+                      &hkFreelance) == ERROR_SUCCESS) 
+    {
+        RegQueryInfoKey( hkFreelance,
+                         NULL,  /* lpClass */
+                         NULL,  /* lpcClass */
+                         NULL,  /* lpReserved */
+                         NULL,  /* lpcSubKeys */
+                         NULL,  /* lpcMaxSubKeyLen */
+                         NULL,  /* lpcMaxClassLen */
+                         &dwSymlinks, /* lpcValues */
+                         NULL,  /* lpcMaxValueNameLen */
+                         NULL,  /* lpcMaxValueLen */
+                         NULL,  /* lpcbSecurityDescriptor */
+                         NULL   /* lpftLastWriteTime */
+                         );
+
+        for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
+            TCHAR szValueName[16];
+            DWORD dwValueSize = 16;
+            dwSize = sizeof(line);
+            RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
+                          &dwType, line, &dwSize);
+
+            cp=strchr(line, ':');
+            memcpy(shortname, line, cp-line);
+            shortname[cp-line]=0;
+
+            if (!strcmp(shortname, filename)) {
+                found = 1;
+                break;
+            }
+
+            if (prefix_ok && strlen(shortname) - strlen(filename) == 1 && !strncmp(shortname, filename, strlen(filename))) {
+                found = 1;
+                break;
+            }
+        }
+        for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
+            TCHAR szValueName[16];
+            DWORD dwValueSize = 16;
+            dwSize = sizeof(line);
+            RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
+                          &dwType, line, &dwSize);
+
+            cp=strchr(line, ':');
+            memcpy(shortname, line, cp-line);
+            shortname[cp-line]=0;
+
+            if (!cm_stricmp_utf8(shortname, filename)) {
+                found = 1;
+                break;
+            }
+        }
+        RegCloseKey(hkFreelance);
+    }
+
+    lock_ReleaseMutex(&cm_Freelance_Lock);
+
+    return found;
+}
+
 long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp)
 {
     FILE *fp;
-    char hfile[120];
+    char hfile[260];
     char line[512];
-    char fullname[200];
+    char fullname[CELL_MAXNAMELEN];
     int n;
     int alias = 0;
-#if !defined(DJGPP)
     HKEY hkFreelance = 0;
     DWORD dwType, dwSize;
     DWORD dwMountPoints;
     DWORD dwIndex;
-#endif
+    afs_uint32 code = 0;
 
     /* before adding, verify the cell name; if it is not a valid cell,
        don't add the mount point.
@@ -761,22 +1020,31 @@ long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw,
               osi_LogSaveString(afsd_logp,cellname), 
               osi_LogSaveString(afsd_logp,volume), 
               rw ? "rw" : "ro");
+
+    if ( filename[0] == '\0' || cellname[0] == '\0' || volume[0] == '\0' )
+        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;
     }
-    
+
     osi_Log1(afsd_logp,"Freelance Adding Mount for Cell: %s", 
               osi_LogSaveString(afsd_logp,cellname));
 
     lock_ObtainMutex(&cm_Freelance_Lock);
 
-#if !defined(DJGPP)
     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
-                      "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
+                      AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
                       0,
                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
                       &hkFreelance) == ERROR_SUCCESS) {
@@ -812,16 +1080,16 @@ long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw,
             if (RegQueryValueEx( hkFreelance, szIndex, 0, &dwType, szMount, &dwSize) != ERROR_SUCCESS) {
                 /* found an unused value */
                 dwType = REG_SZ;
-                dwSize = strlen(line) + 1;
+                dwSize = (DWORD)strlen(line) + 1;
                 RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
                 break;
             } else {
-                               int len = strlen(filename);
-                               if ( dwType == REG_SZ && !strncmp(filename, szMount, len) && 
-                                       (szMount[len] == '%' || szMount[len] == '#')) {
+                int len = (int)strlen(filename);
+                if ( dwType == REG_SZ && !strncmp(filename, szMount, len) && 
+                     (szMount[len] == '%' || szMount[len] == '#')) {
                     /* Replace the existing value */
                     dwType = REG_SZ;
-                    dwSize = strlen(line) + 1;
+                    dwSize = (DWORD)strlen(line) + 1;
                     RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
                     break;
                 }
@@ -829,9 +1097,8 @@ long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw,
         }
         RegCloseKey(hkFreelance);
     } else 
-#endif
     {
-        cm_GetConfigDir(hfile);
+        cm_GetConfigDir(hfile, sizeof(hfile));
         strcat(hfile, AFS_FREELANCE_INI);
         fp = fopen(hfile, "r+");
         if (!fp)
@@ -848,15 +1115,39 @@ long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw,
             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(); */
+  done:
     if (fidp) {
-        fidp->unique = 1;
-        fidp->vnode = cm_noLocalMountPoints + 1;   /* vnode value of last mt pt */
+        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_data.rootSCachep, 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);
     }
-    cm_noteLocalMountPointChange();
-    return 0;
+    
+    return code;
 }
 
 long cm_FreelanceRemoveMount(char *toremove)
@@ -865,22 +1156,18 @@ long cm_FreelanceRemoveMount(char *toremove)
     char* cp;
     char line[512];
     char shortname[200];
-    char hfile[120], hfile2[120];
+    char hfile[260], hfile2[260];
     FILE *fp1, *fp2;
     int found=0;
-#if !defined(DJGPP)
     HKEY hkFreelance = 0;
     DWORD dwType, dwSize;
     DWORD dwMountPoints;
     DWORD dwIndex;
-#endif
 
     lock_ObtainMutex(&cm_Freelance_Lock);
 
-
-#if !defined(DJGPP)
     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
-                      "SOFTWARE\\OpenAFS\\Client\\Freelance",
+                      AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
                       0,
                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
                       &hkFreelance) == ERROR_SUCCESS) {
@@ -914,14 +1201,14 @@ long cm_FreelanceRemoveMount(char *toremove)
 
             if (!strcmp(shortname, toremove)) {
                 RegDeleteValue( hkFreelance, szValueName );
+                found = 1;
                 break;
             }
         }
         RegCloseKey(hkFreelance);
     } else 
-#endif
     {
-        cm_GetConfigDir(hfile);
+        cm_GetConfigDir(hfile, sizeof(hfile));
         strcat(hfile, AFS_FREELANCE_INI);
         strcpy(hfile2, hfile);
         strcat(hfile2, "2");
@@ -956,46 +1243,70 @@ long cm_FreelanceRemoveMount(char *toremove)
 
         fclose(fp1);
         fclose(fp2);
-        if (!found)
-            return CM_ERROR_NOSUCHFILE;
+        if (found) {
+            unlink(hfile);
+            rename(hfile2, hfile);
+        }
+    }
 
-        unlink(hfile);
-        rename(hfile2, hfile);
+    if (found) {
+        /* Do this while we are holding the lock */
+        cm_noteLocalMountPointChange(TRUE);
     }
-    
     lock_ReleaseMutex(&cm_Freelance_Lock);
-    cm_noteLocalMountPointChange();
-    return 0;
+    return (found ? 0 : CM_ERROR_NOSUCHFILE);
 }
 
 long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
 {
-    FILE *fp;
-    char hfile[120];
     char line[512];
-    char fullname[200];
-    int n;
+    char fullname[CELL_MAXNAMELEN] = "";
     int alias = 0;
-#if !defined(DJGPP)
     HKEY hkFreelanceSymlinks = 0;
     DWORD dwType, dwSize;
     DWORD dwSymlinks;
     DWORD dwIndex;
-#endif
+    afs_uint32 code = 0;
 
-    /* before adding, verify the cell name; if it is not a valid cell,
-       don't add the mount point.
-       allow partial matches as a means of poor man's alias. */
-    /* major performance issue? */
+    /*
+     * 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",
               osi_LogSaveString(afsd_logp,filename), 
               osi_LogSaveString(afsd_logp,destination));
     
+    if ( filename[0] == '\0' || destination[0] == '\0' )
+        return CM_ERROR_INVAL;
+
+    /* 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) {
+            code = CM_ERROR_EXISTS;
+            goto done;
+        }
+    } else {
+        cm_GetCell_Gen(filename, fullname, CM_FLAG_CREATE);
+        if (cm_stricmp_utf8(filename,fullname) == 0) {
+            code = CM_ERROR_EXISTS;
+            goto done;
+        }
+    }
+
     lock_ObtainMutex(&cm_Freelance_Lock);
 
-#if !defined(DJGPP)
     if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
-                        "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
+                        AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
                         0,
                         NULL,
                         REG_OPTION_NON_VOLATILE,
@@ -1032,15 +1343,15 @@ long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
             if (RegQueryValueEx( hkFreelanceSymlinks, szIndex, 0, &dwType, szLink, &dwSize) != ERROR_SUCCESS) {
                 /* found an unused value */
                 dwType = REG_SZ;
-                dwSize = strlen(line) + 1;
+                dwSize = (DWORD)strlen(line) + 1;
                 RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
                 break;
             } else {
-                               int len = strlen(filename);
-                               if ( dwType == REG_SZ && !strncmp(filename, szLink, len) && szLink[len] == ':') {
+                int len = (int)strlen(filename);
+                if ( dwType == REG_SZ && !strncmp(filename, szLink, len) && szLink[len] == ':') {
                     /* Replace the existing value */
                     dwType = REG_SZ;
-                    dwSize = strlen(line) + 1;
+                    dwSize = (DWORD)strlen(line) + 1;
                     RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
                     break;
                 }
@@ -1048,40 +1359,57 @@ long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
         }
         RegCloseKey(hkFreelanceSymlinks);
     } 
-#endif
+
+    /* Do this while we are holding the lock */
+    cm_noteLocalMountPointChange(TRUE);
     lock_ReleaseMutex(&cm_Freelance_Lock);
 
-    /* cm_reInitLocalMountPoints(); */
+  done:
     if (fidp) {
-        fidp->unique = 1;
-        fidp->vnode = cm_noLocalMountPoints + 1;   /* vnode value of last mt pt */
+        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_data.rootSCachep, 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);
+            }
+        }
     }
-    cm_noteLocalMountPointChange();
-    return 0;
+
+    return code;
 }
 
 long cm_FreelanceRemoveSymlink(char *toremove)
 {
-    int i, n;
     char* cp;
     char line[512];
     char shortname[200];
-    char hfile[120], hfile2[120];
-    FILE *fp1, *fp2;
     int found=0;
-#if !defined(DJGPP)
     HKEY hkFreelanceSymlinks = 0;
     DWORD dwType, dwSize;
     DWORD dwSymlinks;
     DWORD dwIndex;
-#endif
 
     lock_ObtainMutex(&cm_Freelance_Lock);
 
-
-#if !defined(DJGPP)
     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
-                      "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
+                      AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
                       0,
                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
                       &hkFreelanceSymlinks) == ERROR_SUCCESS) {
@@ -1113,15 +1441,57 @@ long cm_FreelanceRemoveSymlink(char *toremove)
 
             if (!strcmp(shortname, toremove)) {
                 RegDeleteValue( hkFreelanceSymlinks, szValueName );
+                found = 1;
                 break;
             }
         }
         RegCloseKey(hkFreelanceSymlinks);
     }
-#endif
     
+    if (found) {
+        /* Do this while we are holding the lock */
+        cm_noteLocalMountPointChange(TRUE);
+    }
     lock_ReleaseMutex(&cm_Freelance_Lock);
-    cm_noteLocalMountPointChange();
+    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 */