afs: Introduce shutdown_dynroot()
[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_IsDynrootMountFid
16  * afs_IsDynrootAnyFid
17  * afs_GetDynrootFid
18  * afs_GetDynrootMountFid
19  * afs_IsDynroot
20  * afs_IsDynrootMount
21  * afs_IsDynrootAny
22  * afs_DynrootInvalidate
23  * afs_GetDynroot
24  * afs_PutDynroot
25  * afs_DynrootNewVnode
26  * afs_SetDynrootEnable
27  * afs_GetDynrootEnable
28  * afs_DynrootVOPRemove
29  * afs_DynrootVOPSymlink
30  *
31  */
32
33 #include <afsconfig.h>
34 #include "afs/param.h"
35
36 #include "afs/stds.h"
37 #include "afs/sysincludes.h"    /* Standard vendor system headers */
38 #include "afsincludes.h"
39 #include "afs/afs_osi.h"
40 #include "afsint.h"
41 #include "afs/lock.h"
42
43 #include "afs/prs_fs.h"
44 #include "afs/dir.h"
45 #include "afs/afs_dynroot.h"
46
47 #define AFS_DYNROOT_CELLNAME    "dynroot"
48 #define AFS_DYNROOT_VOLUME      1
49 #define AFS_DYNROOT_VNODE       1
50 #define AFS_DYNROOT_MOUNT_VNODE 3
51 #define AFS_DYNROOT_UNIQUE      1
52
53 static int afs_dynrootInit = 0;
54 static int afs_dynrootEnable = 0;
55 static int afs_dynrootCell = 0;
56
57 afs_rwlock_t afs_dynrootDirLock;
58 /* Start of variables protected by afs_dynrootDirLock */
59 static char *afs_dynrootDir = NULL;
60 static int afs_dynrootDirLen;
61 static char *afs_dynrootMountDir = NULL;
62 static int afs_dynrootMountDirLen;
63 static int afs_dynrootDirLinkcnt;
64 static int afs_dynrootDirVersion;
65 static int afs_dynrootVersion = 1;
66 static int afs_dynrootVersionHigh = 1;
67 /* End of variables protected by afs_dynrootDirLock */
68
69 /* A dynamically-created symlink in a dynroot /afs */
70 struct afs_dynSymlink {
71     struct afs_dynSymlink *next;
72     int index;
73     char *name;
74     char *target;
75 };
76
77 afs_rwlock_t afs_dynSymlinkLock;
78 /* Start of variables protected by afs_dynSymlinkLock */
79 static struct afs_dynSymlink *afs_dynSymlinkBase = NULL;
80 static int afs_dynSymlinkIndex = 0;
81 /* End of variables protected by afs_dynSymlinkLock */
82
83 /*
84  * Set up a cell for dynroot if it's not there yet.
85  */
86 static int
87 afs_dynrootCellInit(void)
88 {
89     if (!afs_dynrootCell) {
90         afs_int32 cellHosts[AFS_MAXCELLHOSTS];
91         struct cell *tc;
92         int code;
93
94         memset(cellHosts, 0, sizeof(cellHosts));
95         code =
96             afs_NewCell(AFS_DYNROOT_CELLNAME, cellHosts, CNoSUID | CNoAFSDB,
97                         NULL, 0, 0, 0);
98         if (code)
99             return code;
100         tc = afs_GetCellByName(AFS_DYNROOT_CELLNAME, READ_LOCK);
101         if (!tc)
102             return ENODEV;
103         afs_dynrootCell = tc->cellNum;
104         afs_PutCell(tc, READ_LOCK);
105     }
106
107     return 0;
108 }
109
110 /*!
111  * Returns non-zero if the volume is the dynroot volume.
112  */
113 int
114 afs_IsDynrootVolume(struct volume *v)
115 {
116     return (afs_dynrootEnable
117             && v->cell == afs_dynrootCell
118             && v->volume == AFS_DYNROOT_VOLUME);
119 }
120
121 /*
122  * Returns non-zero iff fid corresponds to the top of the dynroot volume.
123  */
124 static int
125 _afs_IsDynrootFid(struct VenusFid *fid)
126 {
127     return (fid->Cell == afs_dynrootCell
128             && fid->Fid.Volume == AFS_DYNROOT_VOLUME
129             && fid->Fid.Vnode == AFS_DYNROOT_VNODE
130             && fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
131 }
132
133 int
134 afs_IsDynrootFid(struct VenusFid *fid)
135 {
136     return (afs_dynrootEnable && _afs_IsDynrootFid(fid));
137 }
138
139 int
140 afs_IsDynrootMountFid(struct VenusFid *fid)
141 {
142     return (fid->Cell == afs_dynrootCell
143             && fid->Fid.Volume == AFS_DYNROOT_VOLUME
144             && fid->Fid.Vnode == AFS_DYNROOT_MOUNT_VNODE
145             && fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
146 }
147
148 int
149 afs_IsDynrootAnyFid(struct VenusFid *fid)
150 {
151     return (fid->Cell == afs_dynrootCell
152             && fid->Fid.Volume == AFS_DYNROOT_VOLUME);
153 }
154
155 /*
156  * Obtain the magic dynroot volume Fid.
157  */
158 void
159 afs_GetDynrootFid(struct VenusFid *fid)
160 {
161     fid->Cell = afs_dynrootCell;
162     fid->Fid.Volume = AFS_DYNROOT_VOLUME;
163     fid->Fid.Vnode = AFS_DYNROOT_VNODE;
164     fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
165 }
166
167 void
168 afs_GetDynrootMountFid(struct VenusFid *fid)
169 {
170     fid->Cell = afs_dynrootCell;
171     fid->Fid.Volume = AFS_DYNROOT_VOLUME;
172     fid->Fid.Vnode = AFS_DYNROOT_MOUNT_VNODE;
173     fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
174 }
175
176 /*
177  * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
178  */
179 int
180 afs_IsDynroot(struct vcache *avc)
181 {
182     return afs_IsDynrootFid(&avc->f.fid);
183 }
184
185 int
186 afs_IsDynrootMount(struct vcache *avc)
187 {
188     return afs_IsDynrootMountFid(&avc->f.fid);
189 }
190
191 int
192 afs_IsDynrootAny(struct vcache *avc)
193 {
194     return afs_IsDynrootAnyFid(&avc->f.fid);
195 }
196
197 /*
198  * Given the current page and chunk pointers in a directory, adjust them
199  * appropriately so that the given file name can be appended.  Used for
200  * computing the size of a directory.
201  */
202 static void
203 afs_dynroot_computeDirEnt(char *name, int *curPageP, int *curChunkP)
204 {
205     int esize;
206
207     esize = afs_dir_NameBlobs(name);
208     if (*curChunkP + esize > EPP) {
209         *curPageP += 1;
210         *curChunkP = 1;
211     }
212     *curChunkP += esize;
213 }
214
215 /*
216  * Add directory entry by given name to a directory.  Assumes the
217  * caller has allocated the directory to be large enough to hold
218  * the necessary entry.
219  */
220 static void
221 afs_dynroot_addDirEnt(struct DirHeader *dirHeader, int *curPageP,
222                       int *curChunkP, char *name, int vnode)
223 {
224     char *dirBase = (char *)dirHeader;
225     struct PageHeader *pageHeader;
226     struct DirEntry *dirEntry;
227     int sizeOfEntry, i, t1, t2;
228     int curPage = *curPageP;
229     int curChunk = *curChunkP;
230     int didNewPage = 0;
231
232     /*
233      * Check if we need to flip pages..  If so, init the new page.
234      */
235     sizeOfEntry = afs_dir_NameBlobs(name);
236     if (curChunk + sizeOfEntry > EPP) {
237         curPage++;
238         curChunk = 1;
239         didNewPage = 1;
240     }
241
242     pageHeader = (struct PageHeader *)(dirBase + curPage * AFS_PAGESIZE);
243     if (didNewPage) {
244         pageHeader->pgcount = 0;
245         pageHeader->tag = htons(1234);
246         pageHeader->freecount = 0;
247         pageHeader->freebitmap[0] = 0x01;
248         for (i = 1; i < EPP / 8; i++)
249             pageHeader->freebitmap[i] = 0;
250
251         dirHeader->alloMap[curPage] = EPP - 1;
252     }
253
254     dirEntry = (struct DirEntry *)(pageHeader + curChunk);
255     dirEntry->flag = 1;
256     dirEntry->length = 0;
257     dirEntry->next = 0;
258     dirEntry->fid.vnode = htonl(vnode);
259     dirEntry->fid.vunique = htonl(1);
260     strcpy(dirEntry->name, name);
261
262     for (i = curChunk; i < curChunk + sizeOfEntry; i++) {
263         t1 = i / 8;
264         t2 = i % 8;
265         pageHeader->freebitmap[t1] |= (1 << t2);
266     }
267
268     /*
269      * Add the new entry to the correct hash chain.
270      */
271     i = afs_dir_DirHash(name);
272     dirEntry->next = dirHeader->hashTable[i];
273     dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
274
275     curChunk += sizeOfEntry;
276     dirHeader->alloMap[curPage] -= sizeOfEntry;
277
278     *curPageP = curPage;
279     *curChunkP = curChunk;
280 }
281
282 /*
283  * Invalidate the /afs vnode for dynroot; called when the underlying
284  * directory has changed and needs to be re-read.
285  */
286 void
287 afs_DynrootInvalidate(void)
288 {
289     afs_int32 retry;
290     struct vcache *tvc;
291     struct VenusFid tfid;
292
293     if (!afs_dynrootEnable)
294         return;
295
296     ObtainWriteLock(&afs_dynrootDirLock, 687);
297     afs_dynrootVersion++;
298     afs_dynrootVersionHigh = osi_Time();
299     ReleaseWriteLock(&afs_dynrootDirLock);
300
301     afs_GetDynrootFid(&tfid);
302     do {
303         retry = 0;
304         ObtainReadLock(&afs_xvcache);
305         tvc = afs_FindVCache(&tfid, &retry, 0);
306         ReleaseReadLock(&afs_xvcache);
307     } while (retry);
308     if (tvc) {
309         afs_StaleVCacheFlags(tvc, AFS_STALEVC_NOCB, CUnique);
310         afs_PutVCache(tvc);
311     }
312 }
313
314 /*
315  * Regenerates the dynroot contents from the current list of
316  * cells.  Useful when the list of cells has changed due to
317  * an AFSDB lookup, for instance.
318  */
319 static void
320 afs_RebuildDynroot(void)
321 {
322     int cellidx, maxcellidx, i;
323     int aliasidx, maxaliasidx;
324     struct cell *c;
325     struct cell_alias *ca;
326     int curChunk, curPage;
327     int dirSize, dotLen;
328     char *newDir, *dotCell;
329     struct DirHeader *dirHeader;
330     int linkCount = 0;
331     struct afs_dynSymlink *ts;
332     int newVersion;
333
334     ObtainReadLock(&afs_dynrootDirLock);
335     newVersion = afs_dynrootVersion;
336     ReleaseReadLock(&afs_dynrootDirLock);
337
338     /*
339      * Compute the amount of space we need for the fake dir
340      */
341     curChunk = 13;
342     curPage = 0;
343
344     /* Reserve space for "." and ".." */
345     curChunk += 2;
346
347     /* Reserve space for the dynamic-mount directory */
348     afs_dynroot_computeDirEnt(AFS_DYNROOT_MOUNTNAME, &curPage, &curChunk);
349
350     for (cellidx = 0;; cellidx++) {
351         c = afs_GetCellByIndex(cellidx, READ_LOCK);
352         if (!c)
353             break;
354         if ((c->cellNum == afs_dynrootCell) || (c->states & CHush)) {
355             afs_PutCell(c, READ_LOCK);
356             continue;
357         }
358         dotLen = strlen(c->cellName) + 2;
359         dotCell = afs_osi_Alloc(dotLen);
360         osi_Assert(dotCell != NULL);
361         strcpy(dotCell, ".");
362         afs_strcat(dotCell, c->cellName);
363
364         afs_dynroot_computeDirEnt(c->cellName, &curPage, &curChunk);
365         afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
366
367         afs_osi_Free(dotCell, dotLen);
368         afs_PutCell(c, READ_LOCK);
369     }
370     maxcellidx = cellidx;
371
372     for (aliasidx = 0;; aliasidx++) {
373         ca = afs_GetCellAlias(aliasidx);
374         if (!ca)
375             break;
376
377         dotLen = strlen(ca->alias) + 2;
378         dotCell = afs_osi_Alloc(dotLen);
379         osi_Assert(dotCell != NULL);
380         strcpy(dotCell, ".");
381         afs_strcat(dotCell, ca->alias);
382
383         afs_dynroot_computeDirEnt(ca->alias, &curPage, &curChunk);
384         afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
385
386         afs_osi_Free(dotCell, dotLen);
387         afs_PutCellAlias(ca);
388     }
389     maxaliasidx = aliasidx;
390
391     ObtainReadLock(&afs_dynSymlinkLock);
392     ts = afs_dynSymlinkBase;
393     while (ts) {
394         afs_dynroot_computeDirEnt(ts->name, &curPage, &curChunk);
395         ts = ts->next;
396     }
397
398     dirSize = (curPage + 1) * AFS_PAGESIZE;
399     newDir = afs_osi_Alloc(dirSize);
400     osi_Assert(newDir != NULL);
401
402     /*
403      * Now actually construct the directory.
404      */
405     curChunk = 13;
406     curPage = 0;
407     dirHeader = (struct DirHeader *)newDir;
408
409     dirHeader->header.pgcount = 0;
410     dirHeader->header.tag = htons(1234);
411     dirHeader->header.freecount = 0;
412
413     dirHeader->header.freebitmap[0] = 0xff;
414     dirHeader->header.freebitmap[1] = 0x1f;
415     for (i = 2; i < EPP / 8; i++)
416         dirHeader->header.freebitmap[i] = 0;
417     dirHeader->alloMap[0] = EPP - DHE - 1;
418     for (i = 1; i < MAXPAGES; i++)
419         dirHeader->alloMap[i] = EPP;
420     memset(dirHeader->hashTable, 0, NHASHENT * sizeof(dirHeader->hashTable[0]));
421
422     /* Install ".", "..", and the dynamic mount directory */
423     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
424     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
425     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
426                           AFS_DYNROOT_MOUNTNAME, AFS_DYNROOT_MOUNT_VNODE);
427     linkCount += 3;
428
429     for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
430         c = afs_GetCellByIndex(cellidx, READ_LOCK);
431         if (!c)
432             continue;
433         if ((c->cellNum == afs_dynrootCell) || (c->states & CHush)) {
434             afs_PutCell(c, READ_LOCK);
435             continue;
436         }
437
438         dotLen = strlen(c->cellName) + 2;
439         dotCell = afs_osi_Alloc(dotLen);
440         osi_Assert(dotCell != NULL);
441         strcpy(dotCell, ".");
442         afs_strcat(dotCell, c->cellName);
443         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, c->cellName,
444                               VNUM_FROM_CIDX_RW(cellidx, 0));
445         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, dotCell,
446                               VNUM_FROM_CIDX_RW(cellidx, 1));
447         afs_osi_Free(dotCell, dotLen);
448
449         linkCount += 2;
450         afs_PutCell(c, READ_LOCK);
451     }
452
453     for (aliasidx = 0; aliasidx < maxaliasidx; aliasidx++) {
454         ca = afs_GetCellAlias(aliasidx);
455         if (!ca)
456             continue;
457
458         dotLen = strlen(ca->alias) + 2;
459         dotCell = afs_osi_Alloc(dotLen);
460         osi_Assert(dotCell != NULL);
461         strcpy(dotCell, ".");
462         afs_strcat(dotCell, ca->alias);
463         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ca->alias,
464                               VNUM_FROM_CAIDX_RW(aliasidx, 0));
465         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, dotCell,
466                               VNUM_FROM_CAIDX_RW(aliasidx, 1));
467         afs_osi_Free(dotCell, dotLen);
468         afs_PutCellAlias(ca);
469     }
470
471     ts = afs_dynSymlinkBase;
472     while (ts) {
473         int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
474         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ts->name, vnum);
475         ts = ts->next;
476     }
477
478     ReleaseReadLock(&afs_dynSymlinkLock);
479
480     ObtainWriteLock(&afs_dynrootDirLock, 549);
481     if (afs_dynrootDir)
482         afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
483     afs_dynrootDir = newDir;
484     afs_dynrootDirLen = dirSize;
485     afs_dynrootDirLinkcnt = linkCount;
486     afs_dynrootDirVersion = newVersion;
487     ReleaseWriteLock(&afs_dynrootDirLock);
488 }
489
490 static void
491 afs_RebuildDynrootMount(void)
492 {
493     int i;
494     int curChunk, curPage;
495     char *newDir;
496     struct DirHeader *dirHeader;
497
498     newDir = afs_osi_Alloc(AFS_PAGESIZE);
499     osi_Assert(newDir != NULL);
500
501     /*
502      * Now actually construct the directory.
503      */
504     curChunk = 13;
505     curPage = 0;
506     dirHeader = (struct DirHeader *)newDir;
507
508     dirHeader->header.pgcount = 0;
509     dirHeader->header.tag = htons(1234);
510     dirHeader->header.freecount = 0;
511
512     dirHeader->header.freebitmap[0] = 0xff;
513     dirHeader->header.freebitmap[1] = 0x1f;
514     for (i = 2; i < EPP / 8; i++)
515         dirHeader->header.freebitmap[i] = 0;
516     dirHeader->alloMap[0] = EPP - DHE - 1;
517     for (i = 1; i < MAXPAGES; i++)
518         dirHeader->alloMap[i] = EPP;
519     memset(dirHeader->hashTable, 0, NHASHENT * sizeof(dirHeader->hashTable[0]));
520
521     /* Install "." and ".." */
522     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
523     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
524
525     ObtainWriteLock(&afs_dynrootDirLock, 549);
526     if (afs_dynrootMountDir)
527         afs_osi_Free(afs_dynrootMountDir, afs_dynrootMountDirLen);
528     afs_dynrootMountDir = newDir;
529     afs_dynrootMountDirLen = AFS_PAGESIZE;
530     ReleaseWriteLock(&afs_dynrootDirLock);
531 }
532
533 /*
534  * Returns a pointer to the base of the dynroot directory in memory,
535  * length thereof, and a FetchStatus.
536  */
537 void
538 afs_GetDynroot(char **dynrootDir, int *dynrootLen,
539                struct AFSFetchStatus *status)
540 {
541     ObtainReadLock(&afs_dynrootDirLock);
542     if (!afs_dynrootDir || afs_dynrootDirVersion != afs_dynrootVersion) {
543         ReleaseReadLock(&afs_dynrootDirLock);
544         afs_RebuildDynroot();
545         ObtainReadLock(&afs_dynrootDirLock);
546     }
547
548     if (dynrootDir)
549         *dynrootDir = afs_dynrootDir;
550     if (dynrootLen)
551         *dynrootLen = afs_dynrootDirLen;
552
553     if (status) {
554         memset(status, 0, sizeof(struct AFSFetchStatus));
555         status->FileType = Directory;
556         status->LinkCount = afs_dynrootDirLinkcnt;
557         status->Length = afs_dynrootDirLen;
558         status->DataVersion = afs_dynrootVersion;
559         status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
560         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
561         status->UnixModeBits = 0755;
562         status->ParentVnode = 1;
563         status->ParentUnique = 1;
564         status->dataVersionHigh = afs_dynrootVersionHigh;
565     }
566 }
567
568 void
569 afs_GetDynrootMount(char **dynrootDir, int *dynrootLen,
570                     struct AFSFetchStatus *status)
571 {
572     ObtainReadLock(&afs_dynrootDirLock);
573     if (!afs_dynrootMountDir) {
574         ReleaseReadLock(&afs_dynrootDirLock);
575         afs_RebuildDynrootMount();
576         ObtainReadLock(&afs_dynrootDirLock);
577     }
578
579     if (dynrootDir)
580         *dynrootDir = afs_dynrootMountDir;
581     if (dynrootLen)
582         *dynrootLen = afs_dynrootMountDirLen;
583
584     if (status) {
585         memset(status, 0, sizeof(struct AFSFetchStatus));
586         status->FileType = Directory;
587         status->LinkCount = 1;
588         status->Length = afs_dynrootMountDirLen;
589         status->DataVersion = 1;
590         status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
591         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
592         status->UnixModeBits = 0755;
593         status->ParentVnode = 1;
594         status->ParentUnique = 1;
595         status->dataVersionHigh = 0;
596     }
597 }
598
599 /*
600  * Puts back the dynroot read lock.
601  */
602 void
603 afs_PutDynroot(void)
604 {
605     ReleaseReadLock(&afs_dynrootDirLock);
606 }
607
608 /*
609  * Inform dynroot that a new vnode is being created.  Return value
610  * is non-zero if this vnode is handled by dynroot, in which case
611  * FetchStatus will be filled in.
612  */
613 int
614 afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
615 {
616     char *bp, tbuf[CVBS];
617
618     if (_afs_IsDynrootFid(&avc->f.fid)) {
619         if (!afs_dynrootEnable)
620             return 0;
621         afs_GetDynroot(0, 0, status);
622         afs_PutDynroot();
623         goto succeed;
624     }
625
626     if (afs_IsDynrootMount(avc)) {
627         afs_GetDynrootMount(0, 0, status);
628         afs_PutDynroot();
629         goto succeed;
630     }
631
632     /*
633      * Check if this is an entry under /afs, e.g. /afs/cellname.
634      */
635     if (avc->f.fid.Cell == afs_dynrootCell
636         && avc->f.fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
637
638         struct cell *c;
639         struct cell_alias *ca;
640         int namelen, linklen, cellidx, rw;
641
642         memset(status, 0, sizeof(struct AFSFetchStatus));
643
644         status->FileType = SymbolicLink;
645         status->LinkCount = 1;
646         status->DataVersion = 1;
647         status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
648         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
649         status->ParentVnode = 1;
650         status->ParentUnique = 1;
651
652         if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_SYMLINK) {
653             struct afs_dynSymlink *ts;
654             int index = VNUM_TO_VNID(avc->f.fid.Fid.Vnode);
655
656             ObtainReadLock(&afs_dynSymlinkLock);
657             ts = afs_dynSymlinkBase;
658             while (ts) {
659                 if (ts->index == index)
660                     break;
661                 ts = ts->next;
662             }
663
664             if (ts) {
665                 linklen = strlen(ts->target);
666                 avc->linkData = afs_osi_Alloc(linklen + 1);
667                 osi_Assert(avc->linkData != NULL);
668                 strcpy(avc->linkData, ts->target);
669
670                 status->Length = linklen;
671                 status->UnixModeBits = 0755;
672             }
673             ReleaseReadLock(&afs_dynSymlinkLock);
674
675             if (ts)
676                 goto succeed;
677
678             return 0;
679         }
680
681         if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) != VN_TYPE_CELL
682             && VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) != VN_TYPE_ALIAS
683             && VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) != VN_TYPE_MOUNT) {
684             afs_warn("dynroot vnode inconsistency, unknown VNTYPE %d\n",
685                      VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode));
686             return 0;
687         }
688
689         cellidx = VNUM_TO_CIDX(avc->f.fid.Fid.Vnode);
690         rw = VNUM_TO_RW(avc->f.fid.Fid.Vnode);
691
692         if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_ALIAS) {
693             char *realName;
694
695             ca = afs_GetCellAlias(cellidx);
696             if (!ca) {
697                 afs_warn("dynroot vnode inconsistency, can't find alias %d\n",
698                          cellidx);
699                 return 0;
700             }
701
702             /*
703              * linkData needs to contain the name of the cell
704              * we're aliasing for.
705              */
706             realName = ca->cell;
707             if (!realName) {
708                 afs_warn("dynroot: alias %s missing real cell name\n",
709                          ca->alias);
710                 avc->linkData = afs_strdup("unknown");
711                 linklen = 7;
712             } else {
713                 int namelen = strlen(realName);
714                 linklen = rw + namelen;
715                 avc->linkData = afs_osi_Alloc(linklen + 1);
716                 osi_Assert(avc->linkData != NULL);
717                 strcpy(avc->linkData, rw ? "." : "");
718                 afs_strcat(avc->linkData, realName);
719             }
720
721             status->UnixModeBits = 0755;
722             afs_PutCellAlias(ca);
723
724         } else if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_MOUNT) {
725             c = afs_GetCellByIndex(cellidx, READ_LOCK);
726             if (!c) {
727                 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
728                          cellidx);
729                 return 0;
730             }
731
732             /*
733              * linkData needs to contain "%cell:volumeid"
734              */
735             namelen = strlen(c->cellName);
736             bp = afs_cv2string(&tbuf[CVBS], avc->f.fid.Fid.Unique);
737             linklen = 2 + namelen + strlen(bp);
738             avc->linkData = afs_osi_Alloc(linklen + 1);
739             osi_Assert(avc->linkData != NULL);
740             strcpy(avc->linkData, "%");
741             afs_strcat(avc->linkData, c->cellName);
742             afs_strcat(avc->linkData, ":");
743             afs_strcat(avc->linkData, bp);
744
745             status->UnixModeBits = 0644;
746             status->ParentVnode = AFS_DYNROOT_MOUNT_VNODE;
747             afs_PutCell(c, READ_LOCK);
748
749         } else {
750             c = afs_GetCellByIndex(cellidx, READ_LOCK);
751             if (!c) {
752                 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
753                          cellidx);
754                 return 0;
755             }
756
757             /*
758              * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
759              */
760             namelen = strlen(c->cellName);
761             linklen = 1 + namelen + 10;
762             avc->linkData = afs_osi_Alloc(linklen + 1);
763             osi_Assert(avc->linkData != NULL);
764             strcpy(avc->linkData, rw ? "%" : "#");
765             afs_strcat(avc->linkData, c->cellName);
766             afs_strcat(avc->linkData, ":root.cell");
767
768             status->UnixModeBits = 0644;
769             afs_PutCell(c, READ_LOCK);
770         }
771
772         status->Length = linklen;
773         goto succeed;
774     }
775
776     return 0;
777
778     /* make sure we set type correctly when we do this. used to stay VREG */
779  succeed:
780     switch (status->FileType) {
781     case File:
782         vSetType(avc, VREG);
783         break;
784     case Directory:
785         vSetType(avc, VDIR);
786         break;
787     case SymbolicLink:
788         if (afs_fakestat_enable && (avc->f.m.Mode & 0111) == 0)
789             vSetType(avc, VDIR);
790         else
791             vSetType(avc, VLNK);
792         break;
793     default:
794         /* shouldn't happen */
795       ;
796     }
797     return 1;
798 }
799
800 /*
801  * Make sure dynroot initialization has been done.
802  */
803 int
804 afs_InitDynroot(void)
805 {
806     if (afs_dynrootInit)
807         return 0;
808     AFS_RWLOCK_INIT(&afs_dynrootDirLock, "afs_dynrootDirLock");
809     AFS_RWLOCK_INIT(&afs_dynSymlinkLock, "afs_dynSymlinkLock");
810     afs_dynrootInit = 0;
811     return afs_dynrootCellInit();
812 }
813
814 /*
815  * Enable or disable dynroot.  Returns 0 if successful.
816  */
817 int
818 afs_SetDynrootEnable(int enable)
819 {
820     afs_dynrootEnable = enable;
821     return afs_InitDynroot();
822 }
823
824 /*
825  * Check if dynroot support is enabled.
826  */
827 int
828 afs_GetDynrootEnable(void)
829 {
830     return afs_dynrootEnable;
831 }
832
833 /*
834  * Remove a temporary symlink entry from /afs.
835  */
836 int
837 afs_DynrootVOPRemove(struct vcache *avc, afs_ucred_t *acred, char *aname)
838 {
839     struct afs_dynSymlink **tpps;
840     struct afs_dynSymlink *tps;
841     int found = 0;
842
843 #if defined(AFS_SUN510_ENV)
844     if (crgetruid(acred))
845 #else
846     if (afs_cr_uid(acred))
847 #endif
848         return EPERM;
849
850     ObtainWriteLock(&afs_dynSymlinkLock, 97);
851     tpps = &afs_dynSymlinkBase;
852     while (*tpps) {
853         tps = *tpps;
854         if (afs_strcasecmp(aname, tps->name) == 0) {
855             afs_osi_Free(tps->name, strlen(tps->name) + 1);
856             afs_osi_Free(tps->target, strlen(tps->target) + 1);
857             *tpps = tps->next;
858             afs_osi_Free(tps, sizeof(*tps));
859             afs_dynSymlinkIndex++;
860             found = 1;
861             break;
862         }
863         tpps = &(tps->next);
864     }
865     ReleaseWriteLock(&afs_dynSymlinkLock);
866     if (found) {
867         afs_DynrootInvalidate();
868         return 0;
869     }
870
871     if (afs_CellOrAliasExists(aname))
872         return EROFS;
873     else
874         return ENOENT;
875 }
876
877 /*
878  * Create a temporary symlink entry in /afs.
879  */
880 int
881 afs_DynrootVOPSymlink(struct vcache *avc, afs_ucred_t *acred,
882                       char *aname, char *atargetName)
883 {
884     struct afs_dynSymlink *tps;
885
886     if (afs_cr_uid(acred))
887         return EPERM;
888     if (afs_CellOrAliasExists(aname))
889         return EEXIST;
890
891     /* Check if it's already a symlink */
892     ObtainWriteLock(&afs_dynSymlinkLock, 91);
893     tps = afs_dynSymlinkBase;
894     while (tps) {
895         if (afs_strcasecmp(aname, tps->name) == 0) {
896             ReleaseWriteLock(&afs_dynSymlinkLock);
897             return EEXIST;
898         }
899         tps = tps->next;
900     }
901
902     /* Doesn't already exist -- go ahead and create it */
903     tps = afs_osi_Alloc(sizeof(*tps));
904     osi_Assert(tps != NULL);
905     tps->index = afs_dynSymlinkIndex++;
906     tps->next = afs_dynSymlinkBase;
907     tps->name = afs_osi_Alloc(strlen(aname) + 1);
908     osi_Assert(tps->name != NULL);
909     strcpy(tps->name, aname);
910     tps->target = afs_osi_Alloc(strlen(atargetName) + 1);
911     osi_Assert(tps->target != NULL);
912     strcpy(tps->target, atargetName);
913     afs_dynSymlinkBase = tps;
914     ReleaseWriteLock(&afs_dynSymlinkLock);
915
916     afs_DynrootInvalidate();
917     return 0;
918 }
919
920 void
921 shutdown_dynroot(void)
922 {
923     if (!afs_dynrootEnable) {
924         return;
925     }
926
927     ObtainWriteLock(&afs_dynrootDirLock, 554);
928
929     if (afs_dynrootDir) {
930         afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
931         afs_dynrootDir = NULL;
932         afs_dynrootDirLen = 0;
933     }
934     if (afs_dynrootMountDir) {
935         afs_osi_Free(afs_dynrootMountDir, afs_dynrootMountDirLen);
936         afs_dynrootMountDir = NULL;
937         afs_dynrootMountDirLen = 0;
938     }
939
940     ReleaseWriteLock(&afs_dynrootDirLock);
941 }