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