windows-freelance-deadlock-20080815
[openafs.git] / src / WINNT / afsd / cm_freelance.c
1 #include <afs/param.h>
2 #include <afs/stds.h>
3
4 #include <windows.h>
5 #include <winreg.h>
6 #include <winsock2.h>
7 #include <stdlib.h>
8 #include <malloc.h>
9 #include <string.h>
10
11 #include <WINNT/afsreg.h>
12 #include "afsd.h"
13 #include <rx/rx.h>
14
15 #ifdef AFS_FREELANCE_CLIENT
16 #include "cm_freelance.h"
17 #include "stdio.h"
18
19 extern void afsi_log(char *pattern, ...);
20
21 int cm_noLocalMountPoints;
22 char * cm_FakeRootDir = NULL;
23 int cm_fakeDirSize = 0;
24 int cm_fakeDirCallback=0;
25 int cm_fakeGettingCallback=0;
26 cm_localMountPoint_t* cm_localMountPoints;
27 osi_mutex_t cm_Freelance_Lock;
28 int cm_localMountPointChangeFlag = 0;
29 int cm_freelanceEnabled = 1;
30 time_t FakeFreelanceModTime = 0x3b49f6e2;
31
32 static int freelance_ShutdownFlag = 0;
33 static HANDLE hFreelanceChangeEvent = 0;
34 static HANDLE hFreelanceSymlinkChangeEvent = 0;
35
36 void cm_InitFakeRootDir();
37
38 void cm_FreelanceChangeNotifier(void * parmp) {
39     HKEY   hkFreelance = 0;
40
41     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
42                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
43                       0,
44                       KEY_NOTIFY,
45                       &hkFreelance) == ERROR_SUCCESS) {
46
47         hFreelanceChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
48         if (hFreelanceChangeEvent == NULL) {
49             RegCloseKey(hkFreelance);
50             return;
51         }
52     }
53
54     while ( TRUE ) {
55     /* check hFreelanceChangeEvent to see if it is set. 
56      * if so, call cm_noteLocalMountPointChange()
57      */
58         if (RegNotifyChangeKeyValue( hkFreelance,   /* hKey */
59                                      FALSE,         /* bWatchSubtree */
60                                      REG_NOTIFY_CHANGE_LAST_SET, /* dwNotifyFilter */
61                                      hFreelanceChangeEvent, /* hEvent */
62                                      TRUE          /* fAsynchronous */
63                                      ) != ERROR_SUCCESS) {
64             RegCloseKey(hkFreelance);
65             CloseHandle(hFreelanceChangeEvent);
66             hFreelanceChangeEvent = 0;
67             return;
68         }
69
70         if (WaitForSingleObject(hFreelanceChangeEvent, INFINITE) == WAIT_OBJECT_0)
71         {
72             if (freelance_ShutdownFlag == 1) {     
73                 RegCloseKey(hkFreelance);          
74                 CloseHandle(hFreelanceChangeEvent);
75                 hFreelanceChangeEvent = 0;         
76                 return;                            
77             }                                      
78             cm_noteLocalMountPointChange();
79         }
80     }
81 }
82
83 void cm_FreelanceSymlinkChangeNotifier(void * parmp) {
84     HKEY   hkFreelance = 0;
85
86     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
87                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
88                       0,
89                       KEY_NOTIFY,
90                       &hkFreelance) == ERROR_SUCCESS) {
91
92         hFreelanceSymlinkChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
93         if (hFreelanceSymlinkChangeEvent == NULL) {
94             RegCloseKey(hkFreelance);
95             return;
96         }
97     }
98
99     while ( TRUE ) {
100     /* check hFreelanceSymlinkChangeEvent to see if it is set. 
101      * if so, call cm_noteLocalMountPointSymlinkChange()
102      */
103         if (RegNotifyChangeKeyValue( hkFreelance,   /* hKey */
104                                      FALSE,         /* bWatchSubtree */
105                                      REG_NOTIFY_CHANGE_LAST_SET, /* dwNotifyFilter */
106                                      hFreelanceSymlinkChangeEvent, /* hEvent */
107                                      TRUE          /* fAsynchronous */
108                                      ) != ERROR_SUCCESS) {
109             RegCloseKey(hkFreelance);
110             CloseHandle(hFreelanceSymlinkChangeEvent);
111             hFreelanceSymlinkChangeEvent = 0;
112             return;
113         }
114
115         if (WaitForSingleObject(hFreelanceSymlinkChangeEvent, INFINITE) == WAIT_OBJECT_0)
116         {
117             if (freelance_ShutdownFlag == 1) {     
118                 RegCloseKey(hkFreelance);          
119                 CloseHandle(hFreelanceSymlinkChangeEvent);
120                 hFreelanceSymlinkChangeEvent = 0;         
121                 return;                            
122             }                                      
123             cm_noteLocalMountPointChange();
124         }
125     }
126 }
127
128 void                                          
129 cm_FreelanceShutdown(void)                    
130 {                                             
131     freelance_ShutdownFlag = 1;               
132     if (hFreelanceChangeEvent != 0)           
133         thrd_SetEvent(hFreelanceChangeEvent); 
134     if (hFreelanceSymlinkChangeEvent != 0)           
135         thrd_SetEvent(hFreelanceSymlinkChangeEvent); 
136 }                                             
137
138 void cm_InitFreelance() {
139     thread_t phandle;
140     int lpid;
141
142     lock_InitializeMutex(&cm_Freelance_Lock, "Freelance Lock");
143
144     // yj: first we make a call to cm_initLocalMountPoints
145     // to read all the local mount points from the registry
146     cm_InitLocalMountPoints();
147
148     // then we make a call to InitFakeRootDir to create
149     // a fake root directory based on the local mount points
150     cm_InitFakeRootDir();
151     // --- end of yj code
152
153     /* Start the registry monitor */
154     phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceChangeNotifier,
155                           NULL, 0, &lpid, "cm_FreelanceChangeNotifier");
156     osi_assertx(phandle != NULL, "cm_FreelanceChangeNotifier thread create failure");
157     thrd_CloseHandle(phandle);
158
159     phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceSymlinkChangeNotifier,
160                           NULL, 0, &lpid, "cm_FreelanceSymlinkChangeNotifier");
161     osi_assertx(phandle != NULL, "cm_FreelanceSymlinkChangeNotifier thread create failure");
162     thrd_CloseHandle(phandle);
163 }
164
165 /* yj: Initialization of the fake root directory */
166 /* to be called while holding freelance lock unless during init. */
167 void cm_InitFakeRootDir() {
168     int i, t1, t2;
169     char* currentPos;
170     int noChunks;
171
172     // allocate space for the fake info
173     cm_dirHeader_t fakeDirHeader;
174     cm_dirEntry_t fakeEntry;
175     cm_pageHeader_t fakePageHeader;
176
177     // i'm going to calculate how much space is needed for
178     // this fake root directory. we have these rules:
179     // 1. there are cm_noLocalMountPoints number of entries
180     // 2. each page is CM_DIR_PAGESIZE in size
181     // 3. the first 13 chunks of the first page are used for
182     //    some header stuff
183     // 4. the first chunk of all subsequent pages are used
184     //    for page header stuff
185     // 5. a max of CM_DIR_EPP entries are allowed per page
186     // 6. each entry takes 1 or more chunks, depending on 
187     //    the size of the mount point string, as determined
188     //    by cm_NameEntries
189     // 7. each chunk is CM_DIR_CHUNKSIZE bytes
190
191     int CPP = CM_DIR_PAGESIZE / CM_DIR_CHUNKSIZE;
192     int curChunk = 13;  // chunks 0 - 12 are used for header stuff
193                         // of the first page in the directory
194     int curPage = 0;
195     int curDirEntry = 0;
196     int curDirEntryInPage = 0;
197     int sizeOfCurEntry;
198     int dirSize;
199
200     /* Reserve 2 directory chunks for "." and ".." */
201     curChunk += 2;
202
203     while (curDirEntry<cm_noLocalMountPoints) {
204         sizeOfCurEntry = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
205         if ((curChunk + sizeOfCurEntry >= CPP) ||
206              (curDirEntryInPage + 1 >= CM_DIR_EPP)) {
207             curPage++;
208             curDirEntryInPage = 0;
209             curChunk = 1;
210         }
211         curChunk += sizeOfCurEntry;
212         curDirEntry++;
213         curDirEntryInPage++;
214     }
215
216     dirSize = (curPage+1) *  CM_DIR_PAGESIZE;
217     if (cm_fakeDirSize != dirSize) {
218         if (cm_FakeRootDir)
219             free(cm_FakeRootDir);
220         cm_FakeRootDir = malloc(dirSize);
221         cm_fakeDirSize = dirSize;
222     }
223
224     // yj: when we get here, we've figured out how much memory we need and 
225     // allocated the appropriate space for it. we now prceed to fill
226     // it up with entries.
227     curPage = 0;
228     curDirEntry = 0;
229     curDirEntryInPage = 0;
230     curChunk = 0;
231
232     // fields in the directory entry that are unused.
233     fakeEntry.flag = 1;
234     fakeEntry.length = 0;
235     fakeEntry.next = 0;
236     fakeEntry.fid.unique = htonl(1);
237
238     // the first page is special, it uses fakeDirHeader instead of fakePageHeader
239     // we fill up the page with dirEntries that belong there and we make changes
240     // to the fakeDirHeader.header.freeBitmap along the way. Then when we're done
241     // filling up the dirEntries in this page, we copy the fakeDirHeader into 
242     // the top of the page.
243
244     // init the freeBitmap array
245     for (i=0; i<8; i++) 
246         fakeDirHeader.header.freeBitmap[i]=0;
247
248     fakeDirHeader.header.freeBitmap[0] = 0xff;
249     fakeDirHeader.header.freeBitmap[1] = 0x7f;
250
251
252     // we start counting at 13 because the 0th to 12th chunks are used for header
253     curChunk = 13;
254
255     // stick the first 2 entries "." and ".." in
256     fakeEntry.fid.vnode = htonl(1);
257     strcpy(fakeEntry.name, ".");
258     currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
259     memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
260     curChunk++; curDirEntryInPage++;
261     strcpy(fakeEntry.name, "..");
262     currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
263     memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
264     curChunk++; curDirEntryInPage++;
265
266     // keep putting stuff into page 0 if
267     // 1. we're not done with all entries
268     // 2. we have less than CM_DIR_EPP entries in page 0
269     // 3. we're not out of chunks in page 0
270
271     while( (curDirEntry<cm_noLocalMountPoints) && 
272            (curDirEntryInPage < CM_DIR_EPP) &&
273            (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP)) 
274     {       
275
276         noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
277         fakeEntry.fid.vnode = htonl(curDirEntry + 2);
278         currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
279
280         memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
281         strcpy(currentPos + 12, (cm_localMountPoints+curDirEntry)->namep);
282         curDirEntry++;
283         curDirEntryInPage++;
284         for (i=0; i<noChunks; i++) {
285             t1 = (curChunk + i) / 8;
286             t2 = curChunk + i - (t1*8);
287             fakeDirHeader.header.freeBitmap[t1] |= (1 << t2);
288         }
289         curChunk+=noChunks;
290     }
291
292     // when we get here, we're done with filling in the entries for page 0
293     // copy in the header info
294
295     memcpy(cm_FakeRootDir, &fakeDirHeader, 13 * CM_DIR_CHUNKSIZE);
296
297     curPage++;
298
299     // ok, page 0's done. Move on to the next page.
300     while (curDirEntry<cm_noLocalMountPoints) {
301         // setup a new page
302         curChunk = 1;                   // the zeroth chunk is reserved for page header
303         curDirEntryInPage = 0; 
304         for (i=0; i<8; i++) {
305             fakePageHeader.freeBitmap[i]=0;
306         }
307         fakePageHeader.freeCount = 0;
308         fakePageHeader.pgcount = 0;
309         fakePageHeader.tag = htons(1234);
310
311         // while we're on the same page...
312         while ( (curDirEntry<cm_noLocalMountPoints) &&
313                 (curDirEntryInPage < CM_DIR_EPP) &&
314                 (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP))
315         {
316             // add an entry to this page
317
318             noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
319             fakeEntry.fid.vnode=htonl(curDirEntry+2);
320             currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
321             memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
322             strcpy(currentPos + 12, (cm_localMountPoints+curDirEntry)->namep);
323             curDirEntry++;
324             curDirEntryInPage++;
325             for (i=0; i<noChunks; i++) {
326                 t1 = (curChunk + i) / 8;
327                 t2 = curChunk + i - (t1*8);
328                 fakePageHeader.freeBitmap[t1] |= (1 << t2);
329             }
330             curChunk+=noChunks;
331         }
332         memcpy(cm_FakeRootDir + curPage * CM_DIR_PAGESIZE, &fakePageHeader, sizeof(fakePageHeader));
333
334         curPage++;
335     }
336
337     // we know the fakeDir is setup properly, so we claim that we have callback
338     osi_Log0(afsd_logp,"cm_InitFakeRootDir fakeDirCallback=1");
339     cm_fakeDirCallback=1;
340
341     // when we get here, we've set up everything! done!
342 }
343
344 int cm_FakeRootFid(cm_fid_t *fidp)
345 {
346     cm_SetFid(fidp, 
347               AFS_FAKE_ROOT_CELL_ID,            /* root cell */
348               AFS_FAKE_ROOT_VOL_ID,            /* root.afs ? */
349               1, 1);
350     return 0;
351 }
352   
353 /* called directly from ioctl */
354 /* called while not holding freelance lock */
355 int cm_noteLocalMountPointChange(void) {
356     lock_ObtainMutex(&cm_Freelance_Lock);
357     cm_data.fakeDirVersion++;
358     cm_localMountPointChangeFlag = 1;
359     lock_ReleaseMutex(&cm_Freelance_Lock);
360     return 1;
361 }
362
363 int cm_getLocalMountPointChange() {
364     return cm_localMountPointChangeFlag;
365 }
366
367 int cm_clearLocalMountPointChange() {
368     cm_localMountPointChangeFlag = 0;
369     return 0;
370 }
371
372 int cm_reInitLocalMountPoints() {
373     cm_fid_t aFid;
374     int i, hash;
375     cm_scache_t *scp, **lscpp, *tscp;
376         
377     osi_Log0(afsd_logp,"----- freelance reinitialization starts ----- ");
378
379     // first we invalidate all the SCPs that were created
380     // for the local mount points
381
382     osi_Log0(afsd_logp,"Invalidating local mount point scp...  ");
383
384
385     lock_ObtainWrite(&cm_scacheLock);
386     lock_ObtainMutex(&cm_Freelance_Lock);  /* always scache then freelance lock */
387     for (i=1; i<=cm_noLocalMountPoints; i++) {
388         cm_SetFid(&aFid, AFS_FAKE_ROOT_CELL_ID, AFS_FAKE_ROOT_VOL_ID, i, 1);
389         hash = CM_SCACHE_HASH(&aFid);
390         for (scp=cm_data.scacheHashTablep[hash]; scp; scp=scp->nextp) {
391             if (scp != cm_data.rootSCachep && cm_FidCmp(&scp->fid, &aFid) == 0) {
392                 // mark the scp to be reused
393                 cm_HoldSCacheNoLock(scp);
394                 lock_ReleaseWrite(&cm_Freelance_Lock);
395                 lock_ReleaseWrite(&cm_scacheLock);
396                 lock_ObtainWrite(&scp->rw);
397                 cm_DiscardSCache(scp);
398
399                 // take the scp out of the hash
400                 lock_ObtainWrite(&cm_scacheLock);
401                 for (lscpp = &cm_data.scacheHashTablep[hash], tscp = cm_data.scacheHashTablep[hash]; 
402                      tscp; 
403                      lscpp = &tscp->nextp, tscp = tscp->nextp) {
404                     if (tscp == scp) {
405                         *lscpp = scp->nextp;
406                         scp->nextp = NULL;
407                         scp->flags &= ~CM_SCACHEFLAG_INHASH;
408                         break;
409                     }
410                 }
411
412                 lock_ReleaseWrite(&scp->rw);
413                 lock_ReleaseWrite(&cm_scacheLock);
414                 cm_CallbackNotifyChange(scp);
415                 lock_ObtainWrite(&cm_scacheLock);
416                 cm_ReleaseSCacheNoLock(scp);
417                 lock_ObtainMutex(&cm_Freelance_Lock);
418             }
419         }
420     }
421     lock_ReleaseWrite(&cm_scacheLock);
422     osi_Log0(afsd_logp,"\tall old scp cleared!");
423
424     // we must free the memory that was allocated in the prev
425     // cm_InitLocalMountPoints call
426     osi_Log0(afsd_logp,"Removing old localmountpoints...  ");
427     free(cm_localMountPoints);
428     osi_Log0(afsd_logp,"\tall old localmountpoints cleared!");
429
430     // now re-init the localmountpoints
431     osi_Log0(afsd_logp,"Creating new localmountpoints...  ");
432     cm_InitLocalMountPoints();
433     osi_Log0(afsd_logp,"\tcreated new set of localmountpoints!");
434
435     // then we re-create that dir
436     osi_Log0(afsd_logp,"Creating new fakedir...  ");
437     cm_InitFakeRootDir();
438     osi_Log0(afsd_logp,"\t\tcreated new fakedir!");
439
440     lock_ReleaseMutex(&cm_Freelance_Lock);
441
442     osi_Log0(afsd_logp,"----- freelance reinit complete -----");
443     return 0;
444 }
445
446
447 // yj: open up the registry and read all the local mount 
448 // points that are stored there. Part of the initialization
449 // process for the freelance client.
450 /* to be called while holding freelance lock unless during init. */
451 long cm_InitLocalMountPoints() {
452     FILE *fp;
453     int i;
454     char line[512];
455     char*t, *t2;
456     cm_localMountPoint_t* aLocalMountPoint;
457     char hdir[260];
458     long code;
459     char rootCellName[256];
460     HKEY hkFreelance = 0, hkFreelanceSymlinks = 0;
461     DWORD dwType, dwSize;
462     DWORD dwMountPoints = 0;
463     DWORD dwIndex;
464     DWORD dwSymlinks = 0;
465     FILETIME ftLastWriteTime;
466
467     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
468                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
469                       0,
470                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
471                       &hkFreelance) == ERROR_SUCCESS) {
472
473         RegQueryInfoKey( hkFreelance,
474                          NULL,  /* lpClass */
475                          NULL,  /* lpcClass */
476                          NULL,  /* lpReserved */
477                          NULL,  /* lpcSubKeys */
478                          NULL,  /* lpcMaxSubKeyLen */
479                          NULL,  /* lpcMaxClassLen */
480                          &dwMountPoints, /* lpcValues */
481                          NULL,  /* lpcMaxValueNameLen */
482                          NULL,  /* lpcMaxValueLen */
483                          NULL,  /* lpcbSecurityDescriptor */
484                          &ftLastWriteTime /* lpftLastWriteTime */
485                          );
486
487         smb_UnixTimeFromLargeSearchTime(&FakeFreelanceModTime, &ftLastWriteTime);
488
489         if ( dwMountPoints == 0 ) {
490             rootCellName[0] = '.';
491             code = cm_GetRootCellName(&rootCellName[1]);
492             if (code == 0) {
493                 cm_FreelanceAddMount(&rootCellName[1], &rootCellName[1], "root.cell.", 0, NULL);
494                 cm_FreelanceAddMount(rootCellName, &rootCellName[1], "root.cell.", 1, NULL);
495                 cm_FreelanceAddMount(".root", &rootCellName[1], "root.afs.", 1, NULL);
496                 dwMountPoints = 3;
497             }
498         }
499
500         if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
501                           AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
502                           0,
503                           NULL,
504                           REG_OPTION_NON_VOLATILE,
505                           KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
506                           NULL,
507                           &hkFreelanceSymlinks,
508                           NULL) == ERROR_SUCCESS) {
509
510             RegQueryInfoKey( hkFreelanceSymlinks,
511                              NULL,  /* lpClass */
512                              NULL,  /* lpcClass */
513                              NULL,  /* lpReserved */
514                              NULL,  /* lpcSubKeys */
515                              NULL,  /* lpcMaxSubKeyLen */
516                              NULL,  /* lpcMaxClassLen */
517                              &dwSymlinks, /* lpcValues */
518                              NULL,  /* lpcMaxValueNameLen */
519                              NULL,  /* lpcMaxValueLen */
520                              NULL,  /* lpcbSecurityDescriptor */
521                              NULL   /* lpftLastWriteTime */
522                              );
523         }
524
525         // get the number of entries there are from the first line
526         // that we read
527         cm_noLocalMountPoints = dwMountPoints + dwSymlinks;
528
529         // create space to store the local mount points
530         cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
531         aLocalMountPoint = cm_localMountPoints;
532
533         // now we read n lines and parse them into local mount points
534         // where n is the number of local mount points there are, as
535         // determined above.
536         // Each line in the ini file represents 1 local mount point and 
537         // is in the format xxx#yyy:zzz, where xxx is the directory
538         // entry name, yyy is the cell name and zzz is the volume name.
539         // #yyy:zzz together make up the mount point.
540         for ( dwIndex = 0 ; dwIndex < dwMountPoints; dwIndex++ ) {
541             TCHAR szValueName[16];
542             DWORD dwValueSize = 16;
543             dwSize = sizeof(line);
544             if (RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
545                           &dwType, line, &dwSize))
546             {
547                 afsi_log("RegEnumValue(hkFreelance) failed");
548                 cm_noLocalMountPoints--;
549                 continue;
550             }
551
552             afsi_log("Mountpoint[%d] = %s",dwIndex, line);
553
554             /* find the trailing dot; null terminate after it */
555             t2 = strrchr(line, '.');
556             if (t2)
557                 *(t2+1) = '\0';
558
559             for ( t=line;*t;t++ ) {
560                 if ( !isprint(*t) ) {
561                     afsi_log("error occurred while parsing mountpoint entry [%d]: non-printable character", dwIndex);
562                     fprintf(stderr, "error occurred while parsing mountpoint entry [%d]: non-printable character", dwIndex);
563                     cm_noLocalMountPoints--;
564                     continue;
565                 }
566             }
567
568             // line is not empty, so let's parse it
569             t = strchr(line, '#');
570             if (!t)
571                 t = strchr(line, '%');
572             // make sure that there is a '#' or '%' separator in the line
573             if (!t) {
574                 afsi_log("error occurred while parsing mountpoint entry [%d]: no # or %% separator", dwIndex);
575                 fprintf(stderr, "error occurred while parsing mountpoint entry [%d]: no # or %% separator", dwIndex);
576                 cm_noLocalMountPoints--;
577                 continue;
578             }
579
580             aLocalMountPoint->fileType = CM_SCACHETYPE_MOUNTPOINT;
581             aLocalMountPoint->namep=malloc(t-line+1);
582             strncpy(aLocalMountPoint->namep, line, t-line);
583             aLocalMountPoint->namep[t-line] = '\0';
584                 
585             /* copy the mount point string */
586             aLocalMountPoint->mountPointStringp=malloc(strlen(t));
587             strncpy(aLocalMountPoint->mountPointStringp, t, strlen(t)-1);
588             aLocalMountPoint->mountPointStringp[strlen(t)-1] = '\0';
589     
590             osi_Log2(afsd_logp,"found mount point: name %s, string %s",
591                       osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
592                       osi_LogSaveString(afsd_logp,aLocalMountPoint->mountPointStringp));
593
594             aLocalMountPoint++;
595         }
596
597         for ( dwIndex = 0 ; dwIndex < dwSymlinks; dwIndex++ ) {
598             TCHAR szValueName[16];
599             DWORD dwValueSize = 16;
600             dwSize = sizeof(line);
601             if (RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
602                               &dwType, line, &dwSize))
603             {
604                 afsi_log("RegEnumValue(hkFreelanceSymlinks) failed");
605                 cm_noLocalMountPoints--;
606                 continue;
607             }
608
609             afsi_log("Symlink[%d] = %s",dwIndex, line);
610
611             /* find the trailing dot; null terminate after it */
612             t2 = strrchr(line, '.');
613             if (t2)
614                 *(t2+1) = '\0';
615
616             for ( t=line;*t;t++ ) {
617                 if ( !isprint(*t) ) {
618                     afsi_log("error occurred while parsing symlink entry [%d]: non-printable character", dwIndex);
619                     fprintf(stderr, "error occurred while parsing symlink entry [%d]: non-printable character", dwIndex);
620                     cm_noLocalMountPoints--;
621                     continue;
622                 }
623             }
624
625             // line is not empty, so let's parse it
626             t = strchr(line, ':');
627
628             // make sure that there is a ':' separator in the line
629             if (!t) {
630                 afsi_log("error occurred while parsing symlink entry [%d]: no ':' separator", dwIndex);
631                 fprintf(stderr, "error occurred while parsing symlink entry [%d]: no ':' separator", dwIndex);
632                 cm_noLocalMountPoints--;
633                 continue;
634             }
635
636             aLocalMountPoint->fileType = CM_SCACHETYPE_SYMLINK;
637             aLocalMountPoint->namep=malloc(t-line+1);
638             strncpy(aLocalMountPoint->namep, line, t-line);
639             aLocalMountPoint->namep[t-line] = '\0';
640                 
641             /* copy the symlink string */
642             aLocalMountPoint->mountPointStringp=malloc(strlen(t)-1);
643             strncpy(aLocalMountPoint->mountPointStringp, t+1, strlen(t)-2);
644             aLocalMountPoint->mountPointStringp[strlen(t)-2] = '\0';
645     
646             osi_Log2(afsd_logp,"found symlink: name %s, string %s",
647                       osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
648                       osi_LogSaveString(afsd_logp,aLocalMountPoint->mountPointStringp));
649
650             aLocalMountPoint++;
651         }
652
653         if ( hkFreelanceSymlinks )
654             RegCloseKey( hkFreelanceSymlinks );
655         RegCloseKey(hkFreelance);
656         return 0;
657     }
658
659     /* What follows is the old code to read freelance mount points 
660      * out of a text file modified to copy the data into the registry
661      */
662     cm_GetConfigDir(hdir, sizeof(hdir));
663     strcat(hdir, AFS_FREELANCE_INI);
664     // open the ini file for reading
665     fp = fopen(hdir, "r");
666     if (!fp) {
667         /* look in the Windows directory where we used to store the file */
668         GetWindowsDirectory(hdir, sizeof(hdir));
669         strcat(hdir,"\\");
670         strcat(hdir, AFS_FREELANCE_INI);
671         fp = fopen(hdir, "r");
672     }
673
674     RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
675                     AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
676                     0,
677                     NULL,
678                     REG_OPTION_NON_VOLATILE,
679                     KEY_READ|KEY_WRITE,
680                     NULL,
681                     &hkFreelance,
682                     NULL);
683     dwIndex = 0;
684
685     if (!fp) {
686         RegCloseKey(hkFreelance);
687         rootCellName[0] = '.';
688         code = cm_GetRootCellName(&rootCellName[1]);
689         if (code == 0) {
690             cm_FreelanceAddMount(&rootCellName[1], &rootCellName[1], "root.cell.", 0, NULL);
691             cm_FreelanceAddMount(rootCellName, &rootCellName[1], "root.cell.", 1, NULL);
692             cm_FreelanceAddMount(".root", &rootCellName[1], "root.afs.", 1, NULL);
693         }
694         return 0;
695     }
696
697     // we successfully opened the file
698     osi_Log0(afsd_logp,"opened afs_freelance.ini");
699         
700     // now we read the first line to see how many entries
701     // there are
702     fgets(line, sizeof(line), fp);
703
704     // if the line is empty at any point when we're reading
705     // we're screwed. report error and return.
706     if (*line==0) {
707         afsi_log("error occurred while reading afs_freelance.ini");
708         fprintf(stderr, "error occurred while reading afs_freelance.ini");
709         return -1;
710     }
711
712     // get the number of entries there are from the first line
713     // that we read
714     cm_noLocalMountPoints = atoi(line);
715
716     if (cm_noLocalMountPoints > 0) {
717         // create space to store the local mount points
718         cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
719         aLocalMountPoint = cm_localMountPoints;
720     }
721
722     // now we read n lines and parse them into local mount points
723     // where n is the number of local mount points there are, as
724     // determined above.
725     // Each line in the ini file represents 1 local mount point and 
726     // is in the format xxx#yyy:zzz, where xxx is the directory
727     // entry name, yyy is the cell name and zzz is the volume name.
728     // #yyy:zzz together make up the mount point.
729     for (i=0; i<cm_noLocalMountPoints; i++) {
730         fgets(line, sizeof(line), fp);
731         // check that the line is not empty
732         if (line[0]==0) {
733             afsi_log("error occurred while parsing entry in %s: empty line in line %d", AFS_FREELANCE_INI, i);
734             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: empty line in line %d", i);
735             return -1;
736         }
737
738         /* find the trailing dot; null terminate after it */
739         t2 = strrchr(line, '.');
740         if (t2)
741             *(t2+1) = '\0';
742
743         if ( hkFreelance ) {
744             char szIndex[16];
745             /* we are migrating to the registry */
746             sprintf(szIndex,"%d",dwIndex++);
747             dwType = REG_SZ;
748             dwSize = (DWORD)strlen(line) + 1;
749             RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
750         }
751
752         // line is not empty, so let's parse it
753         t = strchr(line, '#');
754         if (!t)
755             t = strchr(line, '%');
756         // make sure that there is a '#' or '%' separator in the line
757         if (!t) {
758             afsi_log("error occurred while parsing entry in %s: no # or %% separator in line %d", AFS_FREELANCE_INI, i);
759             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: no # or %% separator in line %d", i);
760             return -1;
761         }
762         aLocalMountPoint->namep=malloc(t-line+1);
763         memcpy(aLocalMountPoint->namep, line, t-line);
764         *(aLocalMountPoint->namep + (t-line)) = 0;
765
766         aLocalMountPoint->mountPointStringp=malloc(strlen(line) - (t-line) + 1);
767         memcpy(aLocalMountPoint->mountPointStringp, t, strlen(line)-(t-line)-1);
768         *(aLocalMountPoint->mountPointStringp + (strlen(line)-(t-line)-1)) = 0;
769
770         osi_Log2(afsd_logp,"found mount point: name %s, string %s",
771                   aLocalMountPoint->namep,
772                   aLocalMountPoint->mountPointStringp);
773
774         aLocalMountPoint++;
775     }
776     fclose(fp);
777     if ( hkFreelance ) {
778         RegCloseKey(hkFreelance);
779         DeleteFile(hdir);
780     }
781     return 0;
782 }
783
784 int cm_getNoLocalMountPoints() {
785     return cm_noLocalMountPoints;
786 }
787
788 long cm_FreelanceMountPointExists(char * filename, int prefix_ok)
789 {
790     char* cp;
791     char line[512];
792     char shortname[200];
793     int found = 0;
794     HKEY hkFreelance = 0;
795     DWORD dwType, dwSize;
796     DWORD dwMountPoints;
797     DWORD dwIndex;
798         
799     lock_ObtainMutex(&cm_Freelance_Lock);
800
801     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
802                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
803                       0,
804                       KEY_READ|KEY_QUERY_VALUE,
805                       &hkFreelance) == ERROR_SUCCESS) 
806     {
807         RegQueryInfoKey( hkFreelance,
808                          NULL,  /* lpClass */
809                          NULL,  /* lpcClass */
810                          NULL,  /* lpReserved */
811                          NULL,  /* lpcSubKeys */
812                          NULL,  /* lpcMaxSubKeyLen */
813                          NULL,  /* lpcMaxClassLen */
814                          &dwMountPoints, /* lpcValues */
815                          NULL,  /* lpcMaxValueNameLen */
816                          NULL,  /* lpcMaxValueLen */
817                          NULL,  /* lpcbSecurityDescriptor */
818                          NULL   /* lpftLastWriteTime */
819                          );
820
821         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
822             TCHAR szValueName[16];
823             DWORD dwValueSize = 16;
824             dwSize = sizeof(line);
825             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
826                           &dwType, line, &dwSize);
827
828             cp=strchr(line, '#');
829             if (!cp)
830                 cp=strchr(line, '%');
831             memcpy(shortname, line, cp-line);
832             shortname[cp-line]=0;
833
834             if (!strcmp(shortname, filename)) {
835                 found = 1;
836                 break;
837             }
838         }
839         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
840             TCHAR szValueName[16];
841             DWORD dwValueSize = 16;
842             dwSize = sizeof(line);
843             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
844                           &dwType, line, &dwSize);
845
846             cp=strchr(line, '#');
847             if (!cp)
848                 cp=strchr(line, '%');
849             memcpy(shortname, line, cp-line);
850             shortname[cp-line]=0;
851
852             if (!cm_stricmp_utf8(shortname, filename)) {
853                 found = 1;
854                 break;
855             }
856
857             if (prefix_ok && strlen(shortname) - strlen(filename) == 1 && !strncmp(shortname, filename, strlen(filename))) {
858                 found = 1;
859                 break;
860             }
861         }
862         RegCloseKey(hkFreelance);
863     }
864
865     lock_ReleaseMutex(&cm_Freelance_Lock);
866
867     return found;
868 }
869
870 long cm_FreelanceSymlinkExists(char * filename, int prefix_ok)
871 {
872     char* cp;
873     char line[512];
874     char shortname[200];
875     int found = 0;
876     HKEY hkFreelance = 0;
877     DWORD dwType, dwSize;
878     DWORD dwSymlinks;
879     DWORD dwIndex;
880         
881     lock_ObtainMutex(&cm_Freelance_Lock);
882
883     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
884                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
885                       0,
886                       KEY_READ|KEY_QUERY_VALUE,
887                       &hkFreelance) == ERROR_SUCCESS) 
888     {
889         RegQueryInfoKey( hkFreelance,
890                          NULL,  /* lpClass */
891                          NULL,  /* lpcClass */
892                          NULL,  /* lpReserved */
893                          NULL,  /* lpcSubKeys */
894                          NULL,  /* lpcMaxSubKeyLen */
895                          NULL,  /* lpcMaxClassLen */
896                          &dwSymlinks, /* lpcValues */
897                          NULL,  /* lpcMaxValueNameLen */
898                          NULL,  /* lpcMaxValueLen */
899                          NULL,  /* lpcbSecurityDescriptor */
900                          NULL   /* lpftLastWriteTime */
901                          );
902
903         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
904             TCHAR szValueName[16];
905             DWORD dwValueSize = 16;
906             dwSize = sizeof(line);
907             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
908                           &dwType, line, &dwSize);
909
910             cp=strchr(line, ':');
911             memcpy(shortname, line, cp-line);
912             shortname[cp-line]=0;
913
914             if (!strcmp(shortname, filename)) {
915                 found = 1;
916                 break;
917             }
918
919             if (prefix_ok && strlen(shortname) - strlen(filename) == 1 && !strncmp(shortname, filename, strlen(filename))) {
920                 found = 1;
921                 break;
922             }
923         }
924         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
925             TCHAR szValueName[16];
926             DWORD dwValueSize = 16;
927             dwSize = sizeof(line);
928             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
929                           &dwType, line, &dwSize);
930
931             cp=strchr(line, ':');
932             memcpy(shortname, line, cp-line);
933             shortname[cp-line]=0;
934
935             if (!cm_stricmp_utf8(shortname, filename)) {
936                 found = 1;
937                 break;
938             }
939         }
940         RegCloseKey(hkFreelance);
941     }
942
943     lock_ReleaseMutex(&cm_Freelance_Lock);
944
945     return found;
946 }
947
948 long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp)
949 {
950     FILE *fp;
951     char hfile[260];
952     char line[512];
953     char fullname[CELL_MAXNAMELEN];
954     int n;
955     int alias = 0;
956     HKEY hkFreelance = 0;
957     DWORD dwType, dwSize;
958     DWORD dwMountPoints;
959     DWORD dwIndex;
960
961     /* before adding, verify the cell name; if it is not a valid cell,
962        don't add the mount point.
963        allow partial matches as a means of poor man's alias. */
964     /* major performance issue? */
965     osi_Log4(afsd_logp,"Freelance Add Mount request: filename=%s cellname=%s volume=%s %s",
966               osi_LogSaveString(afsd_logp,filename), 
967               osi_LogSaveString(afsd_logp,cellname), 
968               osi_LogSaveString(afsd_logp,volume), 
969               rw ? "rw" : "ro");
970
971     if ( filename[0] == '\0' || cellname[0] == '\0' || volume[0] == '\0' )
972         return -1;
973
974     if (cellname[0] == '.') {
975         if (!cm_GetCell_Gen(&cellname[1], fullname, CM_FLAG_CREATE))
976             return -1;
977     } else {
978         if (!cm_GetCell_Gen(cellname, fullname, CM_FLAG_CREATE))
979             return -1;
980     }
981
982     if ( cm_FreelanceMountPointExists(filename, 0) ||
983          cm_FreelanceSymlinkExists(filename, 0) )
984         return -1;
985     
986     osi_Log1(afsd_logp,"Freelance Adding Mount for Cell: %s", 
987               osi_LogSaveString(afsd_logp,cellname));
988
989     lock_ObtainMutex(&cm_Freelance_Lock);
990
991     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
992                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
993                       0,
994                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
995                       &hkFreelance) == ERROR_SUCCESS) {
996
997         RegQueryInfoKey( hkFreelance,
998                          NULL,  /* lpClass */
999                          NULL,  /* lpcClass */
1000                          NULL,  /* lpReserved */
1001                          NULL,  /* lpcSubKeys */
1002                          NULL,  /* lpcMaxSubKeyLen */
1003                          NULL,  /* lpcMaxClassLen */
1004                          &dwMountPoints, /* lpcValues */
1005                          NULL,  /* lpcMaxValueNameLen */
1006                          NULL,  /* lpcMaxValueLen */
1007                          NULL,  /* lpcbSecurityDescriptor */
1008                          NULL   /* lpftLastWriteTime */
1009                          );
1010
1011         if (rw)
1012             sprintf(line, "%s%%%s:%s", filename, fullname, volume);
1013         else
1014             sprintf(line, "%s#%s:%s", filename, fullname, volume);
1015
1016         /* If we are adding a new value, there must be an unused name
1017          * within the range 0 to dwMountPoints 
1018          */
1019         for ( dwIndex = 0; dwIndex <= dwMountPoints; dwIndex++ ) {
1020             char szIndex[16];
1021             char szMount[1024];
1022
1023             dwSize = sizeof(szMount);
1024             sprintf(szIndex, "%d", dwIndex);
1025             if (RegQueryValueEx( hkFreelance, szIndex, 0, &dwType, szMount, &dwSize) != ERROR_SUCCESS) {
1026                 /* found an unused value */
1027                 dwType = REG_SZ;
1028                 dwSize = (DWORD)strlen(line) + 1;
1029                 RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
1030                 break;
1031             } else {
1032                 int len = (int)strlen(filename);
1033                 if ( dwType == REG_SZ && !strncmp(filename, szMount, len) && 
1034                      (szMount[len] == '%' || szMount[len] == '#')) {
1035                     /* Replace the existing value */
1036                     dwType = REG_SZ;
1037                     dwSize = (DWORD)strlen(line) + 1;
1038                     RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
1039                     break;
1040                 }
1041             }
1042         }
1043         RegCloseKey(hkFreelance);
1044     } else 
1045     {
1046         cm_GetConfigDir(hfile, sizeof(hfile));
1047         strcat(hfile, AFS_FREELANCE_INI);
1048         fp = fopen(hfile, "r+");
1049         if (!fp)
1050             return CM_ERROR_INVAL;
1051         fgets(line, sizeof(line), fp);
1052         n = atoi(line);
1053         n++;
1054         fseek(fp, 0, SEEK_SET);
1055         fprintf(fp, "%d", n);
1056         fseek(fp, 0, SEEK_END);
1057         if (rw)
1058             fprintf(fp, "%s%%%s:%s\n", filename, fullname, volume);
1059         else
1060             fprintf(fp, "%s#%s:%s\n", filename, fullname, volume);
1061         fclose(fp);
1062     }
1063     lock_ReleaseMutex(&cm_Freelance_Lock);
1064
1065     /* cm_reInitLocalMountPoints(); */
1066     if (fidp)
1067         cm_SetFid(fidp, fidp->cell, fidp->volume, ++cm_noLocalMountPoints, 1);
1068     cm_noteLocalMountPointChange();
1069     return 0;
1070 }
1071
1072 long cm_FreelanceRemoveMount(char *toremove)
1073 {
1074     int i, n;
1075     char* cp;
1076     char line[512];
1077     char shortname[200];
1078     char hfile[260], hfile2[260];
1079     FILE *fp1, *fp2;
1080     int found=0;
1081     HKEY hkFreelance = 0;
1082     DWORD dwType, dwSize;
1083     DWORD dwMountPoints;
1084     DWORD dwIndex;
1085
1086     lock_ObtainMutex(&cm_Freelance_Lock);
1087
1088     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
1089                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
1090                       0,
1091                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1092                       &hkFreelance) == ERROR_SUCCESS) {
1093
1094         RegQueryInfoKey( hkFreelance,
1095                          NULL,  /* lpClass */
1096                          NULL,  /* lpcClass */
1097                          NULL,  /* lpReserved */
1098                          NULL,  /* lpcSubKeys */
1099                          NULL,  /* lpcMaxSubKeyLen */
1100                          NULL,  /* lpcMaxClassLen */
1101                          &dwMountPoints, /* lpcValues */
1102                          NULL,  /* lpcMaxValueNameLen */
1103                          NULL,  /* lpcMaxValueLen */
1104                          NULL,  /* lpcbSecurityDescriptor */
1105                          NULL   /* lpftLastWriteTime */
1106                          );
1107
1108         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
1109             TCHAR szValueName[16];
1110             DWORD dwValueSize = 16;
1111             dwSize = sizeof(line);
1112             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
1113                           &dwType, line, &dwSize);
1114
1115             cp=strchr(line, '#');
1116             if (!cp)
1117                 cp=strchr(line, '%');
1118             memcpy(shortname, line, cp-line);
1119             shortname[cp-line]=0;
1120
1121             if (!strcmp(shortname, toremove)) {
1122                 RegDeleteValue( hkFreelance, szValueName );
1123                 found = 1;
1124                 break;
1125             }
1126         }
1127         RegCloseKey(hkFreelance);
1128     } else 
1129     {
1130         cm_GetConfigDir(hfile, sizeof(hfile));
1131         strcat(hfile, AFS_FREELANCE_INI);
1132         strcpy(hfile2, hfile);
1133         strcat(hfile2, "2");
1134         fp1=fopen(hfile, "r+");
1135         if (!fp1)
1136             return CM_ERROR_INVAL;
1137         fp2=fopen(hfile2, "w+");
1138         if (!fp2) {
1139             fclose(fp1);
1140             return CM_ERROR_INVAL;
1141         }
1142
1143         fgets(line, sizeof(line), fp1);
1144         n=atoi(line);
1145         fprintf(fp2, "%d\n", n-1);
1146
1147         for (i=0; i<n; i++) {
1148             fgets(line, sizeof(line), fp1);
1149             cp=strchr(line, '#');
1150             if (!cp)
1151                 cp=strchr(line, '%');
1152             memcpy(shortname, line, cp-line);
1153             shortname[cp-line]=0;
1154
1155             if (strcmp(shortname, toremove)==0) {
1156
1157             } else {
1158                 found = 1;
1159                 fputs(line, fp2);
1160             }
1161         }
1162
1163         fclose(fp1);
1164         fclose(fp2);
1165         if (found) {
1166             unlink(hfile);
1167             rename(hfile2, hfile);
1168         }
1169     }
1170     
1171     lock_ReleaseMutex(&cm_Freelance_Lock);
1172     if (found) {
1173         cm_noteLocalMountPointChange();
1174         return 0;
1175     } else
1176         return CM_ERROR_NOSUCHFILE;
1177 }
1178
1179 long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
1180 {
1181     char line[512];
1182     char fullname[CELL_MAXNAMELEN];
1183     int alias = 0;
1184     HKEY hkFreelanceSymlinks = 0;
1185     DWORD dwType, dwSize;
1186     DWORD dwSymlinks;
1187     DWORD dwIndex;
1188
1189     /* before adding, verify the filename.  If it is already in use, either as 
1190      * as mount point or a cellname, do not permit the creation of the symlink.
1191      */
1192     osi_Log2(afsd_logp,"Freelance Add Symlink request: filename=%s destination=%s",
1193               osi_LogSaveString(afsd_logp,filename), 
1194               osi_LogSaveString(afsd_logp,destination));
1195     
1196     if ( filename[0] == '\0' || destination[0] == '\0' )
1197         return CM_ERROR_INVAL;
1198
1199     fullname[0] = '\0';
1200     if (filename[0] == '.') {
1201         cm_GetCell_Gen(&filename[1], fullname, CM_FLAG_CREATE);
1202         if (cm_stricmp_utf8(&filename[1],fullname) == 0)
1203             return CM_ERROR_EXISTS;
1204     } else {
1205         cm_GetCell_Gen(filename, fullname, CM_FLAG_CREATE);
1206         if (cm_stricmp_utf8(filename,fullname) == 0)
1207             return CM_ERROR_EXISTS;
1208     }
1209
1210     if ( cm_FreelanceMountPointExists(filename, 0) ||
1211          cm_FreelanceSymlinkExists(filename, 0) )
1212         return CM_ERROR_EXISTS;
1213
1214     lock_ObtainMutex(&cm_Freelance_Lock);
1215
1216     if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
1217                         AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
1218                         0,
1219                         NULL,
1220                         REG_OPTION_NON_VOLATILE,
1221                         KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1222                         NULL,
1223                         &hkFreelanceSymlinks,
1224                         NULL) == ERROR_SUCCESS) {
1225
1226         RegQueryInfoKey( hkFreelanceSymlinks,
1227                          NULL,  /* lpClass */
1228                          NULL,  /* lpcClass */
1229                          NULL,  /* lpReserved */
1230                          NULL,  /* lpcSubKeys */
1231                          NULL,  /* lpcMaxSubKeyLen */
1232                          NULL,  /* lpcMaxClassLen */
1233                          &dwSymlinks, /* lpcValues */
1234                          NULL,  /* lpcMaxValueNameLen */
1235                          NULL,  /* lpcMaxValueLen */
1236                          NULL,  /* lpcbSecurityDescriptor */
1237                          NULL   /* lpftLastWriteTime */
1238                          );
1239
1240         sprintf(line, "%s:%s.", filename, destination);
1241
1242         /* If we are adding a new value, there must be an unused name
1243          * within the range 0 to dwSymlinks 
1244          */
1245         for ( dwIndex = 0; dwIndex <= dwSymlinks; dwIndex++ ) {
1246             char szIndex[16];
1247             char szLink[1024];
1248
1249             dwSize = sizeof(szLink);
1250             sprintf(szIndex, "%d", dwIndex);
1251             if (RegQueryValueEx( hkFreelanceSymlinks, szIndex, 0, &dwType, szLink, &dwSize) != ERROR_SUCCESS) {
1252                 /* found an unused value */
1253                 dwType = REG_SZ;
1254                 dwSize = (DWORD)strlen(line) + 1;
1255                 RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1256                 break;
1257             } else {
1258                 int len = (int)strlen(filename);
1259                 if ( dwType == REG_SZ && !strncmp(filename, szLink, len) && szLink[len] == ':') {
1260                     /* Replace the existing value */
1261                     dwType = REG_SZ;
1262                     dwSize = (DWORD)strlen(line) + 1;
1263                     RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1264                     break;
1265                 }
1266             }
1267         }
1268         RegCloseKey(hkFreelanceSymlinks);
1269     } 
1270     lock_ReleaseMutex(&cm_Freelance_Lock);
1271
1272     /* cm_reInitLocalMountPoints(); */
1273     if (fidp)
1274         cm_SetFid(fidp, fidp->cell, fidp->volume, ++cm_noLocalMountPoints, 1);
1275     cm_noteLocalMountPointChange();
1276     return 0;
1277 }
1278
1279 long cm_FreelanceRemoveSymlink(char *toremove)
1280 {
1281     char* cp;
1282     char line[512];
1283     char shortname[200];
1284     int found=0;
1285     HKEY hkFreelanceSymlinks = 0;
1286     DWORD dwType, dwSize;
1287     DWORD dwSymlinks;
1288     DWORD dwIndex;
1289
1290     lock_ObtainMutex(&cm_Freelance_Lock);
1291
1292     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
1293                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
1294                       0,
1295                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1296                       &hkFreelanceSymlinks) == ERROR_SUCCESS) {
1297
1298         RegQueryInfoKey( hkFreelanceSymlinks,
1299                          NULL,  /* lpClass */
1300                          NULL,  /* lpcClass */
1301                          NULL,  /* lpReserved */
1302                          NULL,  /* lpcSubKeys */
1303                          NULL,  /* lpcMaxSubKeyLen */
1304                          NULL,  /* lpcMaxClassLen */
1305                          &dwSymlinks, /* lpcValues */
1306                          NULL,  /* lpcMaxValueNameLen */
1307                          NULL,  /* lpcMaxValueLen */
1308                          NULL,  /* lpcbSecurityDescriptor */
1309                          NULL   /* lpftLastWriteTime */
1310                          );
1311
1312         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
1313             TCHAR szValueName[16];
1314             DWORD dwValueSize = 16;
1315             dwSize = sizeof(line);
1316             RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
1317                           &dwType, line, &dwSize);
1318
1319             cp=strchr(line, ':');
1320             memcpy(shortname, line, cp-line);
1321             shortname[cp-line]=0;
1322
1323             if (!strcmp(shortname, toremove)) {
1324                 RegDeleteValue( hkFreelanceSymlinks, szValueName );
1325                 found = 1;
1326                 break;
1327             }
1328         }
1329         RegCloseKey(hkFreelanceSymlinks);
1330     }
1331     
1332     lock_ReleaseMutex(&cm_Freelance_Lock);
1333     if (found) {
1334         cm_noteLocalMountPointChange();
1335         return 0;
1336     } else
1337         return CM_ERROR_NOSUCHFILE;
1338 }
1339 #endif /* AFS_FREELANCE_CLIENT */