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