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