ffbcf85ae34fe42c658c2762e1dc72456b857194
[openafs.git] / src / afs / afs_dynroot.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Dynamic /afs volume support.
12  *
13  * Implements:
14  * afs_IsDynrootFid
15  * afs_GetDynrootFid
16  * afs_IsDynroot
17  * afs_RefreshDynroot
18  * afs_GetDynroot
19  * afs_PutDynroot
20  * afs_DynrootNewVnode
21  * afs_SetDynrootEnable
22  * afs_GetDynrootEnable
23  * afs_DynrootVOPRemove
24  * afs_DynrootVOPSymlink
25  *
26  */
27
28 #include <afsconfig.h>
29 #include "../afs/param.h"
30
31 #include "../afs/stds.h"
32 #include "../afs/sysincludes.h" /* Standard vendor system headers */
33 #include "../afs/afsincludes.h"
34 #include "../afs/afs_osi.h"
35 #include "../afsint/afsint.h"
36 #include "../afs/lock.h"
37
38 #include "../afs/prs_fs.h"
39 #include "../afs/dir.h"
40
41 #define AFS_DYNROOT_CELL        1
42 #define AFS_DYNROOT_VOLUME      1
43 #define AFS_DYNROOT_VNODE       1
44 #define AFS_DYNROOT_UNIQUE      1
45
46 /*
47  * Vnode numbers in dynroot are composed of a type field (upper 8 bits)
48  * and a type-specific identifier in the lower 24 bits.
49  */
50 #define VN_TYPE_CELL            0x01    /* Corresponds to a struct cell */
51 #define VN_TYPE_SYMLINK         0x02    /* User-created symlink in /afs */
52
53 #define VNUM_TO_VNTYPE(vnum)    ((vnum) >> 24)
54 #define VNUM_TO_VNID(vnum)      ((vnum) & 0x00ffffff)
55 #define VNUM_FROM_TYPEID(type, id) \
56                                 ((type) << 24 | (id))
57 #define VNUM_TO_CIDX(vnum)      (VNUM_TO_VNID(vnum) >> 2)
58 #define VNUM_TO_RW(vnum)        (VNUM_TO_VNID(vnum) >> 1 & 1)
59 #define VNUM_FROM_CIDX_RW(cidx, rw) \
60                                 VNUM_FROM_TYPEID(VN_TYPE_CELL, \
61                                                  ((cidx) << 2 | (rw) << 1))
62
63 static int afs_dynrootEnable = 0;
64
65 static afs_rwlock_t afs_dynrootDirLock;
66 /* Start of variables protected by afs_dynrootDirLock */
67 static char *afs_dynrootDir = NULL;
68 static int afs_dynrootDirLen;
69 static int afs_dynrootDirLinkcnt;
70 static int afs_dynrootCellCount;
71 static int afs_dynrootVersion = 1;
72 static int afs_dynrootVersionHigh = 1;
73 /* End of variables protected by afs_dynrootDirLock */
74
75 /* A dynamically-created symlink in a dynroot /afs */
76 struct afs_dynSymlink {
77     struct afs_dynSymlink *next;
78     int index;
79     char *name;
80     char *target;
81 };
82
83 static afs_rwlock_t afs_dynSymlinkLock;
84 /* Start of variables protected by afs_dynSymlinkLock */
85 static struct afs_dynSymlink *afs_dynSymlinkBase = NULL;
86 static int afs_dynSymlinkIndex = 0;
87 /* End of variables protected by afs_dynSymlinkLock */
88
89 /*
90  * Returns non-zero iff fid corresponds to the top of the dynroot volume.
91  */
92 int afs_IsDynrootFid(struct VenusFid *fid)
93 {
94     return
95         (afs_dynrootEnable &&
96          fid->Cell       == AFS_DYNROOT_CELL   &&
97          fid->Fid.Volume == AFS_DYNROOT_VOLUME &&
98          fid->Fid.Vnode  == AFS_DYNROOT_VNODE  &&
99          fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
100 }
101
102 /*
103  * Obtain the magic dynroot volume Fid.
104  */
105 void afs_GetDynrootFid(struct VenusFid *fid) 
106 {
107     fid->Cell       = AFS_DYNROOT_CELL;
108     fid->Fid.Volume = AFS_DYNROOT_VOLUME;
109     fid->Fid.Vnode  = AFS_DYNROOT_VNODE;
110     fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
111 }
112
113 /*
114  * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
115  */
116 int afs_IsDynroot(struct vcache *avc)
117 {
118     return afs_IsDynrootFid(&avc->fid);
119 }
120
121 /*
122  * Add directory entry by given name to a directory.  Assumes the
123  * caller has allocated the directory to be large enough to hold
124  * the necessary entry.
125  */
126 static void afs_dynroot_addDirEnt(struct DirHeader *dirHeader, 
127         int *curPageP, int *curChunkP, char *name, int vnode)
128 {
129     char *dirBase = (char *) dirHeader;
130     struct PageHeader *pageHeader;
131     struct DirEntry *dirEntry;
132     int sizeOfEntry, i, t1, t2;
133     int curPage = *curPageP;
134     int curChunk = *curChunkP;
135     int didNewPage = 0;
136
137     /*
138      * Check if we need to flip pages..  If so, init the new page.
139      */
140     sizeOfEntry = afs_dir_NameBlobs(name);
141     if (curChunk + sizeOfEntry > EPP) {
142         curPage++;
143         curChunk = 1;
144         didNewPage = 1;
145     }
146
147     pageHeader = (struct PageHeader *) (dirBase + curPage * AFS_PAGESIZE);
148     if (didNewPage) {
149         pageHeader->pgcount = 0;
150         pageHeader->tag = htons(1234);
151         pageHeader->freecount = 0;
152         pageHeader->freebitmap[0] = 0x01;
153         for (i = 1; i < EPP/8; i++)
154             pageHeader->freebitmap[i] = 0;
155
156         dirHeader->alloMap[curPage] = EPP - 1;
157     }
158
159     dirEntry = (struct DirEntry *) (pageHeader + curChunk);
160     dirEntry->flag        = 1;
161     dirEntry->length      = 0;
162     dirEntry->next        = 0;
163     dirEntry->fid.vnode   = htonl(vnode);
164     dirEntry->fid.vunique = htonl(1);
165     strcpy(dirEntry->name, name);
166
167     for (i = curChunk; i < curChunk + sizeOfEntry; i++) {
168         t1 = i / 8;
169         t2 = i % 8;
170         pageHeader->freebitmap[t1] |= (1 << t2);
171     }
172
173     /*
174      * Add the new entry to the correct hash chain.
175      */
176     i = DirHash(name);
177     dirEntry->next = dirHeader->hashTable[i];
178     dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
179
180     curChunk += sizeOfEntry;
181     dirHeader->alloMap[curPage] -= sizeOfEntry;
182
183     *curPageP = curPage;
184     *curChunkP = curChunk;
185 }
186
187 /*
188  * Regenerates the dynroot contents from the current list of
189  * cells.  Useful when the list of cells has changed due to
190  * an AFSDB lookup, for instance.
191  */
192 void afs_RefreshDynroot(void)
193 {
194     int cellidx, maxcellidx, i;
195     struct cell *c;
196     int curChunk, curPage;
197     int dirSize, sizeOfCurEntry;
198     char *newDir, *dotCell;
199     struct DirHeader *dirHeader;
200     int doFlush = 0;
201     int linkCount = 0;
202     struct afs_dynSymlink *ts;
203     int newCellCount;
204
205     /*
206      * Save afs_cellindex here, in case it changes between the
207      * two loops.
208      */
209     maxcellidx = afs_cellindex;
210
211     /*
212      * Compute the amount of space we need for the fake dir
213      */
214     curChunk = 13;
215     curPage = 0;
216
217     /* Reserve space for "." and ".." */
218     curChunk += 2;
219
220     for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
221         c = afs_GetCellByIndex(cellidx, READ_LOCK, 0 /* don't refresh */);
222         if (!c) continue;
223
224         sizeOfCurEntry = afs_dir_NameBlobs(c->cellName);
225         if (curChunk + sizeOfCurEntry > EPP) {
226             curPage++;
227             curChunk = 1;
228         }
229         curChunk += sizeOfCurEntry;
230
231         dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
232         strcpy(dotCell, ".");
233         strcat(dotCell, c->cellName);
234         sizeOfCurEntry = afs_dir_NameBlobs(dotCell);
235         if (curChunk + sizeOfCurEntry > EPP) {
236             curPage++;
237             curChunk = 1;
238         }
239         curChunk += sizeOfCurEntry;
240
241         afs_PutCell(c, READ_LOCK);
242     }
243
244     ObtainReadLock(&afs_dynSymlinkLock);
245     ts = afs_dynSymlinkBase;
246     while (ts) {
247         sizeOfCurEntry = afs_dir_NameBlobs(ts->name);
248         if (curChunk + sizeOfCurEntry > EPP) {
249             curPage++;
250             curChunk = 1;
251         }
252         curChunk += sizeOfCurEntry;
253         ts = ts->next;
254     }
255
256     dirSize = (curPage + 1) * AFS_PAGESIZE;
257     newDir = afs_osi_Alloc(dirSize);
258
259     /*
260      * Now actually construct the directory.
261      */
262     curChunk = 13;
263     curPage = 0;
264     dirHeader = (struct DirHeader *) newDir;
265
266     dirHeader->header.pgcount = 0;
267     dirHeader->header.tag = htons(1234);
268     dirHeader->header.freecount = 0;
269
270     dirHeader->header.freebitmap[0] = 0xff;
271     dirHeader->header.freebitmap[1] = 0x1f;
272     for (i = 2; i < EPP/8; i++)
273         dirHeader->header.freebitmap[i] = 0;
274     dirHeader->alloMap[0] = EPP - DHE - 1;
275     for (i = 1; i < MAXPAGES; i++)
276         dirHeader->alloMap[i] = EPP;
277     for (i = 0; i < NHASHENT; i++)
278         dirHeader->hashTable[i] = 0;
279
280     /* Install "." and ".." */
281     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
282     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
283     linkCount += 2;
284
285     for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
286         c = afs_GetCellByIndex(cellidx, READ_LOCK, 0 /* don't refresh */);
287         if (!c) continue;
288
289         dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
290         strcpy(dotCell, ".");
291         strcat(dotCell, c->cellName);
292         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
293                               c->cellName, VNUM_FROM_CIDX_RW(cellidx, 0));
294         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
295                               dotCell, VNUM_FROM_CIDX_RW(cellidx, 1));
296
297         if (!(c->states & CAlias)) linkCount += 2;
298         afs_PutCell(c, READ_LOCK);
299     }
300
301     ts = afs_dynSymlinkBase;
302     while (ts) {
303         int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
304         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
305                               ts->name, vnum);
306         ts = ts->next;
307     }
308
309     newCellCount = maxcellidx + afs_dynSymlinkIndex;
310     ReleaseReadLock(&afs_dynSymlinkLock);
311
312     ObtainWriteLock(&afs_dynrootDirLock, 549);
313     if (afs_dynrootDir) afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
314     afs_dynrootDir = newDir;
315     afs_dynrootDirLen = dirSize;
316     afs_dynrootDirLinkcnt = linkCount;
317     if (afs_dynrootCellCount != newCellCount) {
318         /*
319          * New cells/symlinks added -- bump data version, invalidate vcache.
320          */
321         afs_dynrootCellCount = newCellCount;
322         afs_dynrootVersion++;
323         afs_dynrootVersionHigh = osi_Time();
324         doFlush = 1;
325     }
326     ReleaseWriteLock(&afs_dynrootDirLock);
327
328     if (doFlush) {
329         afs_int32 retry;
330         struct vcache *tvc;
331         struct VenusFid tfid;
332
333         afs_GetDynrootFid(&tfid);
334         do {
335             retry = 0;
336             ObtainReadLock(&afs_xvcache);
337             tvc = afs_FindVCache(&tfid, &retry, 0);
338             ReleaseReadLock(&afs_xvcache);
339         } while (retry);
340         if (tvc) {
341             tvc->states &= ~(CStatd | CUnique);
342             osi_dnlc_purgedp(tvc);
343             afs_PutVCache(tvc);
344         }
345     }
346 }
347
348 /*
349  * Returns a pointer to the base of the dynroot directory in memory,
350  * length thereof, and a FetchStatus.
351  */
352 void afs_GetDynroot(char **dynrootDir, int *dynrootLen, struct AFSFetchStatus *status)
353 {
354     ObtainReadLock(&afs_dynrootDirLock);
355     if (!afs_dynrootDir) {
356         ReleaseReadLock(&afs_dynrootDirLock);
357         afs_RefreshDynroot();
358         ObtainReadLock(&afs_dynrootDirLock);
359     }
360
361     if (dynrootDir) *dynrootDir = afs_dynrootDir;
362     if (dynrootLen) *dynrootLen = afs_dynrootDirLen;
363
364     if (status) {
365         memset(status, 0, sizeof(struct AFSFetchStatus));
366         status->FileType        = Directory;
367         status->LinkCount       = afs_dynrootDirLinkcnt;
368         status->Length          = afs_dynrootDirLen;
369         status->DataVersion     = afs_dynrootVersion;
370         status->CallerAccess    = PRSFS_LOOKUP | PRSFS_READ;
371         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
372         status->UnixModeBits    = 0755;
373         status->ParentVnode     = 1;
374         status->ParentUnique    = 1;
375         status->dataVersionHigh = afs_dynrootVersionHigh;
376     }
377 }
378
379 /*
380  * Puts back the dynroot read lock.
381  */
382 void afs_PutDynroot(void)
383 {
384     ReleaseReadLock(&afs_dynrootDirLock);
385 }
386
387 /*
388  * Inform dynroot that a new vnode is being created.  Return value
389  * is non-zero if this vnode is handled by dynroot, in which case
390  * FetchStatus will be filled in.
391  */
392 int afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
393 {
394     if (!afs_dynrootEnable) return 0;
395
396     if (afs_IsDynroot(avc)) {
397         afs_GetDynroot(0, 0, status);
398         afs_PutDynroot();
399         return 1;
400     }
401
402     /*
403      * Check if this is an entry under /afs, e.g. /afs/cellname.
404      */
405     if (avc->fid.Cell       == AFS_DYNROOT_CELL &&
406         avc->fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
407
408         struct cell *c;
409         int namelen, linklen, cellidx, rw;
410
411         memset(status, 0, sizeof(struct AFSFetchStatus));
412
413         status->FileType        = SymbolicLink;
414         status->LinkCount       = 1;
415         status->DataVersion     = 1;
416         status->CallerAccess    = PRSFS_LOOKUP | PRSFS_READ;
417         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
418         status->ParentVnode     = 1;
419         status->ParentUnique    = 1;
420
421         if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_SYMLINK) {
422             struct afs_dynSymlink *ts;
423             int index = VNUM_TO_VNID(avc->fid.Fid.Vnode);
424
425             ObtainReadLock(&afs_dynSymlinkLock);
426             ts = afs_dynSymlinkBase;
427             while (ts) {
428                 if (ts->index == index) break;
429                 ts = ts->next;
430             }
431
432             if (ts) {
433                 linklen = strlen(ts->target);
434                 avc->linkData = afs_osi_Alloc(linklen + 1);
435                 strcpy(avc->linkData, ts->target);
436
437                 status->Length       = linklen;
438                 status->UnixModeBits = 0755;
439             }
440             ReleaseReadLock(&afs_dynSymlinkLock);
441
442             return ts ? 1 : 0;
443         }
444
445         if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_CELL) {
446             afs_warn("dynroot vnode inconsistency, unknown VNTYPE %d\n",
447                      VNUM_TO_VNTYPE(avc->fid.Fid.Vnode));
448             return 0;
449         }
450
451         cellidx = VNUM_TO_CIDX(avc->fid.Fid.Vnode);
452         rw = VNUM_TO_RW(avc->fid.Fid.Vnode);
453
454         c = afs_GetCellByIndex(cellidx, READ_LOCK, 1 /* refresh */);
455         if (!c) {
456             afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
457                      cellidx);
458             return 0;
459         }
460
461         if (c->states & CAlias) {
462             /*
463              * linkData needs to contain the name of the cell
464              * we're aliasing for.
465              */
466             char *realName = c->realName;
467
468             if (!realName) {
469                 afs_warn("dynroot: alias %s missing real cell name\n",
470                          c->cellName);
471                 linklen = 7;
472                 avc->linkData = afs_osi_Alloc(linklen + 1);
473                 strcpy(avc->linkData, "unknown");
474             } else {
475                 int namelen = strlen(realName);
476                 linklen = rw + namelen;
477                 avc->linkData = afs_osi_Alloc(linklen + 1);
478                 strcpy(avc->linkData, rw ? "." : "");
479                 strcat(avc->linkData, realName);
480             }
481
482             status->UnixModeBits = 0755;
483         } else {
484             /*
485              * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
486              */
487             namelen = strlen(c->cellName);
488             linklen = 1 + namelen + 10;
489             avc->linkData = afs_osi_Alloc(linklen + 1);
490             strcpy(avc->linkData, rw ? "%" : "#");
491             strcat(avc->linkData, c->cellName);
492             strcat(avc->linkData, ":root.cell");
493
494             status->UnixModeBits = 0644;
495         }
496
497         status->Length = linklen;
498         afs_PutCell(c, READ_LOCK);
499         return 1;
500     }
501
502     return 0;
503 }
504
505 /*
506  * Enable or disable dynroot.  Returns 0 if successful.
507  */
508 int afs_SetDynrootEnable(int enable)
509 {
510     afs_dynrootEnable = enable;
511     return 0;
512 }
513
514 /*
515  * Check if dynroot support is enabled.
516  */
517 int afs_GetDynrootEnable(void)
518 {
519     return afs_dynrootEnable;
520 }
521
522 /*
523  * Remove a temporary symlink entry from /afs.
524  */
525 int afs_DynrootVOPRemove(struct vcache *avc, struct AFS_UCRED *acred, char *aname)
526 {
527     struct afs_dynSymlink **tpps;
528     struct afs_dynSymlink *tps;
529     struct cell *c;
530     int found = 0;
531
532     if (acred->cr_uid)
533         return EPERM;
534
535     ObtainWriteLock(&afs_dynSymlinkLock, 97);
536     tpps = &afs_dynSymlinkBase;
537     while (*tpps) {
538         tps = *tpps;
539         if (afs_strcasecmp(aname, tps->name) == 0) {
540             afs_osi_Free(tps->name, strlen(tps->name) + 1);
541             afs_osi_Free(tps->target, strlen(tps->target) + 1);
542             *tpps = tps->next;
543             afs_osi_Free(tps, sizeof(*tps));
544             afs_dynSymlinkIndex++;
545             found = 1;
546             break;
547         }
548         tpps = &(tps->next);
549     }
550     ReleaseWriteLock(&afs_dynSymlinkLock);
551     if (found) {
552         afs_RefreshDynroot();
553         return 0;
554     }
555
556     /* Check if this is an actual cell? */
557     c = afs_FindCellByName(aname, READ_LOCK);
558     if (c) {
559         afs_PutCell(c, READ_LOCK);
560         return EROFS;
561     } else {
562         return ENOENT;
563     }
564 }
565
566 /*
567  * Create a temporary symlink entry in /afs.
568  */
569 int afs_DynrootVOPSymlink(struct vcache *avc, struct AFS_UCRED *acred, 
570         char *aname, char *atargetName)
571 {
572     struct afs_dynSymlink *tps;
573     struct cell *c;
574
575     if (acred->cr_uid)
576         return EPERM;
577
578     /* Check if it's already a cell */
579     c = afs_FindCellByName(aname, READ_LOCK);
580     if (c) {
581         afs_PutCell(c, READ_LOCK);
582         return EEXIST;
583     }
584
585     /* Check if it's already a symlink */
586     ObtainWriteLock(&afs_dynSymlinkLock, 91);
587     tps = afs_dynSymlinkBase;
588     while (tps) {
589         if (afs_strcasecmp(aname, tps->name) == 0) {
590             ReleaseWriteLock(&afs_dynSymlinkLock);
591             return EEXIST;
592         }
593         tps = tps->next;
594     }
595
596     /* Doesn't already exist -- go ahead and create it */
597     tps = afs_osi_Alloc(sizeof(*tps));
598     tps->index = afs_dynSymlinkIndex++;
599     tps->next = afs_dynSymlinkBase;
600     tps->name = afs_osi_Alloc(strlen(aname) + 1);
601     strcpy(tps->name, aname);
602     tps->target = afs_osi_Alloc(strlen(atargetName) + 1);
603     strcpy(tps->target, atargetName);
604     afs_dynSymlinkBase = tps;
605     ReleaseWriteLock(&afs_dynSymlinkLock);
606
607     afs_RefreshDynroot();
608     return 0;
609 }