libafs: avoid resetting the dynroot volume every 10 minutes
[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         tvc->f.states &= ~(CStatd | CUnique);
310         osi_dnlc_purgedp(tvc);
311         afs_PutVCache(tvc);
312     }
313 }
314
315 /*
316  * Regenerates the dynroot contents from the current list of
317  * cells.  Useful when the list of cells has changed due to
318  * an AFSDB lookup, for instance.
319  */
320 static void
321 afs_RebuildDynroot(void)
322 {
323     int cellidx, maxcellidx, i;
324     int aliasidx, maxaliasidx;
325     struct cell *c;
326     struct cell_alias *ca;
327     int curChunk, curPage;
328     int dirSize, dotLen;
329     char *newDir, *dotCell;
330     struct DirHeader *dirHeader;
331     int linkCount = 0;
332     struct afs_dynSymlink *ts;
333     int newVersion;
334
335     ObtainReadLock(&afs_dynrootDirLock);
336     newVersion = afs_dynrootVersion;
337     ReleaseReadLock(&afs_dynrootDirLock);
338
339     /*
340      * Compute the amount of space we need for the fake dir
341      */
342     curChunk = 13;
343     curPage = 0;
344
345     /* Reserve space for "." and ".." */
346     curChunk += 2;
347
348     /* Reserve space for the dynamic-mount directory */
349     afs_dynroot_computeDirEnt(AFS_DYNROOT_MOUNTNAME, &curPage, &curChunk);
350
351     for (cellidx = 0;; cellidx++) {
352         c = afs_GetCellByIndex(cellidx, READ_LOCK);
353         if (!c)
354             break;
355         if ((c->cellNum == afs_dynrootCell) || (c->states & CHush)) {
356             afs_PutCell(c, READ_LOCK);
357             continue;
358         }
359         dotLen = strlen(c->cellName) + 2;
360         dotCell = afs_osi_Alloc(dotLen);
361         osi_Assert(dotCell != NULL);
362         strcpy(dotCell, ".");
363         afs_strcat(dotCell, c->cellName);
364
365         afs_dynroot_computeDirEnt(c->cellName, &curPage, &curChunk);
366         afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
367
368         afs_osi_Free(dotCell, dotLen);
369         afs_PutCell(c, READ_LOCK);
370     }
371     maxcellidx = cellidx;
372
373     for (aliasidx = 0;; aliasidx++) {
374         ca = afs_GetCellAlias(aliasidx);
375         if (!ca)
376             break;
377
378         dotLen = strlen(ca->alias) + 2;
379         dotCell = afs_osi_Alloc(dotLen);
380         osi_Assert(dotCell != NULL);
381         strcpy(dotCell, ".");
382         afs_strcat(dotCell, ca->alias);
383
384         afs_dynroot_computeDirEnt(ca->alias, &curPage, &curChunk);
385         afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
386
387         afs_osi_Free(dotCell, dotLen);
388         afs_PutCellAlias(ca);
389     }
390     maxaliasidx = aliasidx;
391
392     ObtainReadLock(&afs_dynSymlinkLock);
393     ts = afs_dynSymlinkBase;
394     while (ts) {
395         afs_dynroot_computeDirEnt(ts->name, &curPage, &curChunk);
396         ts = ts->next;
397     }
398
399     dirSize = (curPage + 1) * AFS_PAGESIZE;
400     newDir = afs_osi_Alloc(dirSize);
401     osi_Assert(newDir != NULL);
402
403     /*
404      * Now actually construct the directory.
405      */
406     curChunk = 13;
407     curPage = 0;
408     dirHeader = (struct DirHeader *)newDir;
409
410     dirHeader->header.pgcount = 0;
411     dirHeader->header.tag = htons(1234);
412     dirHeader->header.freecount = 0;
413
414     dirHeader->header.freebitmap[0] = 0xff;
415     dirHeader->header.freebitmap[1] = 0x1f;
416     for (i = 2; i < EPP / 8; i++)
417         dirHeader->header.freebitmap[i] = 0;
418     dirHeader->alloMap[0] = EPP - DHE - 1;
419     for (i = 1; i < MAXPAGES; i++)
420         dirHeader->alloMap[i] = EPP;
421     memset(dirHeader->hashTable, 0, NHASHENT * sizeof(dirHeader->hashTable[0]));
422
423     /* Install ".", "..", and the dynamic mount directory */
424     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
425     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
426     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
427                           AFS_DYNROOT_MOUNTNAME, AFS_DYNROOT_MOUNT_VNODE);
428     linkCount += 3;
429
430     for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
431         c = afs_GetCellByIndex(cellidx, READ_LOCK);
432         if (!c)
433             continue;
434         if ((c->cellNum == afs_dynrootCell) || (c->states & CHush)) {
435             afs_PutCell(c, READ_LOCK);
436             continue;
437         }
438
439         dotLen = strlen(c->cellName) + 2;
440         dotCell = afs_osi_Alloc(dotLen);
441         osi_Assert(dotCell != NULL);
442         strcpy(dotCell, ".");
443         afs_strcat(dotCell, c->cellName);
444         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, c->cellName,
445                               VNUM_FROM_CIDX_RW(cellidx, 0));
446         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, dotCell,
447                               VNUM_FROM_CIDX_RW(cellidx, 1));
448         afs_osi_Free(dotCell, dotLen);
449
450         linkCount += 2;
451         afs_PutCell(c, READ_LOCK);
452     }
453
454     for (aliasidx = 0; aliasidx < maxaliasidx; aliasidx++) {
455         ca = afs_GetCellAlias(aliasidx);
456         if (!ca)
457             continue;
458
459         dotLen = strlen(ca->alias) + 2;
460         dotCell = afs_osi_Alloc(dotLen);
461         osi_Assert(dotCell != NULL);
462         strcpy(dotCell, ".");
463         afs_strcat(dotCell, ca->alias);
464         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ca->alias,
465                               VNUM_FROM_CAIDX_RW(aliasidx, 0));
466         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, dotCell,
467                               VNUM_FROM_CAIDX_RW(aliasidx, 1));
468         afs_osi_Free(dotCell, dotLen);
469         afs_PutCellAlias(ca);
470     }
471
472     ts = afs_dynSymlinkBase;
473     while (ts) {
474         int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
475         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ts->name, vnum);
476         ts = ts->next;
477     }
478
479     ReleaseReadLock(&afs_dynSymlinkLock);
480
481     ObtainWriteLock(&afs_dynrootDirLock, 549);
482     if (afs_dynrootDir)
483         afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
484     afs_dynrootDir = newDir;
485     afs_dynrootDirLen = dirSize;
486     afs_dynrootDirLinkcnt = linkCount;
487     afs_dynrootDirVersion = newVersion;
488     ReleaseWriteLock(&afs_dynrootDirLock);
489 }
490
491 static void
492 afs_RebuildDynrootMount(void)
493 {
494     int i;
495     int curChunk, curPage;
496     char *newDir;
497     struct DirHeader *dirHeader;
498
499     newDir = afs_osi_Alloc(AFS_PAGESIZE);
500     osi_Assert(newDir != NULL);
501
502     /*
503      * Now actually construct the directory.
504      */
505     curChunk = 13;
506     curPage = 0;
507     dirHeader = (struct DirHeader *)newDir;
508
509     dirHeader->header.pgcount = 0;
510     dirHeader->header.tag = htons(1234);
511     dirHeader->header.freecount = 0;
512
513     dirHeader->header.freebitmap[0] = 0xff;
514     dirHeader->header.freebitmap[1] = 0x1f;
515     for (i = 2; i < EPP / 8; i++)
516         dirHeader->header.freebitmap[i] = 0;
517     dirHeader->alloMap[0] = EPP - DHE - 1;
518     for (i = 1; i < MAXPAGES; i++)
519         dirHeader->alloMap[i] = EPP;
520     memset(dirHeader->hashTable, 0, NHASHENT * sizeof(dirHeader->hashTable[0]));
521
522     /* Install "." and ".." */
523     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
524     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
525
526     ObtainWriteLock(&afs_dynrootDirLock, 549);
527     if (afs_dynrootMountDir)
528         afs_osi_Free(afs_dynrootMountDir, afs_dynrootMountDirLen);
529     afs_dynrootMountDir = newDir;
530     afs_dynrootMountDirLen = AFS_PAGESIZE;
531     ReleaseWriteLock(&afs_dynrootDirLock);
532 }
533
534 /*
535  * Returns a pointer to the base of the dynroot directory in memory,
536  * length thereof, and a FetchStatus.
537  */
538 void
539 afs_GetDynroot(char **dynrootDir, int *dynrootLen,
540                struct AFSFetchStatus *status)
541 {
542     ObtainReadLock(&afs_dynrootDirLock);
543     if (!afs_dynrootDir || afs_dynrootDirVersion != afs_dynrootVersion) {
544         ReleaseReadLock(&afs_dynrootDirLock);
545         afs_RebuildDynroot();
546         ObtainReadLock(&afs_dynrootDirLock);
547     }
548
549     if (dynrootDir)
550         *dynrootDir = afs_dynrootDir;
551     if (dynrootLen)
552         *dynrootLen = afs_dynrootDirLen;
553
554     if (status) {
555         memset(status, 0, sizeof(struct AFSFetchStatus));
556         status->FileType = Directory;
557         status->LinkCount = afs_dynrootDirLinkcnt;
558         status->Length = afs_dynrootDirLen;
559         status->DataVersion = afs_dynrootVersion;
560         status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
561         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
562         status->UnixModeBits = 0755;
563         status->ParentVnode = 1;
564         status->ParentUnique = 1;
565         status->dataVersionHigh = afs_dynrootVersionHigh;
566     }
567 }
568
569 void
570 afs_GetDynrootMount(char **dynrootDir, int *dynrootLen,
571                     struct AFSFetchStatus *status)
572 {
573     ObtainReadLock(&afs_dynrootDirLock);
574     if (!afs_dynrootMountDir) {
575         ReleaseReadLock(&afs_dynrootDirLock);
576         afs_RebuildDynrootMount();
577         ObtainReadLock(&afs_dynrootDirLock);
578     }
579
580     if (dynrootDir)
581         *dynrootDir = afs_dynrootMountDir;
582     if (dynrootLen)
583         *dynrootLen = afs_dynrootMountDirLen;
584
585     if (status) {
586         memset(status, 0, sizeof(struct AFSFetchStatus));
587         status->FileType = Directory;
588         status->LinkCount = 1;
589         status->Length = afs_dynrootMountDirLen;
590         status->DataVersion = 1;
591         status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
592         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
593         status->UnixModeBits = 0755;
594         status->ParentVnode = 1;
595         status->ParentUnique = 1;
596         status->dataVersionHigh = 0;
597     }
598 }
599
600 /*
601  * Puts back the dynroot read lock.
602  */
603 void
604 afs_PutDynroot(void)
605 {
606     ReleaseReadLock(&afs_dynrootDirLock);
607 }
608
609 /*
610  * Inform dynroot that a new vnode is being created.  Return value
611  * is non-zero if this vnode is handled by dynroot, in which case
612  * FetchStatus will be filled in.
613  */
614 int
615 afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
616 {
617     char *bp, tbuf[CVBS];
618
619     if (_afs_IsDynrootFid(&avc->f.fid)) {
620         if (!afs_dynrootEnable)
621             return 0;
622         afs_GetDynroot(0, 0, status);
623         afs_PutDynroot();
624         goto succeed;
625     }
626
627     if (afs_IsDynrootMount(avc)) {
628         afs_GetDynrootMount(0, 0, status);
629         afs_PutDynroot();
630         goto succeed;
631     }
632
633     /*
634      * Check if this is an entry under /afs, e.g. /afs/cellname.
635      */
636     if (avc->f.fid.Cell == afs_dynrootCell
637         && avc->f.fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
638
639         struct cell *c;
640         struct cell_alias *ca;
641         int namelen, linklen, cellidx, rw;
642
643         memset(status, 0, sizeof(struct AFSFetchStatus));
644
645         status->FileType = SymbolicLink;
646         status->LinkCount = 1;
647         status->DataVersion = 1;
648         status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
649         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
650         status->ParentVnode = 1;
651         status->ParentUnique = 1;
652
653         if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_SYMLINK) {
654             struct afs_dynSymlink *ts;
655             int index = VNUM_TO_VNID(avc->f.fid.Fid.Vnode);
656
657             ObtainReadLock(&afs_dynSymlinkLock);
658             ts = afs_dynSymlinkBase;
659             while (ts) {
660                 if (ts->index == index)
661                     break;
662                 ts = ts->next;
663             }
664
665             if (ts) {
666                 linklen = strlen(ts->target);
667                 avc->linkData = afs_osi_Alloc(linklen + 1);
668                 osi_Assert(avc->linkData != NULL);
669                 strcpy(avc->linkData, ts->target);
670
671                 status->Length = linklen;
672                 status->UnixModeBits = 0755;
673             }
674             ReleaseReadLock(&afs_dynSymlinkLock);
675
676             if (ts)
677                 goto succeed;
678
679             return 0;
680         }
681
682         if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) != VN_TYPE_CELL
683             && VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) != VN_TYPE_ALIAS
684             && VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) != VN_TYPE_MOUNT) {
685             afs_warn("dynroot vnode inconsistency, unknown VNTYPE %d\n",
686                      VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode));
687             return 0;
688         }
689
690         cellidx = VNUM_TO_CIDX(avc->f.fid.Fid.Vnode);
691         rw = VNUM_TO_RW(avc->f.fid.Fid.Vnode);
692
693         if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_ALIAS) {
694             char *realName;
695
696             ca = afs_GetCellAlias(cellidx);
697             if (!ca) {
698                 afs_warn("dynroot vnode inconsistency, can't find alias %d\n",
699                          cellidx);
700                 return 0;
701             }
702
703             /*
704              * linkData needs to contain the name of the cell
705              * we're aliasing for.
706              */
707             realName = ca->cell;
708             if (!realName) {
709                 afs_warn("dynroot: alias %s missing real cell name\n",
710                          ca->alias);
711                 avc->linkData = afs_strdup("unknown");
712                 linklen = 7;
713             } else {
714                 int namelen = strlen(realName);
715                 linklen = rw + namelen;
716                 avc->linkData = afs_osi_Alloc(linklen + 1);
717                 osi_Assert(avc->linkData != NULL);
718                 strcpy(avc->linkData, rw ? "." : "");
719                 afs_strcat(avc->linkData, realName);
720             }
721
722             status->UnixModeBits = 0755;
723             afs_PutCellAlias(ca);
724
725         } else if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_MOUNT) {
726             c = afs_GetCellByIndex(cellidx, READ_LOCK);
727             if (!c) {
728                 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
729                          cellidx);
730                 return 0;
731             }
732
733             /*
734              * linkData needs to contain "%cell:volumeid"
735              */
736             namelen = strlen(c->cellName);
737             bp = afs_cv2string(&tbuf[CVBS], avc->f.fid.Fid.Unique);
738             linklen = 2 + namelen + strlen(bp);
739             avc->linkData = afs_osi_Alloc(linklen + 1);
740             osi_Assert(avc->linkData != NULL);
741             strcpy(avc->linkData, "%");
742             afs_strcat(avc->linkData, c->cellName);
743             afs_strcat(avc->linkData, ":");
744             afs_strcat(avc->linkData, bp);
745
746             status->UnixModeBits = 0644;
747             status->ParentVnode = AFS_DYNROOT_MOUNT_VNODE;
748             afs_PutCell(c, READ_LOCK);
749
750         } else {
751             c = afs_GetCellByIndex(cellidx, READ_LOCK);
752             if (!c) {
753                 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
754                          cellidx);
755                 return 0;
756             }
757
758             /*
759              * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
760              */
761             namelen = strlen(c->cellName);
762             linklen = 1 + namelen + 10;
763             avc->linkData = afs_osi_Alloc(linklen + 1);
764             osi_Assert(avc->linkData != NULL);
765             strcpy(avc->linkData, rw ? "%" : "#");
766             afs_strcat(avc->linkData, c->cellName);
767             afs_strcat(avc->linkData, ":root.cell");
768
769             status->UnixModeBits = 0644;
770             afs_PutCell(c, READ_LOCK);
771         }
772
773         status->Length = linklen;
774         goto succeed;
775     }
776
777     return 0;
778
779     /* make sure we set type correctly when we do this. used to stay VREG */
780  succeed:
781     switch (status->FileType) {
782     case File:
783         vSetType(avc, VREG);
784         break;
785     case Directory:
786         vSetType(avc, VDIR);
787         break;
788     case SymbolicLink:
789         if (afs_fakestat_enable && (avc->f.m.Mode & 0111) == 0)
790             vSetType(avc, VDIR);
791         else
792             vSetType(avc, VLNK);
793         break;
794     default:
795         /* shouldn't happen */
796       ;
797     }
798     return 1;
799 }
800
801 /*
802  * Make sure dynroot initialization has been done.
803  */
804 int
805 afs_InitDynroot(void)
806 {
807     if (afs_dynrootInit)
808         return 0;
809     AFS_RWLOCK_INIT(&afs_dynrootDirLock, "afs_dynrootDirLock");
810     AFS_RWLOCK_INIT(&afs_dynSymlinkLock, "afs_dynSymlinkLock");
811     afs_dynrootInit = 0;
812     return afs_dynrootCellInit();
813 }
814
815 /*
816  * Enable or disable dynroot.  Returns 0 if successful.
817  */
818 int
819 afs_SetDynrootEnable(int enable)
820 {
821     afs_dynrootEnable = enable;
822     return afs_InitDynroot();
823 }
824
825 /*
826  * Check if dynroot support is enabled.
827  */
828 int
829 afs_GetDynrootEnable(void)
830 {
831     return afs_dynrootEnable;
832 }
833
834 /*
835  * Remove a temporary symlink entry from /afs.
836  */
837 int
838 afs_DynrootVOPRemove(struct vcache *avc, afs_ucred_t *acred, char *aname)
839 {
840     struct afs_dynSymlink **tpps;
841     struct afs_dynSymlink *tps;
842     int found = 0;
843
844 #if defined(AFS_SUN510_ENV)
845     if (crgetruid(acred))
846 #else
847     if (afs_cr_uid(acred))
848 #endif
849         return EPERM;
850
851     ObtainWriteLock(&afs_dynSymlinkLock, 97);
852     tpps = &afs_dynSymlinkBase;
853     while (*tpps) {
854         tps = *tpps;
855         if (afs_strcasecmp(aname, tps->name) == 0) {
856             afs_osi_Free(tps->name, strlen(tps->name) + 1);
857             afs_osi_Free(tps->target, strlen(tps->target) + 1);
858             *tpps = tps->next;
859             afs_osi_Free(tps, sizeof(*tps));
860             afs_dynSymlinkIndex++;
861             found = 1;
862             break;
863         }
864         tpps = &(tps->next);
865     }
866     ReleaseWriteLock(&afs_dynSymlinkLock);
867     if (found) {
868         afs_DynrootInvalidate();
869         return 0;
870     }
871
872     if (afs_CellOrAliasExists(aname))
873         return EROFS;
874     else
875         return ENOENT;
876 }
877
878 /*
879  * Create a temporary symlink entry in /afs.
880  */
881 int
882 afs_DynrootVOPSymlink(struct vcache *avc, afs_ucred_t *acred,
883                       char *aname, char *atargetName)
884 {
885     struct afs_dynSymlink *tps;
886
887     if (afs_cr_uid(acred))
888         return EPERM;
889     if (afs_CellOrAliasExists(aname))
890         return EEXIST;
891
892     /* Check if it's already a symlink */
893     ObtainWriteLock(&afs_dynSymlinkLock, 91);
894     tps = afs_dynSymlinkBase;
895     while (tps) {
896         if (afs_strcasecmp(aname, tps->name) == 0) {
897             ReleaseWriteLock(&afs_dynSymlinkLock);
898             return EEXIST;
899         }
900         tps = tps->next;
901     }
902
903     /* Doesn't already exist -- go ahead and create it */
904     tps = afs_osi_Alloc(sizeof(*tps));
905     osi_Assert(tps != NULL);
906     tps->index = afs_dynSymlinkIndex++;
907     tps->next = afs_dynSymlinkBase;
908     tps->name = afs_osi_Alloc(strlen(aname) + 1);
909     osi_Assert(tps->name != NULL);
910     strcpy(tps->name, aname);
911     tps->target = afs_osi_Alloc(strlen(atargetName) + 1);
912     osi_Assert(tps->target != NULL);
913     strcpy(tps->target, atargetName);
914     afs_dynSymlinkBase = tps;
915     ReleaseWriteLock(&afs_dynSymlinkLock);
916
917     afs_DynrootInvalidate();
918     return 0;
919 }