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