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