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