211eb0dcb3d7bca4393c05c40d081b83ee02f3dd
[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     cm_SetFid(&aFid, AFS_FAKE_ROOT_CELL_ID, AFS_FAKE_ROOT_VOL_ID, 1, 1);
385
386     lock_ObtainWrite(&cm_scacheLock);
387     lock_ObtainMutex(&cm_Freelance_Lock);  /* always scache then freelance lock */
388     for (i=0; i<cm_noLocalMountPoints; i++) {
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_scacheLock);
395                 lock_ObtainWrite(&scp->rw);
396                 cm_DiscardSCache(scp);
397                 lock_ReleaseWrite(&scp->rw);
398                 cm_CallbackNotifyChange(scp);
399                 lock_ObtainWrite(&cm_scacheLock);
400                 cm_ReleaseSCacheNoLock(scp);
401
402                 // take the scp out of the hash
403                 for (lscpp = &cm_data.scacheHashTablep[hash], tscp = cm_data.scacheHashTablep[hash]; 
404                      tscp; 
405                      lscpp = &tscp->nextp, tscp = tscp->nextp) {
406                     if (tscp == scp) {
407                         *lscpp = scp->nextp;
408                         scp->nextp = NULL;
409                         lock_ObtainWrite(&scp->rw);
410                         scp->flags &= ~CM_SCACHEFLAG_INHASH;
411                         lock_ReleaseWrite(&scp->rw);
412                         break;
413                     }
414                 }
415             }
416         }
417         cm_SetFid(&aFid, AFS_FAKE_ROOT_CELL_ID, AFS_FAKE_ROOT_VOL_ID, aFid.vnode + 1, 1);
418     }
419     lock_ReleaseWrite(&cm_scacheLock);
420     osi_Log0(afsd_logp,"\tall old scp cleared!");
421
422     // we must free the memory that was allocated in the prev
423     // cm_InitLocalMountPoints call
424     osi_Log0(afsd_logp,"Removing old localmountpoints...  ");
425     free(cm_localMountPoints);
426     osi_Log0(afsd_logp,"\tall old localmountpoints cleared!");
427
428     // now re-init the localmountpoints
429     osi_Log0(afsd_logp,"Creating new localmountpoints...  ");
430     cm_InitLocalMountPoints();
431     osi_Log0(afsd_logp,"\tcreated new set of localmountpoints!");
432
433     // then we re-create that dir
434     osi_Log0(afsd_logp,"Creating new fakedir...  ");
435     cm_InitFakeRootDir();
436     osi_Log0(afsd_logp,"\t\tcreated new fakedir!");
437
438     lock_ReleaseMutex(&cm_Freelance_Lock);
439
440     osi_Log0(afsd_logp,"----- freelance reinit complete -----");
441     return 0;
442 }
443
444
445 // yj: open up the registry and read all the local mount 
446 // points that are stored there. Part of the initialization
447 // process for the freelance client.
448 /* to be called while holding freelance lock unless during init. */
449 long cm_InitLocalMountPoints() {
450     FILE *fp;
451     int i;
452     char line[512];
453     char*t, *t2;
454     cm_localMountPoint_t* aLocalMountPoint;
455     char hdir[260];
456     long code;
457     char rootCellName[256];
458     HKEY hkFreelance = 0, hkFreelanceSymlinks = 0;
459     DWORD dwType, dwSize;
460     DWORD dwMountPoints = 0;
461     DWORD dwIndex;
462     DWORD dwSymlinks = 0;
463     FILETIME ftLastWriteTime;
464
465     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
466                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
467                       0,
468                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
469                       &hkFreelance) == ERROR_SUCCESS) {
470
471         RegQueryInfoKey( hkFreelance,
472                          NULL,  /* lpClass */
473                          NULL,  /* lpcClass */
474                          NULL,  /* lpReserved */
475                          NULL,  /* lpcSubKeys */
476                          NULL,  /* lpcMaxSubKeyLen */
477                          NULL,  /* lpcMaxClassLen */
478                          &dwMountPoints, /* lpcValues */
479                          NULL,  /* lpcMaxValueNameLen */
480                          NULL,  /* lpcMaxValueLen */
481                          NULL,  /* lpcbSecurityDescriptor */
482                          &ftLastWriteTime /* lpftLastWriteTime */
483                          );
484
485         smb_UnixTimeFromLargeSearchTime(&FakeFreelanceModTime, &ftLastWriteTime);
486
487         if ( dwMountPoints == 0 ) {
488             rootCellName[0] = '.';
489             code = cm_GetRootCellName(&rootCellName[1]);
490             if (code == 0) {
491                 cm_FreelanceAddMount(&rootCellName[1], &rootCellName[1], "root.cell.", 0, NULL);
492                 cm_FreelanceAddMount(rootCellName, &rootCellName[1], "root.cell.", 1, NULL);
493                 cm_FreelanceAddMount(".root", &rootCellName[1], "root.afs.", 1, NULL);
494                 dwMountPoints = 3;
495             }
496         }
497
498         if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
499                           AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
500                           0,
501                           NULL,
502                           REG_OPTION_NON_VOLATILE,
503                           KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
504                           NULL,
505                           &hkFreelanceSymlinks,
506                           NULL) == ERROR_SUCCESS) {
507
508             RegQueryInfoKey( hkFreelanceSymlinks,
509                              NULL,  /* lpClass */
510                              NULL,  /* lpcClass */
511                              NULL,  /* lpReserved */
512                              NULL,  /* lpcSubKeys */
513                              NULL,  /* lpcMaxSubKeyLen */
514                              NULL,  /* lpcMaxClassLen */
515                              &dwSymlinks, /* lpcValues */
516                              NULL,  /* lpcMaxValueNameLen */
517                              NULL,  /* lpcMaxValueLen */
518                              NULL,  /* lpcbSecurityDescriptor */
519                              NULL   /* lpftLastWriteTime */
520                              );
521         }
522
523         // get the number of entries there are from the first line
524         // that we read
525         cm_noLocalMountPoints = dwMountPoints + dwSymlinks;
526
527         // create space to store the local mount points
528         cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
529         aLocalMountPoint = cm_localMountPoints;
530
531         // now we read n lines and parse them into local mount points
532         // where n is the number of local mount points there are, as
533         // determined above.
534         // Each line in the ini file represents 1 local mount point and 
535         // is in the format xxx#yyy:zzz, where xxx is the directory
536         // entry name, yyy is the cell name and zzz is the volume name.
537         // #yyy:zzz together make up the mount point.
538         for ( dwIndex = 0 ; dwIndex < dwMountPoints; dwIndex++ ) {
539             TCHAR szValueName[16];
540             DWORD dwValueSize = 16;
541             dwSize = sizeof(line);
542             if (RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
543                           &dwType, line, &dwSize))
544             {
545                 afsi_log("RegEnumValue(hkFreelance) failed");
546                 cm_noLocalMountPoints--;
547                 continue;
548             }
549
550             afsi_log("Mountpoint[%d] = %s",dwIndex, line);
551
552             /* find the trailing dot; null terminate after it */
553             t2 = strrchr(line, '.');
554             if (t2)
555                 *(t2+1) = '\0';
556
557             for ( t=line;*t;t++ ) {
558                 if ( !isprint(*t) ) {
559                     afsi_log("error occurred while parsing mountpoint entry [%d]: non-printable character", dwIndex);
560                     fprintf(stderr, "error occurred while parsing mountpoint entry [%d]: non-printable character", dwIndex);
561                     cm_noLocalMountPoints--;
562                     continue;
563                 }
564             }
565
566             // line is not empty, so let's parse it
567             t = strchr(line, '#');
568             if (!t)
569                 t = strchr(line, '%');
570             // make sure that there is a '#' or '%' separator in the line
571             if (!t) {
572                 afsi_log("error occurred while parsing mountpoint entry [%d]: no # or %% separator", dwIndex);
573                 fprintf(stderr, "error occurred while parsing mountpoint entry [%d]: no # or %% separator", dwIndex);
574                 cm_noLocalMountPoints--;
575                 continue;
576             }
577
578             aLocalMountPoint->fileType = CM_SCACHETYPE_MOUNTPOINT;
579             aLocalMountPoint->namep=malloc(t-line+1);
580             strncpy(aLocalMountPoint->namep, line, t-line);
581             aLocalMountPoint->namep[t-line] = '\0';
582                 
583             /* copy the mount point string */
584             aLocalMountPoint->mountPointStringp=malloc(strlen(t));
585             strncpy(aLocalMountPoint->mountPointStringp, t, strlen(t)-1);
586             aLocalMountPoint->mountPointStringp[strlen(t)-1] = '\0';
587     
588             osi_Log2(afsd_logp,"found mount point: name %s, string %s",
589                       osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
590                       osi_LogSaveString(afsd_logp,aLocalMountPoint->mountPointStringp));
591
592             aLocalMountPoint++;
593         }
594
595         for ( dwIndex = 0 ; dwIndex < dwSymlinks; dwIndex++ ) {
596             TCHAR szValueName[16];
597             DWORD dwValueSize = 16;
598             dwSize = sizeof(line);
599             if (RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
600                               &dwType, line, &dwSize))
601             {
602                 afsi_log("RegEnumValue(hkFreelanceSymlinks) failed");
603                 cm_noLocalMountPoints--;
604                 continue;
605             }
606
607             afsi_log("Symlink[%d] = %s",dwIndex, line);
608
609             /* find the trailing dot; null terminate after it */
610             t2 = strrchr(line, '.');
611             if (t2)
612                 *(t2+1) = '\0';
613
614             for ( t=line;*t;t++ ) {
615                 if ( !isprint(*t) ) {
616                     afsi_log("error occurred while parsing symlink entry [%d]: non-printable character", dwIndex);
617                     fprintf(stderr, "error occurred while parsing symlink entry [%d]: non-printable character", dwIndex);
618                     cm_noLocalMountPoints--;
619                     continue;
620                 }
621             }
622
623             // line is not empty, so let's parse it
624             t = strchr(line, ':');
625
626             // make sure that there is a ':' separator in the line
627             if (!t) {
628                 afsi_log("error occurred while parsing symlink entry [%d]: no ':' separator", dwIndex);
629                 fprintf(stderr, "error occurred while parsing symlink entry [%d]: no ':' separator", dwIndex);
630                 cm_noLocalMountPoints--;
631                 continue;
632             }
633
634             aLocalMountPoint->fileType = CM_SCACHETYPE_SYMLINK;
635             aLocalMountPoint->namep=malloc(t-line+1);
636             strncpy(aLocalMountPoint->namep, line, t-line);
637             aLocalMountPoint->namep[t-line] = '\0';
638                 
639             /* copy the symlink string */
640             aLocalMountPoint->mountPointStringp=malloc(strlen(t)-1);
641             strncpy(aLocalMountPoint->mountPointStringp, t+1, strlen(t)-2);
642             aLocalMountPoint->mountPointStringp[strlen(t)-2] = '\0';
643     
644             osi_Log2(afsd_logp,"found symlink: name %s, string %s",
645                       osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
646                       osi_LogSaveString(afsd_logp,aLocalMountPoint->mountPointStringp));
647
648             aLocalMountPoint++;
649         }
650
651         if ( hkFreelanceSymlinks )
652             RegCloseKey( hkFreelanceSymlinks );
653         RegCloseKey(hkFreelance);
654         return 0;
655     }
656
657     /* What follows is the old code to read freelance mount points 
658      * out of a text file modified to copy the data into the registry
659      */
660     cm_GetConfigDir(hdir, sizeof(hdir));
661     strcat(hdir, AFS_FREELANCE_INI);
662     // open the ini file for reading
663     fp = fopen(hdir, "r");
664     if (!fp) {
665         /* look in the Windows directory where we used to store the file */
666         GetWindowsDirectory(hdir, sizeof(hdir));
667         strcat(hdir,"\\");
668         strcat(hdir, AFS_FREELANCE_INI);
669         fp = fopen(hdir, "r");
670     }
671
672     RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
673                     AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
674                     0,
675                     NULL,
676                     REG_OPTION_NON_VOLATILE,
677                     KEY_READ|KEY_WRITE,
678                     NULL,
679                     &hkFreelance,
680                     NULL);
681     dwIndex = 0;
682
683     if (!fp) {
684         RegCloseKey(hkFreelance);
685         rootCellName[0] = '.';
686         code = cm_GetRootCellName(&rootCellName[1]);
687         if (code == 0) {
688             cm_FreelanceAddMount(&rootCellName[1], &rootCellName[1], "root.cell.", 0, NULL);
689             cm_FreelanceAddMount(rootCellName, &rootCellName[1], "root.cell.", 1, NULL);
690             cm_FreelanceAddMount(".root", &rootCellName[1], "root.afs.", 1, NULL);
691         }
692         return 0;
693     }
694
695     // we successfully opened the file
696     osi_Log0(afsd_logp,"opened afs_freelance.ini");
697         
698     // now we read the first line to see how many entries
699     // there are
700     fgets(line, sizeof(line), fp);
701
702     // if the line is empty at any point when we're reading
703     // we're screwed. report error and return.
704     if (*line==0) {
705         afsi_log("error occurred while reading afs_freelance.ini");
706         fprintf(stderr, "error occurred while reading afs_freelance.ini");
707         return -1;
708     }
709
710     // get the number of entries there are from the first line
711     // that we read
712     cm_noLocalMountPoints = atoi(line);
713
714     if (cm_noLocalMountPoints > 0) {
715         // create space to store the local mount points
716         cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
717         aLocalMountPoint = cm_localMountPoints;
718     }
719
720     // now we read n lines and parse them into local mount points
721     // where n is the number of local mount points there are, as
722     // determined above.
723     // Each line in the ini file represents 1 local mount point and 
724     // is in the format xxx#yyy:zzz, where xxx is the directory
725     // entry name, yyy is the cell name and zzz is the volume name.
726     // #yyy:zzz together make up the mount point.
727     for (i=0; i<cm_noLocalMountPoints; i++) {
728         fgets(line, sizeof(line), fp);
729         // check that the line is not empty
730         if (line[0]==0) {
731             afsi_log("error occurred while parsing entry in %s: empty line in line %d", AFS_FREELANCE_INI, i);
732             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: empty line in line %d", i);
733             return -1;
734         }
735
736         /* find the trailing dot; null terminate after it */
737         t2 = strrchr(line, '.');
738         if (t2)
739             *(t2+1) = '\0';
740
741         if ( hkFreelance ) {
742             char szIndex[16];
743             /* we are migrating to the registry */
744             sprintf(szIndex,"%d",dwIndex++);
745             dwType = REG_SZ;
746             dwSize = (DWORD)strlen(line) + 1;
747             RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
748         }
749
750         // line is not empty, so let's parse it
751         t = strchr(line, '#');
752         if (!t)
753             t = strchr(line, '%');
754         // make sure that there is a '#' or '%' separator in the line
755         if (!t) {
756             afsi_log("error occurred while parsing entry in %s: no # or %% separator in line %d", AFS_FREELANCE_INI, i);
757             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: no # or %% separator in line %d", i);
758             return -1;
759         }
760         aLocalMountPoint->namep=malloc(t-line+1);
761         memcpy(aLocalMountPoint->namep, line, t-line);
762         *(aLocalMountPoint->namep + (t-line)) = 0;
763
764         aLocalMountPoint->mountPointStringp=malloc(strlen(line) - (t-line) + 1);
765         memcpy(aLocalMountPoint->mountPointStringp, t, strlen(line)-(t-line)-1);
766         *(aLocalMountPoint->mountPointStringp + (strlen(line)-(t-line)-1)) = 0;
767
768         osi_Log2(afsd_logp,"found mount point: name %s, string %s",
769                   aLocalMountPoint->namep,
770                   aLocalMountPoint->mountPointStringp);
771
772         aLocalMountPoint++;
773     }
774     fclose(fp);
775     if ( hkFreelance ) {
776         RegCloseKey(hkFreelance);
777         DeleteFile(hdir);
778     }
779     return 0;
780 }
781
782 int cm_getNoLocalMountPoints() {
783     return cm_noLocalMountPoints;
784 }
785
786 long cm_FreelanceMountPointExists(char * filename, int prefix_ok)
787 {
788     char* cp;
789     char line[512];
790     char shortname[200];
791     int found = 0;
792     HKEY hkFreelance = 0;
793     DWORD dwType, dwSize;
794     DWORD dwMountPoints;
795     DWORD dwIndex;
796         
797     lock_ObtainMutex(&cm_Freelance_Lock);
798
799     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
800                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
801                       0,
802                       KEY_READ|KEY_QUERY_VALUE,
803                       &hkFreelance) == ERROR_SUCCESS) 
804     {
805         RegQueryInfoKey( hkFreelance,
806                          NULL,  /* lpClass */
807                          NULL,  /* lpcClass */
808                          NULL,  /* lpReserved */
809                          NULL,  /* lpcSubKeys */
810                          NULL,  /* lpcMaxSubKeyLen */
811                          NULL,  /* lpcMaxClassLen */
812                          &dwMountPoints, /* lpcValues */
813                          NULL,  /* lpcMaxValueNameLen */
814                          NULL,  /* lpcMaxValueLen */
815                          NULL,  /* lpcbSecurityDescriptor */
816                          NULL   /* lpftLastWriteTime */
817                          );
818
819         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
820             TCHAR szValueName[16];
821             DWORD dwValueSize = 16;
822             dwSize = sizeof(line);
823             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
824                           &dwType, line, &dwSize);
825
826             cp=strchr(line, '#');
827             if (!cp)
828                 cp=strchr(line, '%');
829             memcpy(shortname, line, cp-line);
830             shortname[cp-line]=0;
831
832             if (!strcmp(shortname, filename)) {
833                 found = 1;
834                 break;
835             }
836         }
837         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
838             TCHAR szValueName[16];
839             DWORD dwValueSize = 16;
840             dwSize = sizeof(line);
841             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
842                           &dwType, line, &dwSize);
843
844             cp=strchr(line, '#');
845             if (!cp)
846                 cp=strchr(line, '%');
847             memcpy(shortname, line, cp-line);
848             shortname[cp-line]=0;
849
850             if (!cm_stricmp_utf8(shortname, filename)) {
851                 found = 1;
852                 break;
853             }
854
855             if (prefix_ok && strlen(shortname) - strlen(filename) == 1 && !strncmp(shortname, filename, strlen(filename))) {
856                 found = 1;
857                 break;
858             }
859         }
860         RegCloseKey(hkFreelance);
861     }
862
863     lock_ReleaseMutex(&cm_Freelance_Lock);
864
865     return found;
866 }
867
868 long cm_FreelanceSymlinkExists(char * filename, int prefix_ok)
869 {
870     char* cp;
871     char line[512];
872     char shortname[200];
873     int found = 0;
874     HKEY hkFreelance = 0;
875     DWORD dwType, dwSize;
876     DWORD dwSymlinks;
877     DWORD dwIndex;
878         
879     lock_ObtainMutex(&cm_Freelance_Lock);
880
881     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
882                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
883                       0,
884                       KEY_READ|KEY_QUERY_VALUE,
885                       &hkFreelance) == ERROR_SUCCESS) 
886     {
887         RegQueryInfoKey( hkFreelance,
888                          NULL,  /* lpClass */
889                          NULL,  /* lpcClass */
890                          NULL,  /* lpReserved */
891                          NULL,  /* lpcSubKeys */
892                          NULL,  /* lpcMaxSubKeyLen */
893                          NULL,  /* lpcMaxClassLen */
894                          &dwSymlinks, /* lpcValues */
895                          NULL,  /* lpcMaxValueNameLen */
896                          NULL,  /* lpcMaxValueLen */
897                          NULL,  /* lpcbSecurityDescriptor */
898                          NULL   /* lpftLastWriteTime */
899                          );
900
901         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
902             TCHAR szValueName[16];
903             DWORD dwValueSize = 16;
904             dwSize = sizeof(line);
905             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
906                           &dwType, line, &dwSize);
907
908             cp=strchr(line, ':');
909             memcpy(shortname, line, cp-line);
910             shortname[cp-line]=0;
911
912             if (!strcmp(shortname, filename)) {
913                 found = 1;
914                 break;
915             }
916
917             if (prefix_ok && strlen(shortname) - strlen(filename) == 1 && !strncmp(shortname, filename, strlen(filename))) {
918                 found = 1;
919                 break;
920             }
921         }
922         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
923             TCHAR szValueName[16];
924             DWORD dwValueSize = 16;
925             dwSize = sizeof(line);
926             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
927                           &dwType, line, &dwSize);
928
929             cp=strchr(line, ':');
930             memcpy(shortname, line, cp-line);
931             shortname[cp-line]=0;
932
933             if (!cm_stricmp_utf8(shortname, filename)) {
934                 found = 1;
935                 break;
936             }
937         }
938         RegCloseKey(hkFreelance);
939     }
940
941     lock_ReleaseMutex(&cm_Freelance_Lock);
942
943     return found;
944 }
945
946 long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp)
947 {
948     FILE *fp;
949     char hfile[260];
950     char line[512];
951     char fullname[CELL_MAXNAMELEN];
952     int n;
953     int alias = 0;
954     HKEY hkFreelance = 0;
955     DWORD dwType, dwSize;
956     DWORD dwMountPoints;
957     DWORD dwIndex;
958
959     /* before adding, verify the cell name; if it is not a valid cell,
960        don't add the mount point.
961        allow partial matches as a means of poor man's alias. */
962     /* major performance issue? */
963     osi_Log4(afsd_logp,"Freelance Add Mount request: filename=%s cellname=%s volume=%s %s",
964               osi_LogSaveString(afsd_logp,filename), 
965               osi_LogSaveString(afsd_logp,cellname), 
966               osi_LogSaveString(afsd_logp,volume), 
967               rw ? "rw" : "ro");
968
969     if ( filename[0] == '\0' || cellname[0] == '\0' || volume[0] == '\0' )
970         return -1;
971
972     if (cellname[0] == '.') {
973         if (!cm_GetCell_Gen(&cellname[1], fullname, CM_FLAG_CREATE))
974             return -1;
975     } else {
976         if (!cm_GetCell_Gen(cellname, fullname, CM_FLAG_CREATE))
977             return -1;
978     }
979
980     if ( cm_FreelanceMountPointExists(filename, 0) ||
981          cm_FreelanceSymlinkExists(filename, 0) )
982         return -1;
983     
984     osi_Log1(afsd_logp,"Freelance Adding Mount for Cell: %s", 
985               osi_LogSaveString(afsd_logp,cellname));
986
987     lock_ObtainMutex(&cm_Freelance_Lock);
988
989     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
990                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
991                       0,
992                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
993                       &hkFreelance) == ERROR_SUCCESS) {
994
995         RegQueryInfoKey( hkFreelance,
996                          NULL,  /* lpClass */
997                          NULL,  /* lpcClass */
998                          NULL,  /* lpReserved */
999                          NULL,  /* lpcSubKeys */
1000                          NULL,  /* lpcMaxSubKeyLen */
1001                          NULL,  /* lpcMaxClassLen */
1002                          &dwMountPoints, /* lpcValues */
1003                          NULL,  /* lpcMaxValueNameLen */
1004                          NULL,  /* lpcMaxValueLen */
1005                          NULL,  /* lpcbSecurityDescriptor */
1006                          NULL   /* lpftLastWriteTime */
1007                          );
1008
1009         if (rw)
1010             sprintf(line, "%s%%%s:%s", filename, fullname, volume);
1011         else
1012             sprintf(line, "%s#%s:%s", filename, fullname, volume);
1013
1014         /* If we are adding a new value, there must be an unused name
1015          * within the range 0 to dwMountPoints 
1016          */
1017         for ( dwIndex = 0; dwIndex <= dwMountPoints; dwIndex++ ) {
1018             char szIndex[16];
1019             char szMount[1024];
1020
1021             dwSize = sizeof(szMount);
1022             sprintf(szIndex, "%d", dwIndex);
1023             if (RegQueryValueEx( hkFreelance, szIndex, 0, &dwType, szMount, &dwSize) != ERROR_SUCCESS) {
1024                 /* found an unused value */
1025                 dwType = REG_SZ;
1026                 dwSize = (DWORD)strlen(line) + 1;
1027                 RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
1028                 break;
1029             } else {
1030                 int len = (int)strlen(filename);
1031                 if ( dwType == REG_SZ && !strncmp(filename, szMount, len) && 
1032                      (szMount[len] == '%' || szMount[len] == '#')) {
1033                     /* Replace the existing value */
1034                     dwType = REG_SZ;
1035                     dwSize = (DWORD)strlen(line) + 1;
1036                     RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
1037                     break;
1038                 }
1039             }
1040         }
1041         RegCloseKey(hkFreelance);
1042     } else 
1043     {
1044         cm_GetConfigDir(hfile, sizeof(hfile));
1045         strcat(hfile, AFS_FREELANCE_INI);
1046         fp = fopen(hfile, "r+");
1047         if (!fp)
1048             return CM_ERROR_INVAL;
1049         fgets(line, sizeof(line), fp);
1050         n = atoi(line);
1051         n++;
1052         fseek(fp, 0, SEEK_SET);
1053         fprintf(fp, "%d", n);
1054         fseek(fp, 0, SEEK_END);
1055         if (rw)
1056             fprintf(fp, "%s%%%s:%s\n", filename, fullname, volume);
1057         else
1058             fprintf(fp, "%s#%s:%s\n", filename, fullname, volume);
1059         fclose(fp);
1060     }
1061     lock_ReleaseMutex(&cm_Freelance_Lock);
1062
1063     /* cm_reInitLocalMountPoints(); */
1064     if (fidp)
1065         cm_SetFid(fidp, fidp->cell, fidp->volume, ++cm_noLocalMountPoints, 1);
1066     cm_noteLocalMountPointChange();
1067     return 0;
1068 }
1069
1070 long cm_FreelanceRemoveMount(char *toremove)
1071 {
1072     int i, n;
1073     char* cp;
1074     char line[512];
1075     char shortname[200];
1076     char hfile[260], hfile2[260];
1077     FILE *fp1, *fp2;
1078     int found=0;
1079     HKEY hkFreelance = 0;
1080     DWORD dwType, dwSize;
1081     DWORD dwMountPoints;
1082     DWORD dwIndex;
1083
1084     lock_ObtainMutex(&cm_Freelance_Lock);
1085
1086     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
1087                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
1088                       0,
1089                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1090                       &hkFreelance) == ERROR_SUCCESS) {
1091
1092         RegQueryInfoKey( hkFreelance,
1093                          NULL,  /* lpClass */
1094                          NULL,  /* lpcClass */
1095                          NULL,  /* lpReserved */
1096                          NULL,  /* lpcSubKeys */
1097                          NULL,  /* lpcMaxSubKeyLen */
1098                          NULL,  /* lpcMaxClassLen */
1099                          &dwMountPoints, /* lpcValues */
1100                          NULL,  /* lpcMaxValueNameLen */
1101                          NULL,  /* lpcMaxValueLen */
1102                          NULL,  /* lpcbSecurityDescriptor */
1103                          NULL   /* lpftLastWriteTime */
1104                          );
1105
1106         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
1107             TCHAR szValueName[16];
1108             DWORD dwValueSize = 16;
1109             dwSize = sizeof(line);
1110             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
1111                           &dwType, line, &dwSize);
1112
1113             cp=strchr(line, '#');
1114             if (!cp)
1115                 cp=strchr(line, '%');
1116             memcpy(shortname, line, cp-line);
1117             shortname[cp-line]=0;
1118
1119             if (!strcmp(shortname, toremove)) {
1120                 RegDeleteValue( hkFreelance, szValueName );
1121                 found = 1;
1122                 break;
1123             }
1124         }
1125         RegCloseKey(hkFreelance);
1126     } else 
1127     {
1128         cm_GetConfigDir(hfile, sizeof(hfile));
1129         strcat(hfile, AFS_FREELANCE_INI);
1130         strcpy(hfile2, hfile);
1131         strcat(hfile2, "2");
1132         fp1=fopen(hfile, "r+");
1133         if (!fp1)
1134             return CM_ERROR_INVAL;
1135         fp2=fopen(hfile2, "w+");
1136         if (!fp2) {
1137             fclose(fp1);
1138             return CM_ERROR_INVAL;
1139         }
1140
1141         fgets(line, sizeof(line), fp1);
1142         n=atoi(line);
1143         fprintf(fp2, "%d\n", n-1);
1144
1145         for (i=0; i<n; i++) {
1146             fgets(line, sizeof(line), fp1);
1147             cp=strchr(line, '#');
1148             if (!cp)
1149                 cp=strchr(line, '%');
1150             memcpy(shortname, line, cp-line);
1151             shortname[cp-line]=0;
1152
1153             if (strcmp(shortname, toremove)==0) {
1154
1155             } else {
1156                 found = 1;
1157                 fputs(line, fp2);
1158             }
1159         }
1160
1161         fclose(fp1);
1162         fclose(fp2);
1163         if (found) {
1164             unlink(hfile);
1165             rename(hfile2, hfile);
1166         }
1167     }
1168     
1169     lock_ReleaseMutex(&cm_Freelance_Lock);
1170     if (found) {
1171         cm_noteLocalMountPointChange();
1172         return 0;
1173     } else
1174         return CM_ERROR_NOSUCHFILE;
1175 }
1176
1177 long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
1178 {
1179     char line[512];
1180     char fullname[CELL_MAXNAMELEN];
1181     int alias = 0;
1182     HKEY hkFreelanceSymlinks = 0;
1183     DWORD dwType, dwSize;
1184     DWORD dwSymlinks;
1185     DWORD dwIndex;
1186
1187     /* before adding, verify the filename.  If it is already in use, either as 
1188      * as mount point or a cellname, do not permit the creation of the symlink.
1189      */
1190     osi_Log2(afsd_logp,"Freelance Add Symlink request: filename=%s destination=%s",
1191               osi_LogSaveString(afsd_logp,filename), 
1192               osi_LogSaveString(afsd_logp,destination));
1193     
1194     if ( filename[0] == '\0' || destination[0] == '\0' )
1195         return CM_ERROR_INVAL;
1196
1197     fullname[0] = '\0';
1198     if (filename[0] == '.') {
1199         cm_GetCell_Gen(&filename[1], fullname, CM_FLAG_CREATE);
1200         if (cm_stricmp_utf8(&filename[1],fullname) == 0)
1201             return CM_ERROR_EXISTS;
1202     } else {
1203         cm_GetCell_Gen(filename, fullname, CM_FLAG_CREATE);
1204         if (cm_stricmp_utf8(filename,fullname) == 0)
1205             return CM_ERROR_EXISTS;
1206     }
1207
1208     if ( cm_FreelanceMountPointExists(filename, 0) ||
1209          cm_FreelanceSymlinkExists(filename, 0) )
1210         return CM_ERROR_EXISTS;
1211
1212     lock_ObtainMutex(&cm_Freelance_Lock);
1213
1214     if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
1215                         AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
1216                         0,
1217                         NULL,
1218                         REG_OPTION_NON_VOLATILE,
1219                         KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1220                         NULL,
1221                         &hkFreelanceSymlinks,
1222                         NULL) == ERROR_SUCCESS) {
1223
1224         RegQueryInfoKey( hkFreelanceSymlinks,
1225                          NULL,  /* lpClass */
1226                          NULL,  /* lpcClass */
1227                          NULL,  /* lpReserved */
1228                          NULL,  /* lpcSubKeys */
1229                          NULL,  /* lpcMaxSubKeyLen */
1230                          NULL,  /* lpcMaxClassLen */
1231                          &dwSymlinks, /* lpcValues */
1232                          NULL,  /* lpcMaxValueNameLen */
1233                          NULL,  /* lpcMaxValueLen */
1234                          NULL,  /* lpcbSecurityDescriptor */
1235                          NULL   /* lpftLastWriteTime */
1236                          );
1237
1238         sprintf(line, "%s:%s.", filename, destination);
1239
1240         /* If we are adding a new value, there must be an unused name
1241          * within the range 0 to dwSymlinks 
1242          */
1243         for ( dwIndex = 0; dwIndex <= dwSymlinks; dwIndex++ ) {
1244             char szIndex[16];
1245             char szLink[1024];
1246
1247             dwSize = sizeof(szLink);
1248             sprintf(szIndex, "%d", dwIndex);
1249             if (RegQueryValueEx( hkFreelanceSymlinks, szIndex, 0, &dwType, szLink, &dwSize) != ERROR_SUCCESS) {
1250                 /* found an unused value */
1251                 dwType = REG_SZ;
1252                 dwSize = (DWORD)strlen(line) + 1;
1253                 RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1254                 break;
1255             } else {
1256                 int len = (int)strlen(filename);
1257                 if ( dwType == REG_SZ && !strncmp(filename, szLink, len) && szLink[len] == ':') {
1258                     /* Replace the existing value */
1259                     dwType = REG_SZ;
1260                     dwSize = (DWORD)strlen(line) + 1;
1261                     RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1262                     break;
1263                 }
1264             }
1265         }
1266         RegCloseKey(hkFreelanceSymlinks);
1267     } 
1268     lock_ReleaseMutex(&cm_Freelance_Lock);
1269
1270     /* cm_reInitLocalMountPoints(); */
1271     if (fidp)
1272         cm_SetFid(fidp, fidp->cell, fidp->volume, ++cm_noLocalMountPoints, 1);
1273     cm_noteLocalMountPointChange();
1274     return 0;
1275 }
1276
1277 long cm_FreelanceRemoveSymlink(char *toremove)
1278 {
1279     char* cp;
1280     char line[512];
1281     char shortname[200];
1282     int found=0;
1283     HKEY hkFreelanceSymlinks = 0;
1284     DWORD dwType, dwSize;
1285     DWORD dwSymlinks;
1286     DWORD dwIndex;
1287
1288     lock_ObtainMutex(&cm_Freelance_Lock);
1289
1290     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
1291                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
1292                       0,
1293                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1294                       &hkFreelanceSymlinks) == ERROR_SUCCESS) {
1295
1296         RegQueryInfoKey( hkFreelanceSymlinks,
1297                          NULL,  /* lpClass */
1298                          NULL,  /* lpcClass */
1299                          NULL,  /* lpReserved */
1300                          NULL,  /* lpcSubKeys */
1301                          NULL,  /* lpcMaxSubKeyLen */
1302                          NULL,  /* lpcMaxClassLen */
1303                          &dwSymlinks, /* lpcValues */
1304                          NULL,  /* lpcMaxValueNameLen */
1305                          NULL,  /* lpcMaxValueLen */
1306                          NULL,  /* lpcbSecurityDescriptor */
1307                          NULL   /* lpftLastWriteTime */
1308                          );
1309
1310         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
1311             TCHAR szValueName[16];
1312             DWORD dwValueSize = 16;
1313             dwSize = sizeof(line);
1314             RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
1315                           &dwType, line, &dwSize);
1316
1317             cp=strchr(line, ':');
1318             memcpy(shortname, line, cp-line);
1319             shortname[cp-line]=0;
1320
1321             if (!strcmp(shortname, toremove)) {
1322                 RegDeleteValue( hkFreelanceSymlinks, szValueName );
1323                 found = 1;
1324                 break;
1325             }
1326         }
1327         RegCloseKey(hkFreelanceSymlinks);
1328     }
1329     
1330     lock_ReleaseMutex(&cm_Freelance_Lock);
1331     if (found) {
1332         cm_noteLocalMountPointChange();
1333         return 0;
1334     } else
1335         return CM_ERROR_NOSUCHFILE;
1336 }
1337 #endif /* AFS_FREELANCE_CLIENT */