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