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