windows-freelance-symlink-20041027
[openafs.git] / src / WINNT / afsd / cm_freelance.c
1 #include <afs/param.h>
2 #include <afs/stds.h>
3
4 #ifndef DJGPP
5 #include <windows.h>
6 #include <winreg.h>
7 #include <winsock2.h>
8 #else
9 #include <netdb.h>
10 #endif /* !DJGPP */
11 #include <stdlib.h>
12 #include <malloc.h>
13 #include <string.h>
14
15 #include <rx/rx.h>
16
17 #include "afsd.h"
18 #ifdef AFS_FREELANCE_CLIENT
19 #include "cm_freelance.h"
20 #include "stdio.h"
21
22 extern void afsi_log(char *pattern, ...);
23
24 int cm_noLocalMountPoints;
25 int cm_fakeDirSize;
26 int cm_fakeDirCallback=0;
27 int cm_fakeGettingCallback=0;
28 int cm_fakeDirVersion = 0x8;
29 cm_localMountPoint_t* cm_localMountPoints;
30 osi_mutex_t cm_Freelance_Lock;
31 int cm_localMountPointChangeFlag = 0;
32 int cm_freelanceEnabled = 0;
33 time_t FakeFreelanceModTime = 0x3b49f6e2;
34
35 void cm_InitFakeRootDir();
36
37 #if !defined(DJGPP)
38 void cm_FreelanceChangeNotifier(void * parmp) {
39     HANDLE hFreelanceChangeEvent = 0;
40     HKEY   hkFreelance = 0;
41
42     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
43                       "SOFTWARE\\OpenAFS\\Client\\Freelance",
44                       0,
45                       KEY_NOTIFY,
46                       &hkFreelance) == ERROR_SUCCESS) {
47
48         hFreelanceChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
49         if (hFreelanceChangeEvent == NULL) {
50             RegCloseKey(hkFreelance);
51             return;
52         }
53     }
54
55     while ( TRUE ) {
56     /* check hFreelanceChangeEvent to see if it is set. 
57      * if so, call cm_noteLocalMountPointChange()
58      */
59         if (RegNotifyChangeKeyValue( hkFreelance,   /* hKey */
60                                      FALSE,         /* bWatchSubtree */
61                                      REG_NOTIFY_CHANGE_LAST_SET, /* dwNotifyFilter */
62                                      hFreelanceChangeEvent, /* hEvent */
63                                      TRUE          /* fAsynchronous */
64                                      ) != ERROR_SUCCESS) {
65             RegCloseKey(hkFreelance);
66             CloseHandle(hFreelanceChangeEvent);
67             return;
68         }
69
70         if (WaitForSingleObject(hFreelanceChangeEvent, INFINITE) == WAIT_OBJECT_0)
71         {
72             cm_noteLocalMountPointChange();
73         }
74     }
75 }
76
77 void cm_FreelanceSymlinkChangeNotifier(void * parmp) {
78     HANDLE hFreelanceSymlinkChangeEvent = 0;
79     HKEY   hkFreelance = 0;
80
81     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
82                       "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
83                       0,
84                       KEY_NOTIFY,
85                       &hkFreelance) == ERROR_SUCCESS) {
86
87         hFreelanceSymlinkChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
88         if (hFreelanceSymlinkChangeEvent == NULL) {
89             RegCloseKey(hkFreelance);
90             return;
91         }
92     }
93
94     while ( TRUE ) {
95     /* check hFreelanceSymlinkChangeEvent to see if it is set. 
96      * if so, call cm_noteLocalMountPointSymlinkChange()
97      */
98         if (RegNotifyChangeKeyValue( hkFreelance,   /* hKey */
99                                      FALSE,         /* bWatchSubtree */
100                                      REG_NOTIFY_CHANGE_LAST_SET, /* dwNotifyFilter */
101                                      hFreelanceSymlinkChangeEvent, /* hEvent */
102                                      TRUE          /* fAsynchronous */
103                                      ) != ERROR_SUCCESS) {
104             RegCloseKey(hkFreelance);
105             CloseHandle(hFreelanceSymlinkChangeEvent);
106             return;
107         }
108
109         if (WaitForSingleObject(hFreelanceSymlinkChangeEvent, INFINITE) == WAIT_OBJECT_0)
110         {
111             cm_noteLocalMountPointChange();
112         }
113     }
114 }
115 #endif
116
117 void cm_InitFreelance() {
118 #if !defined(DJGPP)
119     thread_t phandle;
120     int lpid;
121 #endif
122
123     lock_InitializeMutex(&cm_Freelance_Lock, "Freelance Lock");
124
125     // yj: first we make a call to cm_initLocalMountPoints
126     // to read all the local mount points from an ini file
127     cm_InitLocalMountPoints();
128
129     // then we make a call to InitFakeRootDir to create
130     // a fake root directory based on the local mount points
131     cm_InitFakeRootDir();
132     // --- end of yj code
133
134 #if !defined(DJGPP)
135     /* Start the registry monitor */
136     phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceChangeNotifier,
137                           NULL, 0, &lpid, "cm_FreelanceChangeNotifier");
138     osi_assert(phandle != NULL);
139     thrd_CloseHandle(phandle);
140
141     phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceSymlinkChangeNotifier,
142                           NULL, 0, &lpid, "cm_FreelanceSymlinkChangeNotifier");
143     osi_assert(phandle != NULL);
144     thrd_CloseHandle(phandle);
145 #endif
146 }
147
148 /* yj: Initialization of the fake root directory */
149 /* to be called while holding freelance lock unless during init. */
150 void cm_InitFakeRootDir() {
151     int i, t1, t2;
152     char* currentPos;
153     int noChunks;
154
155     // allocate space for the fake info
156     cm_dirHeader_t fakeDirHeader;
157     cm_dirEntry_t fakeEntry;
158     cm_pageHeader_t fakePageHeader;
159
160     // i'm going to calculate how much space is needed for
161     // this fake root directory. we have these rules:
162     // 1. there are cm_noLocalMountPoints number of entries
163     // 2. each page is CM_DIR_PAGESIZE in size
164     // 3. the first 13 chunks of the first page are used for
165     //    some header stuff
166     // 4. the first chunk of all subsequent pages are used
167     //    for page header stuff
168     // 5. a max of CM_DIR_EPP entries are allowed per page
169     // 6. each entry takes 1 or more chunks, depending on 
170     //    the size of the mount point string, as determined
171     //    by cm_NameEntries
172     // 7. each chunk is CM_DIR_CHUNKSIZE bytes
173
174     int CPP = CM_DIR_PAGESIZE / CM_DIR_CHUNKSIZE;
175     int curChunk = 13;  // chunks 0 - 12 are used for header stuff
176                         // of the first page in the directory
177     int curPage = 0;
178     int curDirEntry = 0;
179     int curDirEntryInPage = 0;
180     int sizeOfCurEntry;
181     int dirSize;
182
183     /* Reserve 2 directory chunks for "." and ".." */
184     curChunk += 2;
185
186     while (curDirEntry!=cm_noLocalMountPoints) {
187         sizeOfCurEntry = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
188         if ((curChunk + sizeOfCurEntry >= CPP) ||
189              (curDirEntryInPage + 1 >= CM_DIR_EPP)) {
190             curPage++;
191             curDirEntryInPage = 0;
192             curChunk = 1;
193         }
194         curChunk += sizeOfCurEntry;
195         curDirEntry++;
196         curDirEntryInPage++;
197     }
198
199     dirSize = (curPage+1) *  CM_DIR_PAGESIZE;
200     cm_FakeRootDir = malloc(dirSize);
201     cm_fakeDirSize = dirSize;
202
203     // yj: when we get here, we've figured out how much memory we need and 
204     // allocated the appropriate space for it. we now prceed to fill
205     // it up with entries.
206     curPage = 0;
207     curDirEntry = 0;
208     curDirEntryInPage = 0;
209     curChunk = 0;
210
211     // fields in the directory entry that are unused.
212     fakeEntry.flag = 1;
213     fakeEntry.length = 0;
214     fakeEntry.next = 0;
215     fakeEntry.fid.unique = htonl(1);
216
217     // the first page is special, it uses fakeDirHeader instead of fakePageHeader
218     // we fill up the page with dirEntries that belong there and we make changes
219     // to the fakeDirHeader.header.freeBitmap along the way. Then when we're done
220     // filling up the dirEntries in this page, we copy the fakeDirHeader into 
221     // the top of the page.
222
223     // init the freeBitmap array
224     for (i=0; i<8; i++) 
225         fakeDirHeader.header.freeBitmap[i]=0;
226
227     fakeDirHeader.header.freeBitmap[0] = 0xff;
228     fakeDirHeader.header.freeBitmap[1] = 0x7f;
229
230
231     // we start counting at 13 because the 0th to 12th chunks are used for header
232     curChunk = 13;
233
234     // stick the first 2 entries "." and ".." in
235     fakeEntry.fid.unique = htonl(1);
236     fakeEntry.fid.vnode = htonl(1);
237     strcpy(fakeEntry.name, ".");
238     currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
239     memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
240     curChunk++; curDirEntryInPage++;
241     strcpy(fakeEntry.name, "..");
242     currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
243     memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
244     curChunk++; curDirEntryInPage++;
245
246     // keep putting stuff into page 0 if
247     // 1. we're not done with all entries
248     // 2. we have less than CM_DIR_EPP entries in page 0
249     // 3. we're not out of chunks in page 0
250
251     while( (curDirEntry!=cm_noLocalMountPoints) && 
252            (curDirEntryInPage < CM_DIR_EPP) &&
253            (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP)) 
254     {       
255
256         noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
257         fakeEntry.fid.vnode = htonl(curDirEntry + 2);
258         currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
259
260         memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
261         strcpy(currentPos + 12, (cm_localMountPoints+curDirEntry)->namep);
262         curDirEntry++;
263         curDirEntryInPage++;
264         for (i=0; i<noChunks; i++) {
265             t1 = (curChunk + i) / 8;
266             t2 = curChunk + i - (t1*8);
267             fakeDirHeader.header.freeBitmap[t1] |= (1 << t2);
268         }
269         curChunk+=noChunks;
270     }
271
272     // when we get here, we're done with filling in the entries for page 0
273     // copy in the header info
274
275     memcpy(cm_FakeRootDir, &fakeDirHeader, 13 * CM_DIR_CHUNKSIZE);
276
277     curPage++;
278
279     // ok, page 0's done. Move on to the next page.
280     while (curDirEntry!=cm_noLocalMountPoints) {
281         // setup a new page
282         curChunk = 1;                   // the zeroth chunk is reserved for page header
283         curDirEntryInPage = 0; 
284         for (i=0; i<8; i++) {
285             fakePageHeader.freeBitmap[i]=0;
286         }
287         fakePageHeader.freeCount = 0;
288         fakePageHeader.pgcount = 0;
289         fakePageHeader.tag = htons(1234);
290
291         // while we're on the same page...
292         while ( (curDirEntry!=cm_noLocalMountPoints) &&
293                 (curDirEntryInPage < CM_DIR_EPP) &&
294                 (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP))
295         {
296             // add an entry to this page
297
298             noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
299             fakeEntry.fid.vnode=htonl(curDirEntry+2);
300             currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
301             memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
302             strcpy(currentPos + 12, (cm_localMountPoints+curDirEntry)->namep);
303             curDirEntry++;
304             curDirEntryInPage++;
305             for (i=0; i<noChunks; i++) {
306                 t1 = (curChunk + i) / 8;
307                 t2 = curChunk + i - (t1*8);
308                 fakePageHeader.freeBitmap[t1] |= (1 << t2);
309             }
310             curChunk+=noChunks;
311         }
312         memcpy(cm_FakeRootDir + curPage * CM_DIR_PAGESIZE, &fakePageHeader, sizeof(fakePageHeader));
313
314         curPage++;
315     }
316
317     // we know the fakeDir is setup properly, so we claim that we have callback
318     osi_Log0(afsd_logp,"cm_InitFakeRootDir fakeDirCallback=1");
319     cm_fakeDirCallback=1;
320
321     // when we get here, we've set up everything! done!
322 }
323
324 int cm_FakeRootFid(cm_fid_t *fidp)
325 {
326     fidp->cell = AFS_FAKE_ROOT_CELL_ID;            /* root cell */
327     fidp->volume = AFS_FAKE_ROOT_VOL_ID;   /* root.afs ? */
328     fidp->vnode = 0x1;
329     fidp->unique = 0x1;
330     return 0;
331 }
332   
333 /* called directly from ioctl */
334 /* called while not holding freelance lock */
335 int cm_noteLocalMountPointChange() {
336     lock_ObtainMutex(&cm_Freelance_Lock);
337     cm_fakeDirVersion++;
338     cm_localMountPointChangeFlag = 1;
339     lock_ReleaseMutex(&cm_Freelance_Lock);
340     return 1;
341 }
342
343 int cm_getLocalMountPointChange() {
344     return cm_localMountPointChangeFlag;
345 }
346
347 int cm_clearLocalMountPointChange() {
348     cm_localMountPointChangeFlag = 0;
349     return 0;
350 }
351
352 int cm_reInitLocalMountPoints() {
353     cm_fid_t aFid;
354     int i, hash;
355     cm_scache_t *scp, **lscpp, *tscp;
356         
357     osi_Log0(afsd_logp,"----- freelance reinitialization starts ----- ");
358
359     // first we invalidate all the SCPs that were created
360     // for the local mount points
361
362     osi_Log0(afsd_logp,"Invalidating local mount point scp...  ");
363
364     aFid.cell = AFS_FAKE_ROOT_CELL_ID;
365     aFid.volume=AFS_FAKE_ROOT_VOL_ID;
366     aFid.unique=0x1;
367     aFid.vnode=0x2;
368
369     lock_ObtainWrite(&cm_scacheLock);
370     lock_ObtainMutex(&cm_Freelance_Lock);  /* always scache then freelance lock */
371     for (i=0; i<cm_noLocalMountPoints; i++) {
372         hash = CM_SCACHE_HASH(&aFid);
373         for (scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
374             if (scp->fid.volume == aFid.volume &&
375                  scp->fid.vnode == aFid.vnode &&
376                  scp->fid.unique == aFid.unique 
377                  ) {
378
379                 // mark the scp to be reused
380                 lock_ReleaseWrite(&cm_scacheLock);
381                 lock_ObtainMutex(&scp->mx);
382                 cm_DiscardSCache(scp);
383                 lock_ReleaseMutex(&scp->mx);
384                 cm_CallbackNotifyChange(scp);
385                 lock_ObtainWrite(&cm_scacheLock);
386                 scp->refCount--;
387
388                 // take the scp out of the hash
389                 lscpp = &cm_hashTablep[hash];
390                 for (tscp=*lscpp; tscp; lscpp = &tscp->nextp, tscp = *lscpp) {
391                     if (tscp == scp) break;
392                 }
393                 *lscpp = scp->nextp;
394                 scp->flags &= ~CM_SCACHEFLAG_INHASH;
395             }
396         }
397         aFid.vnode = aFid.vnode + 1;
398     }
399     lock_ReleaseWrite(&cm_scacheLock);
400     osi_Log0(afsd_logp,"\tall old scp cleared!");
401
402     // we must free the memory that was allocated in the prev
403     // cm_InitLocalMountPoints call
404     osi_Log0(afsd_logp,"Removing old localmountpoints...  ");
405     free(cm_localMountPoints);
406     osi_Log0(afsd_logp,"\tall old localmountpoints cleared!");
407
408     // now re-init the localmountpoints
409     osi_Log0(afsd_logp,"Creating new localmountpoints...  ");
410     cm_InitLocalMountPoints();
411     osi_Log0(afsd_logp,"\tcreated new set of localmountpoints!");
412
413     // now we have to free the memory allocated in cm_initfakerootdir
414     osi_Log0(afsd_logp,"Removing old fakedir...  ");
415     free(cm_FakeRootDir);
416     osi_Log0(afsd_logp,"\t\told fakedir removed!");
417
418     // then we re-create that dir
419     osi_Log0(afsd_logp,"Creating new fakedir...  ");
420     cm_InitFakeRootDir();
421     osi_Log0(afsd_logp,"\t\tcreated new fakedir!");
422
423     lock_ReleaseMutex(&cm_Freelance_Lock);
424
425     osi_Log0(afsd_logp,"----- freelance reinit complete -----");
426     return 0;
427 }
428
429
430 // yj: open up the ini file and read all the local mount 
431 // points that are stored there. Part of the initialization
432 // process for the freelance client.
433 /* to be called while holding freelance lock unless during init. */
434 long cm_InitLocalMountPoints() {
435     FILE *fp;
436     int i;
437     char line[512];
438     char*t, *t2;
439     cm_localMountPoint_t* aLocalMountPoint;
440     char hdir[120];
441     long code;
442     char rootCellName[256];
443 #if !defined(DJGPP)
444     HKEY hkFreelance = 0, hkFreelanceSymlinks = 0;
445     DWORD dwType, dwSize;
446     DWORD dwMountPoints = 0;
447     DWORD dwIndex;
448     DWORD dwSymlinks = 0;
449     FILETIME ftLastWriteTime;
450 #endif
451
452 #if !defined(DJGPP)
453     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
454                         "SOFTWARE\\OpenAFS\\Client\\Freelance",
455                                                 0,
456                         KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
457                         &hkFreelance) == ERROR_SUCCESS) {
458
459         RegQueryInfoKey( hkFreelance,
460                          NULL,  /* lpClass */
461                          NULL,  /* lpcClass */
462                          NULL,  /* lpReserved */
463                          NULL,  /* lpcSubKeys */
464                          NULL,  /* lpcMaxSubKeyLen */
465                          NULL,  /* lpcMaxClassLen */
466                          &dwMountPoints, /* lpcValues */
467                          NULL,  /* lpcMaxValueNameLen */
468                          NULL,  /* lpcMaxValueLen */
469                          NULL,  /* lpcbSecurityDescriptor */
470                          &ftLastWriteTime /* lpftLastWriteTime */
471                          );
472
473         smb_UnixTimeFromLargeSearchTime(&FakeFreelanceModTime, &ftLastWriteTime);
474
475         if ( dwMountPoints == 0 ) {
476             sprintf(line,"%s#%s:root.cell.\n",rootCellName,rootCellName);
477             dwType = REG_SZ;
478             dwSize = strlen(line) + 1;
479             RegSetValueEx( hkFreelance, "0", 0, dwType, line, dwSize);
480             sprintf(line,".%s%%%s:root.cell.\n",rootCellName,rootCellName);
481             dwSize = strlen(line) + 1;
482             RegSetValueEx( hkFreelance, "1", 0, dwType, line, dwSize);
483             dwMountPoints = 2;
484         }
485
486         if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
487                           "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
488                           0,
489                           NULL,
490                           REG_OPTION_NON_VOLATILE,
491                           KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
492                           NULL,
493                           &hkFreelanceSymlinks,
494                           NULL) == ERROR_SUCCESS) {
495
496             RegQueryInfoKey( hkFreelanceSymlinks,
497                              NULL,  /* lpClass */
498                              NULL,  /* lpcClass */
499                              NULL,  /* lpReserved */
500                              NULL,  /* lpcSubKeys */
501                              NULL,  /* lpcMaxSubKeyLen */
502                              NULL,  /* lpcMaxClassLen */
503                              &dwSymlinks, /* lpcValues */
504                              NULL,  /* lpcMaxValueNameLen */
505                              NULL,  /* lpcMaxValueLen */
506                              NULL,  /* lpcbSecurityDescriptor */
507                              NULL   /* lpftLastWriteTime */
508                              );
509         }
510
511         // get the number of entries there are from the first line
512         // that we read
513         cm_noLocalMountPoints = dwMountPoints + dwSymlinks;
514
515         // create space to store the local mount points
516         cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
517         aLocalMountPoint = cm_localMountPoints;
518
519         // now we read n lines and parse them into local mount points
520         // where n is the number of local mount points there are, as
521         // determined above.
522         // Each line in the ini file represents 1 local mount point and 
523         // is in the format xxx#yyy:zzz, where xxx is the directory
524         // entry name, yyy is the cell name and zzz is the volume name.
525         // #yyy:zzz together make up the mount point.
526         for ( dwIndex = 0 ; dwIndex < dwMountPoints; dwIndex++ ) {
527             TCHAR szValueName[16];
528             DWORD dwValueSize = 16;
529             dwSize = sizeof(line);
530             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
531                           &dwType, line, &dwSize);
532
533             /* find the trailing dot; null terminate after it */
534             t2 = strrchr(line, '.');
535             if (t2)
536                 *(t2+1) = '\0';
537
538             // line is not empty, so let's parse it
539             t = strchr(line, '#');
540             if (!t)
541                 t = strchr(line, '%');
542             // make sure that there is a '#' or '%' separator in the line
543             if (!t) {
544                 afsi_log("error occurred while parsing entry in %s: no # or %% separator in line %d", AFS_FREELANCE_INI, dwIndex);
545                 fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: no # or %% separator in line %d", dwIndex);
546                 cm_noLocalMountPoints--;
547                 continue;
548             }
549
550             aLocalMountPoint->fileType = CM_SCACHETYPE_MOUNTPOINT;
551             aLocalMountPoint->namep=malloc(t-line+1);
552             strncpy(aLocalMountPoint->namep, line, t-line);
553             aLocalMountPoint->namep[t-line] = '\0';
554                 
555             /* copy the mount point string */
556             aLocalMountPoint->mountPointStringp=malloc(strlen(t));
557             strncpy(aLocalMountPoint->mountPointStringp, t, strlen(t)-1);
558             aLocalMountPoint->mountPointStringp[strlen(t)-1] = '\0';
559     
560             osi_Log2(afsd_logp,"found mount point: name %s, string %s",
561                       osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
562                       osi_LogSaveString(afsd_logp,aLocalMountPoint->mountPointStringp));
563
564             aLocalMountPoint++;
565         }
566
567         for ( dwIndex = 0 ; dwIndex < dwSymlinks; dwIndex++ ) {
568             TCHAR szValueName[16];
569             DWORD dwValueSize = 16;
570             dwSize = sizeof(line);
571             RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
572                           &dwType, line, &dwSize);
573
574             /* find the trailing dot; null terminate after it */
575             t2 = strrchr(line, '.');
576             if (t2)
577                 *(t2+1) = '\0';
578
579             // line is not empty, so let's parse it
580             t = strchr(line, ':');
581
582             // make sure that there is a ':' separator in the line
583             if (!t) {
584                 afsi_log("error occurred while parsing symlink entry: no ':' separator in line %d", dwIndex);
585                 fprintf(stderr, "error occurred while parsing symlink entry: no ':' separator in line %d", dwIndex);
586                 cm_noLocalMountPoints--;
587                 continue;
588             }
589
590             aLocalMountPoint->fileType = CM_SCACHETYPE_SYMLINK;
591             aLocalMountPoint->namep=malloc(t-line+1);
592             strncpy(aLocalMountPoint->namep, line, t-line);
593             aLocalMountPoint->namep[t-line] = '\0';
594                 
595             /* copy the symlink string */
596             aLocalMountPoint->mountPointStringp=malloc(strlen(t)-1);
597             strncpy(aLocalMountPoint->mountPointStringp, t+1, strlen(t)-2);
598             aLocalMountPoint->mountPointStringp[strlen(t)-2] = '\0';
599     
600             osi_Log2(afsd_logp,"found symlink: name %s, string %s",
601                       osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
602                       osi_LogSaveString(afsd_logp,aLocalMountPoint->mountPointStringp));
603
604             aLocalMountPoint++;
605         }
606
607         if ( hkFreelanceSymlinks )
608             RegCloseKey( hkFreelanceSymlinks );
609         RegCloseKey(hkFreelance);
610         return 0;
611     }
612 #endif
613
614     /* What follows is the old code to read freelance mount points 
615      * out of a text file modified to copy the data into the registry
616      */
617     cm_GetConfigDir(hdir);
618     strcat(hdir, AFS_FREELANCE_INI);
619     // open the ini file for reading
620     fp = fopen(hdir, "r");
621
622     // if we fail to open the file, create an empty one
623     if (!fp) {
624         fp = fopen(hdir, "w");
625         code = cm_GetRootCellName(rootCellName);
626         if (code == 0) {
627             fputs("1\n", fp);
628             fprintf(fp,"%s#%s:root.cell.\n",rootCellName,rootCellName);
629             fprintf(fp,".%s%%%s:root.cell.\n",rootCellName,rootCellName);
630             fclose(fp);
631             fp = fopen(hdir, "r");
632         } else {
633             fputs("0\n", fp);
634             fclose(fp);
635             return 0;  /* success */
636         }
637     }
638
639     // we successfully opened the file
640     osi_Log0(afsd_logp,"opened afs_freelance.ini");
641         
642 #if !defined(DJGPP)
643     RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
644                     "SOFTWARE\\OpenAFS\\Client\\Freelance",
645                     0,
646                     NULL,
647                     REG_OPTION_NON_VOLATILE,
648                     KEY_READ|KEY_WRITE,
649                     NULL,
650                     &hkFreelance,
651                     NULL);
652     dwIndex = 0;
653 #endif
654
655     // now we read the first line to see how many entries
656     // there are
657     fgets(line, sizeof(line), fp);
658
659     // if the line is empty at any point when we're reading
660     // we're screwed. report error and return.
661     if (*line==0) {
662         afsi_log("error occurred while reading afs_freelance.ini");
663         fprintf(stderr, "error occurred while reading afs_freelance.ini");
664         return -1;
665     }
666
667     // get the number of entries there are from the first line
668     // that we read
669     cm_noLocalMountPoints = atoi(line);
670
671     // create space to store the local mount points
672     cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
673     aLocalMountPoint = cm_localMountPoints;
674
675     // now we read n lines and parse them into local mount points
676     // where n is the number of local mount points there are, as
677     // determined above.
678     // Each line in the ini file represents 1 local mount point and 
679     // is in the format xxx#yyy:zzz, where xxx is the directory
680     // entry name, yyy is the cell name and zzz is the volume name.
681     // #yyy:zzz together make up the mount point.
682     for (i=0; i<cm_noLocalMountPoints; i++) {
683         fgets(line, sizeof(line), fp);
684         // check that the line is not empty
685         if (line[0]==0) {
686             afsi_log("error occurred while parsing entry in %s: empty line in line %d", AFS_FREELANCE_INI, i);
687             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: empty line in line %d", i);
688             return -1;
689         }
690
691 #if !defined(DJGPP)
692         if ( hkFreelance ) {
693             char szIndex[16];
694             /* we are migrating to the registry */
695             sprintf(szIndex,"%d",dwIndex++);
696             dwType = REG_SZ;
697             dwSize = strlen(line) + 1;
698             RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
699         }
700 #endif 
701
702         // line is not empty, so let's parse it
703         t = strchr(line, '#');
704         if (!t)
705             t = strchr(line, '%');
706         // make sure that there is a '#' or '%' separator in the line
707         if (!t) {
708             afsi_log("error occurred while parsing entry in %s: no # or %% separator in line %d", AFS_FREELANCE_INI, i);
709             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: no # or %% separator in line %d", i);
710             return -1;
711         }
712         aLocalMountPoint->namep=malloc(t-line+1);
713         memcpy(aLocalMountPoint->namep, line, t-line);
714         *(aLocalMountPoint->namep + (t-line)) = 0;
715
716         aLocalMountPoint->mountPointStringp=malloc(strlen(line) - (t-line) + 1);
717         memcpy(aLocalMountPoint->mountPointStringp, t, strlen(line)-(t-line)-2);
718         *(aLocalMountPoint->mountPointStringp + (strlen(line)-(t-line)-2)) = 0;
719
720         osi_Log2(afsd_logp,"found mount point: name %s, string %s",
721                   aLocalMountPoint->namep,
722                   aLocalMountPoint->mountPointStringp);
723
724         aLocalMountPoint++;
725     }
726     fclose(fp);
727 #if !defined(DJGPP)
728     if ( hkFreelance ) {
729         RegCloseKey(hkFreelance);
730         DeleteFile(hdir);
731     }
732 #endif
733     return 0;
734 }
735
736 int cm_getNoLocalMountPoints() {
737     return cm_noLocalMountPoints;
738 }
739
740 long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp)
741 {
742     FILE *fp;
743     char hfile[120];
744     char line[512];
745     char fullname[200];
746     int n;
747     int alias = 0;
748 #if !defined(DJGPP)
749     HKEY hkFreelance = 0;
750     DWORD dwType, dwSize;
751     DWORD dwMountPoints;
752     DWORD dwIndex;
753 #endif
754
755     /* before adding, verify the cell name; if it is not a valid cell,
756        don't add the mount point.
757        allow partial matches as a means of poor man's alias. */
758     /* major performance issue? */
759     osi_Log4(afsd_logp,"Freelance Add Mount request: filename=%s cellname=%s volume=%s %s",
760               osi_LogSaveString(afsd_logp,filename), 
761               osi_LogSaveString(afsd_logp,cellname), 
762               osi_LogSaveString(afsd_logp,volume), 
763               rw ? "rw" : "ro");
764     if (cellname[0] == '.') {
765         if (!cm_GetCell_Gen(&cellname[1], fullname, CM_FLAG_CREATE))
766             return -1;
767     } else {
768         if (!cm_GetCell_Gen(cellname, fullname, CM_FLAG_CREATE))
769             return -1;
770     }
771     
772     osi_Log1(afsd_logp,"Freelance Adding Mount for Cell: %s", 
773               osi_LogSaveString(afsd_logp,cellname));
774
775     lock_ObtainMutex(&cm_Freelance_Lock);
776
777 #if !defined(DJGPP)
778     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
779                       "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
780                       0,
781                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
782                       &hkFreelance) == ERROR_SUCCESS) {
783
784         RegQueryInfoKey( hkFreelance,
785                          NULL,  /* lpClass */
786                          NULL,  /* lpcClass */
787                          NULL,  /* lpReserved */
788                          NULL,  /* lpcSubKeys */
789                          NULL,  /* lpcMaxSubKeyLen */
790                          NULL,  /* lpcMaxClassLen */
791                          &dwMountPoints, /* lpcValues */
792                          NULL,  /* lpcMaxValueNameLen */
793                          NULL,  /* lpcMaxValueLen */
794                          NULL,  /* lpcbSecurityDescriptor */
795                          NULL   /* lpftLastWriteTime */
796                          );
797
798         if (rw)
799             sprintf(line, "%s%%%s:%s", filename, fullname, volume);
800         else
801             sprintf(line, "%s#%s:%s", filename, fullname, volume);
802
803         /* If we are adding a new value, there must be an unused name
804          * within the range 0 to dwMountPoints 
805          */
806         for ( dwIndex = 0; dwIndex <= dwMountPoints; dwIndex++ ) {
807             char szIndex[16];
808             char szMount[1024];
809
810             dwSize = sizeof(szMount);
811             sprintf(szIndex, "%d", dwIndex);
812             if (RegQueryValueEx( hkFreelance, szIndex, 0, &dwType, szMount, &dwSize) != ERROR_SUCCESS) {
813                 /* found an unused value */
814                 dwType = REG_SZ;
815                 dwSize = strlen(line) + 1;
816                 RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
817                 break;
818             } else {
819                                 int len = strlen(filename);
820                                 if ( dwType == REG_SZ && !strncmp(filename, szMount, len) && 
821                                         (szMount[len] == '%' || szMount[len] == '#')) {
822                     /* Replace the existing value */
823                     dwType = REG_SZ;
824                     dwSize = strlen(line) + 1;
825                     RegSetValueEx( hkFreelance, szIndex, 0, dwType, line, dwSize);
826                     break;
827                 }
828             }
829         }
830         RegCloseKey(hkFreelance);
831     } else 
832 #endif
833     {
834         cm_GetConfigDir(hfile);
835         strcat(hfile, AFS_FREELANCE_INI);
836         fp = fopen(hfile, "r+");
837         if (!fp)
838             return CM_ERROR_INVAL;
839         fgets(line, sizeof(line), fp);
840         n = atoi(line);
841         n++;
842         fseek(fp, 0, SEEK_SET);
843         fprintf(fp, "%d", n);
844         fseek(fp, 0, SEEK_END);
845         if (rw)
846             fprintf(fp, "%s%%%s:%s\n", filename, fullname, volume);
847         else
848             fprintf(fp, "%s#%s:%s\n", filename, fullname, volume);
849         fclose(fp);
850     }
851     lock_ReleaseMutex(&cm_Freelance_Lock);
852
853     /* cm_reInitLocalMountPoints(); */
854     if (fidp) {
855         fidp->unique = 1;
856         fidp->vnode = cm_noLocalMountPoints + 1;   /* vnode value of last mt pt */
857     }
858     cm_noteLocalMountPointChange();
859     return 0;
860 }
861
862 long cm_FreelanceRemoveMount(char *toremove)
863 {
864     int i, n;
865     char* cp;
866     char line[512];
867     char shortname[200];
868     char hfile[120], hfile2[120];
869     FILE *fp1, *fp2;
870     int found=0;
871 #if !defined(DJGPP)
872     HKEY hkFreelance = 0;
873     DWORD dwType, dwSize;
874     DWORD dwMountPoints;
875     DWORD dwIndex;
876 #endif
877
878     lock_ObtainMutex(&cm_Freelance_Lock);
879
880
881 #if !defined(DJGPP)
882     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
883                       "SOFTWARE\\OpenAFS\\Client\\Freelance",
884                       0,
885                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
886                       &hkFreelance) == ERROR_SUCCESS) {
887
888         RegQueryInfoKey( hkFreelance,
889                          NULL,  /* lpClass */
890                          NULL,  /* lpcClass */
891                          NULL,  /* lpReserved */
892                          NULL,  /* lpcSubKeys */
893                          NULL,  /* lpcMaxSubKeyLen */
894                          NULL,  /* lpcMaxClassLen */
895                          &dwMountPoints, /* lpcValues */
896                          NULL,  /* lpcMaxValueNameLen */
897                          NULL,  /* lpcMaxValueLen */
898                          NULL,  /* lpcbSecurityDescriptor */
899                          NULL   /* lpftLastWriteTime */
900                          );
901
902         for ( dwIndex = 0; dwIndex < dwMountPoints; dwIndex++ ) {
903             TCHAR szValueName[16];
904             DWORD dwValueSize = 16;
905             dwSize = sizeof(line);
906             RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
907                           &dwType, line, &dwSize);
908
909             cp=strchr(line, '#');
910             if (!cp)
911                 cp=strchr(line, '%');
912             memcpy(shortname, line, cp-line);
913             shortname[cp-line]=0;
914
915             if (!strcmp(shortname, toremove)) {
916                 RegDeleteValue( hkFreelance, szValueName );
917                 break;
918             }
919         }
920         RegCloseKey(hkFreelance);
921     } else 
922 #endif
923     {
924         cm_GetConfigDir(hfile);
925         strcat(hfile, AFS_FREELANCE_INI);
926         strcpy(hfile2, hfile);
927         strcat(hfile2, "2");
928         fp1=fopen(hfile, "r+");
929         if (!fp1)
930             return CM_ERROR_INVAL;
931         fp2=fopen(hfile2, "w+");
932         if (!fp2) {
933             fclose(fp1);
934             return CM_ERROR_INVAL;
935         }
936
937         fgets(line, sizeof(line), fp1);
938         n=atoi(line);
939         fprintf(fp2, "%d\n", n-1);
940
941         for (i=0; i<n; i++) {
942             fgets(line, sizeof(line), fp1);
943             cp=strchr(line, '#');
944             if (!cp)
945                 cp=strchr(line, '%');
946             memcpy(shortname, line, cp-line);
947             shortname[cp-line]=0;
948
949             if (strcmp(shortname, toremove)==0) {
950
951             } else {
952                 found = 1;
953                 fputs(line, fp2);
954             }
955         }
956
957         fclose(fp1);
958         fclose(fp2);
959         if (!found)
960             return CM_ERROR_NOSUCHFILE;
961
962         unlink(hfile);
963         rename(hfile2, hfile);
964     }
965     
966     lock_ReleaseMutex(&cm_Freelance_Lock);
967     cm_noteLocalMountPointChange();
968     return 0;
969 }
970
971 long cm_FreelanceAddSymlink(char *filename, char *destination, cm_fid_t *fidp)
972 {
973     FILE *fp;
974     char hfile[120];
975     char line[512];
976     char fullname[200];
977     int n;
978     int alias = 0;
979 #if !defined(DJGPP)
980     HKEY hkFreelanceSymlinks = 0;
981     DWORD dwType, dwSize;
982     DWORD dwSymlinks;
983     DWORD dwIndex;
984 #endif
985
986     /* before adding, verify the cell name; if it is not a valid cell,
987        don't add the mount point.
988        allow partial matches as a means of poor man's alias. */
989     /* major performance issue? */
990     osi_Log2(afsd_logp,"Freelance Add Symlink request: filename=%s destination=%s",
991               osi_LogSaveString(afsd_logp,filename), 
992               osi_LogSaveString(afsd_logp,destination));
993     
994     lock_ObtainMutex(&cm_Freelance_Lock);
995
996 #if !defined(DJGPP)
997     if (RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
998                         "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
999                         0,
1000                         NULL,
1001                         REG_OPTION_NON_VOLATILE,
1002                         KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1003                         NULL,
1004                         &hkFreelanceSymlinks,
1005                         NULL) == ERROR_SUCCESS) {
1006
1007         RegQueryInfoKey( hkFreelanceSymlinks,
1008                          NULL,  /* lpClass */
1009                          NULL,  /* lpcClass */
1010                          NULL,  /* lpReserved */
1011                          NULL,  /* lpcSubKeys */
1012                          NULL,  /* lpcMaxSubKeyLen */
1013                          NULL,  /* lpcMaxClassLen */
1014                          &dwSymlinks, /* lpcValues */
1015                          NULL,  /* lpcMaxValueNameLen */
1016                          NULL,  /* lpcMaxValueLen */
1017                          NULL,  /* lpcbSecurityDescriptor */
1018                          NULL   /* lpftLastWriteTime */
1019                          );
1020
1021         sprintf(line, "%s:%s.", filename, destination);
1022
1023         /* If we are adding a new value, there must be an unused name
1024          * within the range 0 to dwSymlinks 
1025          */
1026         for ( dwIndex = 0; dwIndex <= dwSymlinks; dwIndex++ ) {
1027             char szIndex[16];
1028             char szLink[1024];
1029
1030             dwSize = sizeof(szLink);
1031             sprintf(szIndex, "%d", dwIndex);
1032             if (RegQueryValueEx( hkFreelanceSymlinks, szIndex, 0, &dwType, szLink, &dwSize) != ERROR_SUCCESS) {
1033                 /* found an unused value */
1034                 dwType = REG_SZ;
1035                 dwSize = strlen(line) + 1;
1036                 RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1037                 break;
1038             } else {
1039                                 int len = strlen(filename);
1040                                 if ( dwType == REG_SZ && !strncmp(filename, szLink, len) && szLink[len] == ':') {
1041                     /* Replace the existing value */
1042                     dwType = REG_SZ;
1043                     dwSize = strlen(line) + 1;
1044                     RegSetValueEx( hkFreelanceSymlinks, szIndex, 0, dwType, line, dwSize);
1045                     break;
1046                 }
1047             }
1048         }
1049         RegCloseKey(hkFreelanceSymlinks);
1050     } 
1051 #endif
1052     lock_ReleaseMutex(&cm_Freelance_Lock);
1053
1054     /* cm_reInitLocalMountPoints(); */
1055     if (fidp) {
1056         fidp->unique = 1;
1057         fidp->vnode = cm_noLocalMountPoints + 1;   /* vnode value of last mt pt */
1058     }
1059     cm_noteLocalMountPointChange();
1060     return 0;
1061 }
1062
1063 long cm_FreelanceRemoveSymlink(char *toremove)
1064 {
1065     int i, n;
1066     char* cp;
1067     char line[512];
1068     char shortname[200];
1069     char hfile[120], hfile2[120];
1070     FILE *fp1, *fp2;
1071     int found=0;
1072 #if !defined(DJGPP)
1073     HKEY hkFreelanceSymlinks = 0;
1074     DWORD dwType, dwSize;
1075     DWORD dwSymlinks;
1076     DWORD dwIndex;
1077 #endif
1078
1079     lock_ObtainMutex(&cm_Freelance_Lock);
1080
1081
1082 #if !defined(DJGPP)
1083     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, 
1084                       "SOFTWARE\\OpenAFS\\Client\\Freelance\\Symlinks",
1085                       0,
1086                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
1087                       &hkFreelanceSymlinks) == ERROR_SUCCESS) {
1088
1089         RegQueryInfoKey( hkFreelanceSymlinks,
1090                          NULL,  /* lpClass */
1091                          NULL,  /* lpcClass */
1092                          NULL,  /* lpReserved */
1093                          NULL,  /* lpcSubKeys */
1094                          NULL,  /* lpcMaxSubKeyLen */
1095                          NULL,  /* lpcMaxClassLen */
1096                          &dwSymlinks, /* lpcValues */
1097                          NULL,  /* lpcMaxValueNameLen */
1098                          NULL,  /* lpcMaxValueLen */
1099                          NULL,  /* lpcbSecurityDescriptor */
1100                          NULL   /* lpftLastWriteTime */
1101                          );
1102
1103         for ( dwIndex = 0; dwIndex < dwSymlinks; dwIndex++ ) {
1104             TCHAR szValueName[16];
1105             DWORD dwValueSize = 16;
1106             dwSize = sizeof(line);
1107             RegEnumValue( hkFreelanceSymlinks, dwIndex, szValueName, &dwValueSize, NULL,
1108                           &dwType, line, &dwSize);
1109
1110             cp=strchr(line, ':');
1111             memcpy(shortname, line, cp-line);
1112             shortname[cp-line]=0;
1113
1114             if (!strcmp(shortname, toremove)) {
1115                 RegDeleteValue( hkFreelanceSymlinks, szValueName );
1116                 break;
1117             }
1118         }
1119         RegCloseKey(hkFreelanceSymlinks);
1120     }
1121 #endif
1122     
1123     lock_ReleaseMutex(&cm_Freelance_Lock);
1124     cm_noteLocalMountPointChange();
1125     return 0;
1126 }
1127 #endif /* AFS_FREELANCE_CLIENT */