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