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