windows-afsd-notification-20071104
[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)
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         RegCloseKey(hkFreelance);
867     }
868
869     lock_ReleaseMutex(&cm_Freelance_Lock);
870
871     return found;
872 }
873
874 long cm_FreelanceSymlinkExists(char * filename)
875 {
876     char* cp;
877     char line[512];
878     char shortname[200];
879     int found = 0;
880     HKEY hkFreelance = 0;
881     DWORD dwType, dwSize;
882     DWORD dwSymlinks;
883     DWORD dwIndex;
884         
885     lock_ObtainMutex(&cm_Freelance_Lock);
886
887     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
888                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
889                       0,
890                       KEY_READ|KEY_QUERY_VALUE,
891                       &hkFreelance) == ERROR_SUCCESS) 
892     {
893         RegQueryInfoKey( hkFreelance,
894                          NULL,  /* lpClass */
895                          NULL,  /* lpcClass */
896                          NULL,  /* lpReserved */
897                          NULL,  /* lpcSubKeys */
898                          NULL,  /* lpcMaxSubKeyLen */
899                          NULL,  /* lpcMaxClassLen */
900                          &dwSymlinks, /* lpcValues */
901                          NULL,  /* lpcMaxValueNameLen */
902                          NULL,  /* lpcMaxValueLen */
903                          NULL,  /* lpcbSecurityDescriptor */
904                          NULL   /* lpftLastWriteTime */
905                          );
906
907         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
908             TCHAR szValueName[16];
909             DWORD dwValueSize = 16;
910             dwSize = sizeof(line);
911             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
912                           &dwType, line, &dwSize);
913
914             cp=strchr(line, ':');
915             memcpy(shortname, line, cp-line);
916             shortname[cp-line]=0;
917
918             if (!strcmp(shortname, filename)) {
919                 found = 1;
920                 break;
921             }
922         }
923         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
924             TCHAR szValueName[16];
925             DWORD dwValueSize = 16;
926             dwSize = sizeof(line);
927             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
928                           &dwType, line, &dwSize);
929
930             cp=strchr(line, ':');
931             memcpy(shortname, line, cp-line);
932             shortname[cp-line]=0;
933
934             if (!stricmp(shortname, filename)) {
935                 found = 1;
936                 break;
937             }
938         }
939         RegCloseKey(hkFreelance);
940     }
941
942     lock_ReleaseMutex(&cm_Freelance_Lock);
943
944     return found;
945 }
946
947 long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp)
948 {
949     FILE *fp;
950     char hfile[120];
951     char line[512];
952     char fullname[200];
953     int n;
954     int alias = 0;
955     HKEY hkFreelance = 0;
956     DWORD dwType, dwSize;
957     DWORD dwMountPoints;
958     DWORD dwIndex;
959
960     /* before adding, verify the cell name; if it is not a valid cell,
961        don't add the mount point.
962        allow partial matches as a means of poor man's alias. */
963     /* major performance issue? */
964     osi_Log4(afsd_logp,"Freelance Add Mount request: filename=%s cellname=%s volume=%s %s",
965               osi_LogSaveString(afsd_logp,filename), 
966               osi_LogSaveString(afsd_logp,cellname), 
967               osi_LogSaveString(afsd_logp,volume), 
968               rw ? "rw" : "ro");
969
970     if ( filename[0] == '\0' || cellname[0] == '\0' || volume[0] == '\0' )
971         return -1;
972
973     if (cellname[0] == '.') {
974         if (!cm_GetCell_Gen(&cellname[1], fullname, CM_FLAG_CREATE))
975             return -1;
976     } else {
977         if (!cm_GetCell_Gen(cellname, fullname, CM_FLAG_CREATE))
978             return -1;
979     }
980
981     if ( cm_FreelanceMountPointExists(filename) ||
982          cm_FreelanceSymlinkExists(filename) )
983         return -1;
984     
985     osi_Log1(afsd_logp,"Freelance Adding Mount for Cell: %s", 
986               osi_LogSaveString(afsd_logp,cellname));
987
988     lock_ObtainMutex(&cm_Freelance_Lock);
989
990     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
991                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
992                       0,
993                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
994                       &hkFreelance) == ERROR_SUCCESS) {
995
996         RegQueryInfoKey( hkFreelance,
997                          NULL,  /* lpClass */
998                          NULL,  /* lpcClass */
999                          NULL,  /* lpReserved */
1000                          NULL,  /* lpcSubKeys */
1001                          NULL,  /* lpcMaxSubKeyLen */
1002                          NULL,  /* lpcMaxClassLen */
1003                          &dwMountPoints, /* lpcValues */
1004                          NULL,  /* lpcMaxValueNameLen */
1005                          NULL,  /* lpcMaxValueLen */
1006                          NULL,  /* lpcbSecurityDescriptor */
1007                          NULL   /* lpftLastWriteTime */
1008                          );
1009
1010         if (rw)
1011             sprintf(line, "%s%%%s:%s", filename, fullname, volume);
1012         else
1013             sprintf(line, "%s#%s:%s", filename, fullname, volume);
1014
1015         /* If we are adding a new value, there must be an unused name
1016          * within the range 0 to dwMountPoints 
1017          */
1018         for ( dwIndex = 0; dwIndex <= dwMountPoints; dwIndex++ ) {
1019             char szIndex[16];
1020             char szMount[1024];
1021
1022             dwSize = sizeof(szMount);
1023             sprintf(szIndex, "%d", dwIndex);
1024             if (RegQueryValueEx( hkFreelance, szIndex, 0, &dwType, szMount, &dwSize) != ERROR_SUCCESS) {
1025                 /* found an unused value */
1026                 dwType = REG_SZ;
1027                 dwSize = (DWORD)strlen(line) + 1;
1028                 RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
1029                 break;
1030             } else {
1031                 int len = (int)strlen(filename);
1032                 if ( dwType == REG_SZ && !strncmp(filename, szMount, len) && 
1033                      (szMount[len] == '%' || szMount[len] == '#')) {
1034                     /* Replace the existing value */
1035                     dwType = REG_SZ;
1036                     dwSize = (DWORD)strlen(line) + 1;
1037                     RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
1038                     break;
1039                 }
1040             }
1041         }
1042         RegCloseKey(hkFreelance);
1043     } else 
1044     {
1045         cm_GetConfigDir(hfile);
1046         strcat(hfile, AFS_FREELANCE_INI);
1047         fp = fopen(hfile, "r+");
1048         if (!fp)
1049             return CM_ERROR_INVAL;
1050         fgets(line, sizeof(line), fp);
1051         n = atoi(line);
1052         n++;
1053         fseek(fp, 0, SEEK_SET);
1054         fprintf(fp, "%d", n);
1055         fseek(fp, 0, SEEK_END);
1056         if (rw)
1057             fprintf(fp, "%s%%%s:%s\n", filename, fullname, volume);
1058         else
1059             fprintf(fp, "%s#%s:%s\n", filename, fullname, volume);
1060         fclose(fp);
1061     }
1062     lock_ReleaseMutex(&cm_Freelance_Lock);
1063
1064     /* cm_reInitLocalMountPoints(); */
1065     if (fidp) {
1066         fidp->unique = 1;
1067         fidp->vnode = cm_noLocalMountPoints + 1;   /* vnode value of last mt pt */
1068     }
1069     cm_noteLocalMountPointChange();
1070     return 0;
1071 }
1072
1073 long cm_FreelanceRemoveMount(char *toremove)
1074 {
1075     int i, n;
1076     char* cp;
1077     char line[512];
1078     char shortname[200];
1079     char hfile[120], hfile2[120];
1080     FILE *fp1, *fp2;
1081     int found=0;
1082     HKEY hkFreelance = 0;
1083     DWORD dwType, dwSize;
1084     DWORD dwMountPoints;
1085     DWORD dwIndex;
1086
1087     lock_ObtainMutex(&cm_Freelance_Lock);
1088
1089     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
1090                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance",
1091                       0,
1092                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1093                       &hkFreelance) == ERROR_SUCCESS) {
1094
1095         RegQueryInfoKey( hkFreelance,
1096                          NULL,  /* lpClass */
1097                          NULL,  /* lpcClass */
1098                          NULL,  /* lpReserved */
1099                          NULL,  /* lpcSubKeys */
1100                          NULL,  /* lpcMaxSubKeyLen */
1101                          NULL,  /* lpcMaxClassLen */
1102                          &dwMountPoints, /* lpcValues */
1103                          NULL,  /* lpcMaxValueNameLen */
1104                          NULL,  /* lpcMaxValueLen */
1105                          NULL,  /* lpcbSecurityDescriptor */
1106                          NULL   /* lpftLastWriteTime */
1107                          );
1108
1109         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
1110             TCHAR szValueName[16];
1111             DWORD dwValueSize = 16;
1112             dwSize = sizeof(line);
1113             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
1114                           &dwType, line, &dwSize);
1115
1116             cp=strchr(line, '#');
1117             if (!cp)
1118                 cp=strchr(line, '%');
1119             memcpy(shortname, line, cp-line);
1120             shortname[cp-line]=0;
1121
1122             if (!strcmp(shortname, toremove)) {
1123                 RegDeleteValue( hkFreelance, szValueName );
1124                 break;
1125             }
1126         }
1127         RegCloseKey(hkFreelance);
1128     } else 
1129     {
1130         cm_GetConfigDir(hfile);
1131         strcat(hfile, AFS_FREELANCE_INI);
1132         strcpy(hfile2, hfile);
1133         strcat(hfile2, "2");
1134         fp1=fopen(hfile, "r+");
1135         if (!fp1)
1136             return CM_ERROR_INVAL;
1137         fp2=fopen(hfile2, "w+");
1138         if (!fp2) {
1139             fclose(fp1);
1140             return CM_ERROR_INVAL;
1141         }
1142
1143         fgets(line, sizeof(line), fp1);
1144         n=atoi(line);
1145         fprintf(fp2, "%d\n", n-1);
1146
1147         for (i=0; i<n; i++) {
1148             fgets(line, sizeof(line), fp1);
1149             cp=strchr(line, '#');
1150             if (!cp)
1151                 cp=strchr(line, '%');
1152             memcpy(shortname, line, cp-line);
1153             shortname[cp-line]=0;
1154
1155             if (strcmp(shortname, toremove)==0) {
1156
1157             } else {
1158                 found = 1;
1159                 fputs(line, fp2);
1160             }
1161         }
1162
1163         fclose(fp1);
1164         fclose(fp2);
1165         if (!found)
1166             return CM_ERROR_NOSUCHFILE;
1167
1168         unlink(hfile);
1169         rename(hfile2, hfile);
1170     }
1171     
1172     lock_ReleaseMutex(&cm_Freelance_Lock);
1173     cm_noteLocalMountPointChange();
1174     return 0;
1175 }
1176
1177 long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
1178 {
1179     char line[512];
1180     char fullname[200];
1181     int alias = 0;
1182     HKEY hkFreelanceSymlinks = 0;
1183     DWORD dwType, dwSize;
1184     DWORD dwSymlinks;
1185     DWORD dwIndex;
1186
1187     /* before adding, verify the filename.  If it is already in use, either as 
1188      * as mount point or a cellname, do not permit the creation of the symlink.
1189      */
1190     osi_Log2(afsd_logp,"Freelance Add Symlink request: filename=%s destination=%s",
1191               osi_LogSaveString(afsd_logp,filename), 
1192               osi_LogSaveString(afsd_logp,destination));
1193     
1194     if ( filename[0] == '\0' || destination[0] == '\0' )
1195         return CM_ERROR_INVAL;
1196
1197     fullname[0] = '\0';
1198     if (filename[0] == '.') {
1199         cm_GetCell_Gen(&filename[1], fullname, CM_FLAG_CREATE);
1200         if (stricmp(&filename[1],fullname) == 0)
1201             return CM_ERROR_EXISTS;
1202     } else {
1203         cm_GetCell_Gen(filename, fullname, CM_FLAG_CREATE);
1204         if (stricmp(filename,fullname) == 0)
1205             return CM_ERROR_EXISTS;
1206     }
1207
1208     if ( cm_FreelanceMountPointExists(filename) ||
1209          cm_FreelanceSymlinkExists(filename) )
1210         return CM_ERROR_EXISTS;
1211
1212     lock_ObtainMutex(&cm_Freelance_Lock);
1213
1214     if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
1215                         AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
1216                         0,
1217                         NULL,
1218                         REG_OPTION_NON_VOLATILE,
1219                         KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1220                         NULL,
1221                         &hkFreelanceSymlinks,
1222                         NULL) == ERROR_SUCCESS) {
1223
1224         RegQueryInfoKey( hkFreelanceSymlinks,
1225                          NULL,  /* lpClass */
1226                          NULL,  /* lpcClass */
1227                          NULL,  /* lpReserved */
1228                          NULL,  /* lpcSubKeys */
1229                          NULL,  /* lpcMaxSubKeyLen */
1230                          NULL,  /* lpcMaxClassLen */
1231                          &dwSymlinks, /* lpcValues */
1232                          NULL,  /* lpcMaxValueNameLen */
1233                          NULL,  /* lpcMaxValueLen */
1234                          NULL,  /* lpcbSecurityDescriptor */
1235                          NULL   /* lpftLastWriteTime */
1236                          );
1237
1238         sprintf(line, "%s:%s.", filename, destination);
1239
1240         /* If we are adding a new value, there must be an unused name
1241          * within the range 0 to dwSymlinks 
1242          */
1243         for ( dwIndex = 0; dwIndex <= dwSymlinks; dwIndex++ ) {
1244             char szIndex[16];
1245             char szLink[1024];
1246
1247             dwSize = sizeof(szLink);
1248             sprintf(szIndex, "%d", dwIndex);
1249             if (RegQueryValueEx( hkFreelanceSymlinks, szIndex, 0, &dwType, szLink, &dwSize) != ERROR_SUCCESS) {
1250                 /* found an unused value */
1251                 dwType = REG_SZ;
1252                 dwSize = (DWORD)strlen(line) + 1;
1253                 RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1254                 break;
1255             } else {
1256                 int len = (int)strlen(filename);
1257                 if ( dwType == REG_SZ && !strncmp(filename, szLink, len) && szLink[len] == ':') {
1258                     /* Replace the existing value */
1259                     dwType = REG_SZ;
1260                     dwSize = (DWORD)strlen(line) + 1;
1261                     RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1262                     break;
1263                 }
1264             }
1265         }
1266         RegCloseKey(hkFreelanceSymlinks);
1267     } 
1268     lock_ReleaseMutex(&cm_Freelance_Lock);
1269
1270     /* cm_reInitLocalMountPoints(); */
1271     if (fidp) {
1272         fidp->unique = 1;
1273         fidp->vnode = cm_noLocalMountPoints + 1;   /* vnode value of last mt pt */
1274     }
1275     cm_noteLocalMountPointChange();
1276     return 0;
1277 }
1278
1279 long cm_FreelanceRemoveSymlink(char *toremove)
1280 {
1281     char* cp;
1282     char line[512];
1283     char shortname[200];
1284     int found=0;
1285     HKEY hkFreelanceSymlinks = 0;
1286     DWORD dwType, dwSize;
1287     DWORD dwSymlinks;
1288     DWORD dwIndex;
1289
1290     lock_ObtainMutex(&cm_Freelance_Lock);
1291
1292     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
1293                       AFSREG_CLT_OPENAFS_SUBKEY "\\Freelance\\Symlinks",
1294                       0,
1295                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1296                       &hkFreelanceSymlinks) == ERROR_SUCCESS) {
1297
1298         RegQueryInfoKey( hkFreelanceSymlinks,
1299                          NULL,  /* lpClass */
1300                          NULL,  /* lpcClass */
1301                          NULL,  /* lpReserved */
1302                          NULL,  /* lpcSubKeys */
1303                          NULL,  /* lpcMaxSubKeyLen */
1304                          NULL,  /* lpcMaxClassLen */
1305                          &dwSymlinks, /* lpcValues */
1306                          NULL,  /* lpcMaxValueNameLen */
1307                          NULL,  /* lpcMaxValueLen */
1308                          NULL,  /* lpcbSecurityDescriptor */
1309                          NULL   /* lpftLastWriteTime */
1310                          );
1311
1312         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
1313             TCHAR szValueName[16];
1314             DWORD dwValueSize = 16;
1315             dwSize = sizeof(line);
1316             RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
1317                           &dwType, line, &dwSize);
1318
1319             cp=strchr(line, ':');
1320             memcpy(shortname, line, cp-line);
1321             shortname[cp-line]=0;
1322
1323             if (!strcmp(shortname, toremove)) {
1324                 RegDeleteValue( hkFreelanceSymlinks, szValueName );
1325                 break;
1326             }
1327         }
1328         RegCloseKey(hkFreelanceSymlinks);
1329     }
1330     
1331     lock_ReleaseMutex(&cm_Freelance_Lock);
1332     cm_noteLocalMountPointChange();
1333     return 0;
1334 }
1335 #endif /* AFS_FREELANCE_CLIENT */