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