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