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