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