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