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