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