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