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