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