windows-dot-dir-part-two-20060906
[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 an ini file
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_assert(phandle != NULL);
160     thrd_CloseHandle(phandle);
161
162     phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceSymlinkChangeNotifier,
163                           NULL, 0, &lpid, "cm_FreelanceSymlinkChangeNotifier");
164     osi_assert(phandle != NULL);
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.hashTablep[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.hashTablep[hash], tscp = cm_data.hashTablep[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 ini file 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                 dwMountPoints = 2;
505             }
506         }
507
508         if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
509                           AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
510                           0,
511                           NULL,
512                           REG_OPTION_NON_VOLATILE,
513                           KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
514                           NULL,
515                           &hkFreelanceSymlinks,
516                           NULL) == ERROR_SUCCESS) {
517
518             RegQueryInfoKey( hkFreelanceSymlinks,
519                              NULL,  /* lpClass */
520                              NULL,  /* lpcClass */
521                              NULL,  /* lpReserved */
522                              NULL,  /* lpcSubKeys */
523                              NULL,  /* lpcMaxSubKeyLen */
524                              NULL,  /* lpcMaxClassLen */
525                              &dwSymlinks, /* lpcValues */
526                              NULL,  /* lpcMaxValueNameLen */
527                              NULL,  /* lpcMaxValueLen */
528                              NULL,  /* lpcbSecurityDescriptor */
529                              NULL   /* lpftLastWriteTime */
530                              );
531         }
532
533         // get the number of entries there are from the first line
534         // that we read
535         cm_noLocalMountPoints = dwMountPoints + dwSymlinks;
536
537         // create space to store the local mount points
538         cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
539         aLocalMountPoint = cm_localMountPoints;
540
541         // now we read n lines and parse them into local mount points
542         // where n is the number of local mount points there are, as
543         // determined above.
544         // Each line in the ini file represents 1 local mount point and 
545         // is in the format xxx#yyy:zzz, where xxx is the directory
546         // entry name, yyy is the cell name and zzz is the volume name.
547         // #yyy:zzz together make up the mount point.
548         for ( dwIndex = 0 ; dwIndex < dwMountPoints; dwIndex++ ) {
549             TCHAR szValueName[16];
550             DWORD dwValueSize = 16;
551             dwSize = sizeof(line);
552             if (RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
553                           &dwType, line, &dwSize))
554             {
555                 afsi_log("RegEnumValue(hkFreelance) failed");
556                 cm_noLocalMountPoints--;
557                 continue;
558             }
559
560             afsi_log("Mountpoint[%d] = %s",dwIndex, line);
561
562             /* find the trailing dot; null terminate after it */
563             t2 = strrchr(line, '.');
564             if (t2)
565                 *(t2+1) = '\0';
566
567             for ( t=line;*t;t++ ) {
568                 if ( !isprint(*t) ) {
569                     afsi_log("error occurred while parsing mountpoint entry [%d]: non-printable character", dwIndex);
570                     fprintf(stderr, "error occurred while parsing mountpoint entry [%d]: non-printable character", dwIndex);
571                     cm_noLocalMountPoints--;
572                     continue;
573                 }
574             }
575
576             // line is not empty, so let's parse it
577             t = strchr(line, '#');
578             if (!t)
579                 t = strchr(line, '%');
580             // make sure that there is a '#' or '%' separator in the line
581             if (!t) {
582                 afsi_log("error occurred while parsing mountpoint entry [%d]: no # or %% separator", dwIndex);
583                 fprintf(stderr, "error occurred while parsing mountpoint entry [%d]: no # or %% separator", dwIndex);
584                 cm_noLocalMountPoints--;
585                 continue;
586             }
587
588             aLocalMountPoint->fileType = CM_SCACHETYPE_MOUNTPOINT;
589             aLocalMountPoint->namep=malloc(t-line+1);
590             strncpy(aLocalMountPoint->namep, line, t-line);
591             aLocalMountPoint->namep[t-line] = '\0';
592                 
593             /* copy the mount point string */
594             aLocalMountPoint->mountPointStringp=malloc(strlen(t));
595             strncpy(aLocalMountPoint->mountPointStringp, t, strlen(t)-1);
596             aLocalMountPoint->mountPointStringp[strlen(t)-1] = '\0';
597     
598             osi_Log2(afsd_logp,"found mount point: name %s, string %s",
599                       osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
600                       osi_LogSaveString(afsd_logp,aLocalMountPoint->mountPointStringp));
601
602             aLocalMountPoint++;
603         }
604
605         for ( dwIndex = 0 ; dwIndex < dwSymlinks; dwIndex++ ) {
606             TCHAR szValueName[16];
607             DWORD dwValueSize = 16;
608             dwSize = sizeof(line);
609             if (RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
610                               &dwType, line, &dwSize))
611             {
612                 afsi_log("RegEnumValue(hkFreelanceSymlinks) failed");
613                 cm_noLocalMountPoints--;
614                 continue;
615             }
616
617             afsi_log("Symlink[%d] = %s",dwIndex, line);
618
619             /* find the trailing dot; null terminate after it */
620             t2 = strrchr(line, '.');
621             if (t2)
622                 *(t2+1) = '\0';
623
624             for ( t=line;*t;t++ ) {
625                 if ( !isprint(*t) ) {
626                     afsi_log("error occurred while parsing symlink entry [%d]: non-printable character", dwIndex);
627                     fprintf(stderr, "error occurred while parsing symlink entry [%d]: non-printable character", dwIndex);
628                     cm_noLocalMountPoints--;
629                     continue;
630                 }
631             }
632
633             // line is not empty, so let's parse it
634             t = strchr(line, ':');
635
636             // make sure that there is a ':' separator in the line
637             if (!t) {
638                 afsi_log("error occurred while parsing symlink entry [%d]: no ':' separator", dwIndex);
639                 fprintf(stderr, "error occurred while parsing symlink entry [%d]: no ':' separator", dwIndex);
640                 cm_noLocalMountPoints--;
641                 continue;
642             }
643
644             aLocalMountPoint->fileType = CM_SCACHETYPE_SYMLINK;
645             aLocalMountPoint->namep=malloc(t-line+1);
646             strncpy(aLocalMountPoint->namep, line, t-line);
647             aLocalMountPoint->namep[t-line] = '\0';
648                 
649             /* copy the symlink string */
650             aLocalMountPoint->mountPointStringp=malloc(strlen(t)-1);
651             strncpy(aLocalMountPoint->mountPointStringp, t+1, strlen(t)-2);
652             aLocalMountPoint->mountPointStringp[strlen(t)-2] = '\0';
653     
654             osi_Log2(afsd_logp,"found symlink: name %s, string %s",
655                       osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
656                       osi_LogSaveString(afsd_logp,aLocalMountPoint->mountPointStringp));
657
658             aLocalMountPoint++;
659         }
660
661         if ( hkFreelanceSymlinks )
662             RegCloseKey( hkFreelanceSymlinks );
663         RegCloseKey(hkFreelance);
664         return 0;
665     }
666
667     /* What follows is the old code to read freelance mount points 
668      * out of a text file modified to copy the data into the registry
669      */
670     cm_GetConfigDir(hdir);
671     strcat(hdir, AFS_FREELANCE_INI);
672     // open the ini file for reading
673     fp = fopen(hdir, "r");
674     if (!fp) {
675         /* look in the Windows directory where we used to store the file */
676         GetWindowsDirectory(hdir, sizeof(hdir));
677         strcat(hdir,"\\");
678         strcat(hdir, AFS_FREELANCE_INI);
679         fp = fopen(hdir, "r");
680     }
681
682     RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
683                     AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
684                     0,
685                     NULL,
686                     REG_OPTION_NON_VOLATILE,
687                     KEY_READ|KEY_WRITE,
688                     NULL,
689                     &hkFreelance,
690                     NULL);
691     dwIndex = 0;
692
693     if (!fp) {
694         RegCloseKey(hkFreelance);
695         rootCellName[0] = '.';
696         code = cm_GetRootCellName(&rootCellName[1]);
697         if (code == 0) {
698             cm_FreelanceAddMount(&rootCellName[1], &rootCellName[1], "root.cell.", 0, NULL);
699             cm_FreelanceAddMount(rootCellName, &rootCellName[1], "root.cell.", 1, NULL);
700         }
701         return 0;
702     }
703
704     // we successfully opened the file
705     osi_Log0(afsd_logp,"opened afs_freelance.ini");
706         
707     // now we read the first line to see how many entries
708     // there are
709     fgets(line, sizeof(line), fp);
710
711     // if the line is empty at any point when we're reading
712     // we're screwed. report error and return.
713     if (*line==0) {
714         afsi_log("error occurred while reading afs_freelance.ini");
715         fprintf(stderr, "error occurred while reading afs_freelance.ini");
716         return -1;
717     }
718
719     // get the number of entries there are from the first line
720     // that we read
721     cm_noLocalMountPoints = atoi(line);
722
723     if (cm_noLocalMountPoints > 0) {
724         // create space to store the local mount points
725         cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
726         aLocalMountPoint = cm_localMountPoints;
727     }
728
729     // now we read n lines and parse them into local mount points
730     // where n is the number of local mount points there are, as
731     // determined above.
732     // Each line in the ini file represents 1 local mount point and 
733     // is in the format xxx#yyy:zzz, where xxx is the directory
734     // entry name, yyy is the cell name and zzz is the volume name.
735     // #yyy:zzz together make up the mount point.
736     for (i=0; i<cm_noLocalMountPoints; i++) {
737         fgets(line, sizeof(line), fp);
738         // check that the line is not empty
739         if (line[0]==0) {
740             afsi_log("error occurred while parsing entry in %s: empty line in line %d", AFS_FREELANCE_INI, i);
741             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: empty line in line %d", i);
742             return -1;
743         }
744
745         /* find the trailing dot; null terminate after it */
746         t2 = strrchr(line, '.');
747         if (t2)
748             *(t2+1) = '\0';
749
750         if ( hkFreelance ) {
751             char szIndex[16];
752             /* we are migrating to the registry */
753             sprintf(szIndex,"%d",dwIndex++);
754             dwType = REG_SZ;
755             dwSize = (DWORD)strlen(line) + 1;
756             RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
757         }
758
759         // line is not empty, so let's parse it
760         t = strchr(line, '#');
761         if (!t)
762             t = strchr(line, '%');
763         // make sure that there is a '#' or '%' separator in the line
764         if (!t) {
765             afsi_log("error occurred while parsing entry in %s: no # or %% separator in line %d", AFS_FREELANCE_INI, i);
766             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: no # or %% separator in line %d", i);
767             return -1;
768         }
769         aLocalMountPoint->namep=malloc(t-line+1);
770         memcpy(aLocalMountPoint->namep, line, t-line);
771         *(aLocalMountPoint->namep + (t-line)) = 0;
772
773         aLocalMountPoint->mountPointStringp=malloc(strlen(line) - (t-line) + 1);
774         memcpy(aLocalMountPoint->mountPointStringp, t, strlen(line)-(t-line)-1);
775         *(aLocalMountPoint->mountPointStringp + (strlen(line)-(t-line)-1)) = 0;
776
777         osi_Log2(afsd_logp,"found mount point: name %s, string %s",
778                   aLocalMountPoint->namep,
779                   aLocalMountPoint->mountPointStringp);
780
781         aLocalMountPoint++;
782     }
783     fclose(fp);
784     if ( hkFreelance ) {
785         RegCloseKey(hkFreelance);
786         DeleteFile(hdir);
787     }
788     return 0;
789 }
790
791 int cm_getNoLocalMountPoints() {
792     return cm_noLocalMountPoints;
793 }
794
795 long cm_FreelanceMountPointExists(char * filename)
796 {
797     char* cp;
798     char line[512];
799     char shortname[200];
800     int found = 0;
801     HKEY hkFreelance = 0;
802     DWORD dwType, dwSize;
803     DWORD dwMountPoints;
804     DWORD dwIndex;
805         
806     lock_ObtainMutex(&cm_Freelance_Lock);
807
808     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
809                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
810                       0,
811                       KEY_READ|KEY_QUERY_VALUE,
812                       &hkFreelance) == ERROR_SUCCESS) 
813     {
814         RegQueryInfoKey( hkFreelance,
815                          NULL,  /* lpClass */
816                          NULL,  /* lpcClass */
817                          NULL,  /* lpReserved */
818                          NULL,  /* lpcSubKeys */
819                          NULL,  /* lpcMaxSubKeyLen */
820                          NULL,  /* lpcMaxClassLen */
821                          &dwMountPoints, /* lpcValues */
822                          NULL,  /* lpcMaxValueNameLen */
823                          NULL,  /* lpcMaxValueLen */
824                          NULL,  /* lpcbSecurityDescriptor */
825                          NULL   /* lpftLastWriteTime */
826                          );
827
828         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
829             TCHAR szValueName[16];
830             DWORD dwValueSize = 16;
831             dwSize = sizeof(line);
832             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
833                           &dwType, line, &dwSize);
834
835             cp=strchr(line, '#');
836             if (!cp)
837                 cp=strchr(line, '%');
838             memcpy(shortname, line, cp-line);
839             shortname[cp-line]=0;
840
841             if (!strcmp(shortname, filename)) {
842                 found = 1;
843                 break;
844             }
845         }
846         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
847             TCHAR szValueName[16];
848             DWORD dwValueSize = 16;
849             dwSize = sizeof(line);
850             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
851                           &dwType, line, &dwSize);
852
853             cp=strchr(line, '#');
854             if (!cp)
855                 cp=strchr(line, '%');
856             memcpy(shortname, line, cp-line);
857             shortname[cp-line]=0;
858
859             if (!stricmp(shortname, filename)) {
860                 found = 1;
861                 break;
862             }
863         }
864         RegCloseKey(hkFreelance);
865     }
866
867     lock_ReleaseMutex(&cm_Freelance_Lock);
868
869     return found;
870 }
871
872 long cm_FreelanceSymlinkExists(char * filename)
873 {
874     char* cp;
875     char line[512];
876     char shortname[200];
877     int found = 0;
878     HKEY hkFreelance = 0;
879     DWORD dwType, dwSize;
880     DWORD dwSymlinks;
881     DWORD dwIndex;
882         
883     lock_ObtainMutex(&cm_Freelance_Lock);
884
885     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
886                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
887                       0,
888                       KEY_READ|KEY_QUERY_VALUE,
889                       &hkFreelance) == ERROR_SUCCESS) 
890     {
891         RegQueryInfoKey( hkFreelance,
892                          NULL,  /* lpClass */
893                          NULL,  /* lpcClass */
894                          NULL,  /* lpReserved */
895                          NULL,  /* lpcSubKeys */
896                          NULL,  /* lpcMaxSubKeyLen */
897                          NULL,  /* lpcMaxClassLen */
898                          &dwSymlinks, /* lpcValues */
899                          NULL,  /* lpcMaxValueNameLen */
900                          NULL,  /* lpcMaxValueLen */
901                          NULL,  /* lpcbSecurityDescriptor */
902                          NULL   /* lpftLastWriteTime */
903                          );
904
905         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
906             TCHAR szValueName[16];
907             DWORD dwValueSize = 16;
908             dwSize = sizeof(line);
909             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
910                           &dwType, line, &dwSize);
911
912             cp=strchr(line, ':');
913             memcpy(shortname, line, cp-line);
914             shortname[cp-line]=0;
915
916             if (!strcmp(shortname, filename)) {
917                 found = 1;
918                 break;
919             }
920         }
921         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
922             TCHAR szValueName[16];
923             DWORD dwValueSize = 16;
924             dwSize = sizeof(line);
925             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
926                           &dwType, line, &dwSize);
927
928             cp=strchr(line, ':');
929             memcpy(shortname, line, cp-line);
930             shortname[cp-line]=0;
931
932             if (!stricmp(shortname, filename)) {
933                 found = 1;
934                 break;
935             }
936         }
937         RegCloseKey(hkFreelance);
938     }
939
940     lock_ReleaseMutex(&cm_Freelance_Lock);
941
942     return found;
943 }
944
945 long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp)
946 {
947     FILE *fp;
948     char hfile[120];
949     char line[512];
950     char fullname[200];
951     int n;
952     int alias = 0;
953     HKEY hkFreelance = 0;
954     DWORD dwType, dwSize;
955     DWORD dwMountPoints;
956     DWORD dwIndex;
957
958     /* before adding, verify the cell name; if it is not a valid cell,
959        don't add the mount point.
960        allow partial matches as a means of poor man's alias. */
961     /* major performance issue? */
962     osi_Log4(afsd_logp,"Freelance Add Mount request: filename=%s cellname=%s volume=%s %s",
963               osi_LogSaveString(afsd_logp,filename), 
964               osi_LogSaveString(afsd_logp,cellname), 
965               osi_LogSaveString(afsd_logp,volume), 
966               rw ? "rw" : "ro");
967
968     if ( filename[0] == '\0' || cellname[0] == '\0' || volume[0] == '\0' )
969         return -1;
970
971     if (cellname[0] == '.') {
972         if (!cm_GetCell_Gen(&cellname[1], fullname, CM_FLAG_CREATE))
973             return -1;
974     } else {
975         if (!cm_GetCell_Gen(cellname, fullname, CM_FLAG_CREATE))
976             return -1;
977     }
978
979     if ( cm_FreelanceMountPointExists(filename) ||
980          cm_FreelanceSymlinkExists(filename) )
981         return -1;
982     
983     osi_Log1(afsd_logp,"Freelance Adding Mount for Cell: %s", 
984               osi_LogSaveString(afsd_logp,cellname));
985
986     lock_ObtainMutex(&cm_Freelance_Lock);
987
988     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
989                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
990                       0,
991                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
992                       &hkFreelance) == ERROR_SUCCESS) {
993
994         RegQueryInfoKey( hkFreelance,
995                          NULL,  /* lpClass */
996                          NULL,  /* lpcClass */
997                          NULL,  /* lpReserved */
998                          NULL,  /* lpcSubKeys */
999                          NULL,  /* lpcMaxSubKeyLen */
1000                          NULL,  /* lpcMaxClassLen */
1001                          &dwMountPoints, /* lpcValues */
1002                          NULL,  /* lpcMaxValueNameLen */
1003                          NULL,  /* lpcMaxValueLen */
1004                          NULL,  /* lpcbSecurityDescriptor */
1005                          NULL   /* lpftLastWriteTime */
1006                          );
1007
1008         if (rw)
1009             sprintf(line, "%s%%%s:%s", filename, fullname, volume);
1010         else
1011             sprintf(line, "%s#%s:%s", filename, fullname, volume);
1012
1013         /* If we are adding a new value, there must be an unused name
1014          * within the range 0 to dwMountPoints 
1015          */
1016         for ( dwIndex = 0; dwIndex <= dwMountPoints; dwIndex++ ) {
1017             char szIndex[16];
1018             char szMount[1024];
1019
1020             dwSize = sizeof(szMount);
1021             sprintf(szIndex, "%d", dwIndex);
1022             if (RegQueryValueEx( hkFreelance, szIndex, 0, &dwType, szMount, &dwSize) != ERROR_SUCCESS) {
1023                 /* found an unused value */
1024                 dwType = REG_SZ;
1025                 dwSize = (DWORD)strlen(line) + 1;
1026                 RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
1027                 break;
1028             } else {
1029                 int len = (int)strlen(filename);
1030                 if ( dwType == REG_SZ && !strncmp(filename, szMount, len) && 
1031                      (szMount[len] == '%' || szMount[len] == '#')) {
1032                     /* Replace the existing value */
1033                     dwType = REG_SZ;
1034                     dwSize = (DWORD)strlen(line) + 1;
1035                     RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
1036                     break;
1037                 }
1038             }
1039         }
1040         RegCloseKey(hkFreelance);
1041     } else 
1042     {
1043         cm_GetConfigDir(hfile);
1044         strcat(hfile, AFS_FREELANCE_INI);
1045         fp = fopen(hfile, "r+");
1046         if (!fp)
1047             return CM_ERROR_INVAL;
1048         fgets(line, sizeof(line), fp);
1049         n = atoi(line);
1050         n++;
1051         fseek(fp, 0, SEEK_SET);
1052         fprintf(fp, "%d", n);
1053         fseek(fp, 0, SEEK_END);
1054         if (rw)
1055             fprintf(fp, "%s%%%s:%s\n", filename, fullname, volume);
1056         else
1057             fprintf(fp, "%s#%s:%s\n", filename, fullname, volume);
1058         fclose(fp);
1059     }
1060     lock_ReleaseMutex(&cm_Freelance_Lock);
1061
1062     /* cm_reInitLocalMountPoints(); */
1063     if (fidp) {
1064         fidp->unique = 1;
1065         fidp->vnode = cm_noLocalMountPoints + 1;   /* vnode value of last mt pt */
1066     }
1067     cm_noteLocalMountPointChange();
1068     return 0;
1069 }
1070
1071 long cm_FreelanceRemoveMount(char *toremove)
1072 {
1073     int i, n;
1074     char* cp;
1075     char line[512];
1076     char shortname[200];
1077     char hfile[120], hfile2[120];
1078     FILE *fp1, *fp2;
1079     int found=0;
1080     HKEY hkFreelance = 0;
1081     DWORD dwType, dwSize;
1082     DWORD dwMountPoints;
1083     DWORD dwIndex;
1084
1085     lock_ObtainMutex(&cm_Freelance_Lock);
1086
1087     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
1088                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
1089                       0,
1090                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1091                       &hkFreelance) == ERROR_SUCCESS) {
1092
1093         RegQueryInfoKey( hkFreelance,
1094                          NULL,  /* lpClass */
1095                          NULL,  /* lpcClass */
1096                          NULL,  /* lpReserved */
1097                          NULL,  /* lpcSubKeys */
1098                          NULL,  /* lpcMaxSubKeyLen */
1099                          NULL,  /* lpcMaxClassLen */
1100                          &dwMountPoints, /* lpcValues */
1101                          NULL,  /* lpcMaxValueNameLen */
1102                          NULL,  /* lpcMaxValueLen */
1103                          NULL,  /* lpcbSecurityDescriptor */
1104                          NULL   /* lpftLastWriteTime */
1105                          );
1106
1107         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
1108             TCHAR szValueName[16];
1109             DWORD dwValueSize = 16;
1110             dwSize = sizeof(line);
1111             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
1112                           &dwType, line, &dwSize);
1113
1114             cp=strchr(line, '#');
1115             if (!cp)
1116                 cp=strchr(line, '%');
1117             memcpy(shortname, line, cp-line);
1118             shortname[cp-line]=0;
1119
1120             if (!strcmp(shortname, toremove)) {
1121                 RegDeleteValue( hkFreelance, szValueName );
1122                 break;
1123             }
1124         }
1125         RegCloseKey(hkFreelance);
1126     } else 
1127     {
1128         cm_GetConfigDir(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             return CM_ERROR_NOSUCHFILE;
1165
1166         unlink(hfile);
1167         rename(hfile2, hfile);
1168     }
1169     
1170     lock_ReleaseMutex(&cm_Freelance_Lock);
1171     cm_noteLocalMountPointChange();
1172     return 0;
1173 }
1174
1175 long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
1176 {
1177     char line[512];
1178     char fullname[200];
1179     int alias = 0;
1180     HKEY hkFreelanceSymlinks = 0;
1181     DWORD dwType, dwSize;
1182     DWORD dwSymlinks;
1183     DWORD dwIndex;
1184
1185     /* before adding, verify the filename.  If it is already in use, either as 
1186      * as mount point or a cellname, do not permit the creation of the symlink.
1187      */
1188     osi_Log2(afsd_logp,"Freelance Add Symlink request: filename=%s destination=%s",
1189               osi_LogSaveString(afsd_logp,filename), 
1190               osi_LogSaveString(afsd_logp,destination));
1191     
1192     if ( filename[0] == '\0' || destination[0] == '\0' )
1193         return -1;
1194
1195     fullname[0] = '\0';
1196     if (filename[0] == '.') {
1197         cm_GetCell_Gen(&filename[1], fullname, CM_FLAG_CREATE);
1198         if (stricmp(&filename[1],fullname) == 0)
1199             return -1;
1200     } else {
1201         cm_GetCell_Gen(filename, fullname, CM_FLAG_CREATE);
1202         if (stricmp(filename,fullname) == 0)
1203             return -1;
1204     }
1205
1206     if ( cm_FreelanceMountPointExists(filename) ||
1207          cm_FreelanceSymlinkExists(filename) )
1208         return -1;
1209
1210     lock_ObtainMutex(&cm_Freelance_Lock);
1211
1212     if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
1213                         AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
1214                         0,
1215                         NULL,
1216                         REG_OPTION_NON_VOLATILE,
1217                         KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1218                         NULL,
1219                         &hkFreelanceSymlinks,
1220                         NULL) == ERROR_SUCCESS) {
1221
1222         RegQueryInfoKey( hkFreelanceSymlinks,
1223                          NULL,  /* lpClass */
1224                          NULL,  /* lpcClass */
1225                          NULL,  /* lpReserved */
1226                          NULL,  /* lpcSubKeys */
1227                          NULL,  /* lpcMaxSubKeyLen */
1228                          NULL,  /* lpcMaxClassLen */
1229                          &dwSymlinks, /* lpcValues */
1230                          NULL,  /* lpcMaxValueNameLen */
1231                          NULL,  /* lpcMaxValueLen */
1232                          NULL,  /* lpcbSecurityDescriptor */
1233                          NULL   /* lpftLastWriteTime */
1234                          );
1235
1236         sprintf(line, "%s:%s.", filename, destination);
1237
1238         /* If we are adding a new value, there must be an unused name
1239          * within the range 0 to dwSymlinks 
1240          */
1241         for ( dwIndex = 0; dwIndex <= dwSymlinks; dwIndex++ ) {
1242             char szIndex[16];
1243             char szLink[1024];
1244
1245             dwSize = sizeof(szLink);
1246             sprintf(szIndex, "%d", dwIndex);
1247             if (RegQueryValueEx( hkFreelanceSymlinks, szIndex, 0, &dwType, szLink, &dwSize) != ERROR_SUCCESS) {
1248                 /* found an unused value */
1249                 dwType = REG_SZ;
1250                 dwSize = (DWORD)strlen(line) + 1;
1251                 RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1252                 break;
1253             } else {
1254                 int len = (int)strlen(filename);
1255                 if ( dwType == REG_SZ && !strncmp(filename, szLink, len) && szLink[len] == ':') {
1256                     /* Replace the existing value */
1257                     dwType = REG_SZ;
1258                     dwSize = (DWORD)strlen(line) + 1;
1259                     RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1260                     break;
1261                 }
1262             }
1263         }
1264         RegCloseKey(hkFreelanceSymlinks);
1265     } 
1266     lock_ReleaseMutex(&cm_Freelance_Lock);
1267
1268     /* cm_reInitLocalMountPoints(); */
1269     if (fidp) {
1270         fidp->unique = 1;
1271         fidp->vnode = cm_noLocalMountPoints + 1;   /* vnode value of last mt pt */
1272     }
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                 break;
1324             }
1325         }
1326         RegCloseKey(hkFreelanceSymlinks);
1327     }
1328     
1329     lock_ReleaseMutex(&cm_Freelance_Lock);
1330     cm_noteLocalMountPointChange();
1331     return 0;
1332 }
1333 #endif /* AFS_FREELANCE_CLIENT */