afsd -dynroot-sparse mode for hushed cells
[openafs.git] / src / afs / afs_dynroot.c
index bba3d42..9a4e1ad 100644 (file)
  *
  * Implements:
  * afs_IsDynrootFid
+ * afs_IsDynrootMountFid
+ * afs_IsDynrootAnyFid
  * afs_GetDynrootFid
+ * afs_GetDynrootMountFid
  * afs_IsDynroot
- * afs_RefreshDynroot
+ * afs_IsDynrootMount
+ * afs_IsDynrootAny
+ * afs_DynrootInvalidate
  * afs_GetDynroot
  * afs_PutDynroot
  * afs_DynrootNewVnode
  */
 
 #include <afsconfig.h>
-#include "../afs/param.h"
+#include "afs/param.h"
 
-#include "../afs/stds.h"
-#include "../afs/sysincludes.h" /* Standard vendor system headers */
-#include "../afs/afsincludes.h"
-#include "../afs/afs_osi.h"
-#include "../afsint/afsint.h"
-#include "../afs/lock.h"
+#include "afs/stds.h"
+#include "afs/sysincludes.h"   /* Standard vendor system headers */
+#include "afsincludes.h"
+#include "afs/afs_osi.h"
+#include "afsint.h"
+#include "afs/lock.h"
 
-#include "../afs/prs_fs.h"
-#include "../afs/dir.h"
+#include "afs/prs_fs.h"
+#include "afs/dir.h"
+#include "afs/afs_dynroot.h"
 
-#define AFS_DYNROOT_CELL       1
+#define AFS_DYNROOT_CELLNAME   "dynroot"
 #define AFS_DYNROOT_VOLUME     1
 #define AFS_DYNROOT_VNODE      1
+#define AFS_DYNROOT_MOUNT_VNODE 3
 #define AFS_DYNROOT_UNIQUE     1
 
-/*
- * Vnode numbers in dynroot are composed of a type field (upper 8 bits)
- * and a type-specific identifier in the lower 24 bits.
- */
-#define VN_TYPE_CELL           0x01    /* Corresponds to a struct cell */
-#define VN_TYPE_SYMLINK                0x02    /* User-created symlink in /afs */
-
-#define VNUM_TO_VNTYPE(vnum)   ((vnum) >> 24)
-#define VNUM_TO_VNID(vnum)     ((vnum) & 0x00ffffff)
-#define VNUM_FROM_TYPEID(type, id) \
-                               ((type) << 24 | (id))
-#define VNUM_TO_CIDX(vnum)     (VNUM_TO_VNID(vnum) >> 2)
-#define VNUM_TO_RW(vnum)       (VNUM_TO_VNID(vnum) >> 1 & 1)
-#define VNUM_FROM_CIDX_RW(cidx, rw) \
-                               VNUM_FROM_TYPEID(VN_TYPE_CELL, \
-                                                ((cidx) << 2 | (rw) << 1))
-
+static int afs_dynrootInit = 0;
 static int afs_dynrootEnable = 0;
+static int afs_dynrootCell = 0;
 
 static afs_rwlock_t afs_dynrootDirLock;
 /* Start of variables protected by afs_dynrootDirLock */
 static char *afs_dynrootDir = NULL;
 static int afs_dynrootDirLen;
+static char *afs_dynrootMountDir = NULL;
+static int afs_dynrootMountDirLen;
 static int afs_dynrootDirLinkcnt;
-static int afs_dynrootCellCount;
+static int afs_dynrootDirVersion;
 static int afs_dynrootVersion = 1;
 static int afs_dynrootVersionHigh = 1;
 /* End of variables protected by afs_dynrootDirLock */
@@ -86,32 +80,85 @@ static struct afs_dynSymlink *afs_dynSymlinkBase = NULL;
 static int afs_dynSymlinkIndex = 0;
 /* End of variables protected by afs_dynSymlinkLock */
 
-extern afs_int32 afs_cellindex;
-extern afs_rwlock_t afs_xvcache;
+/*
+ * Set up a cell for dynroot if it's not there yet.
+ */
+static int
+afs_dynrootCellInit(void)
+{
+    if (!afs_dynrootCell) {
+       afs_int32 cellHosts[AFS_MAXCELLHOSTS];
+       struct cell *tc;
+       int code;
+
+       memset(cellHosts, 0, sizeof(cellHosts));
+       code =
+           afs_NewCell(AFS_DYNROOT_CELLNAME, cellHosts, CNoSUID | CNoAFSDB,
+                       NULL, 0, 0, 0);
+       if (code)
+           return code;
+       tc = afs_GetCellByName(AFS_DYNROOT_CELLNAME, READ_LOCK);
+       if (!tc)
+           return ENODEV;
+       afs_dynrootCell = tc->cellNum;
+       afs_PutCell(tc, READ_LOCK);
+    }
+
+    return 0;
+}
 
 /*
  * Returns non-zero iff fid corresponds to the top of the dynroot volume.
  */
+static int
+_afs_IsDynrootFid(struct VenusFid *fid)
+{
+    return (fid->Cell == afs_dynrootCell
+           && fid->Fid.Volume == AFS_DYNROOT_VOLUME
+           && fid->Fid.Vnode == AFS_DYNROOT_VNODE
+           && fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
+}
+
 int
 afs_IsDynrootFid(struct VenusFid *fid)
 {
-    return
-       (afs_dynrootEnable &&
-        fid->Cell       == AFS_DYNROOT_CELL   &&
-        fid->Fid.Volume == AFS_DYNROOT_VOLUME &&
-        fid->Fid.Vnode  == AFS_DYNROOT_VNODE  &&
-        fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
+    return (afs_dynrootEnable && _afs_IsDynrootFid(fid));
+}
+
+int
+afs_IsDynrootMountFid(struct VenusFid *fid)
+{
+    return (fid->Cell == afs_dynrootCell
+           && fid->Fid.Volume == AFS_DYNROOT_VOLUME
+           && fid->Fid.Vnode == AFS_DYNROOT_MOUNT_VNODE
+           && fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
+}
+
+int
+afs_IsDynrootAnyFid(struct VenusFid *fid)
+{
+    return (fid->Cell == afs_dynrootCell
+           && fid->Fid.Volume == AFS_DYNROOT_VOLUME);
 }
 
 /*
  * Obtain the magic dynroot volume Fid.
  */
 void
-afs_GetDynrootFid(struct VenusFid *fid) 
+afs_GetDynrootFid(struct VenusFid *fid)
 {
-    fid->Cell       = AFS_DYNROOT_CELL;
+    fid->Cell = afs_dynrootCell;
     fid->Fid.Volume = AFS_DYNROOT_VOLUME;
-    fid->Fid.Vnode  = AFS_DYNROOT_VNODE;
+    fid->Fid.Vnode = AFS_DYNROOT_VNODE;
+    fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
+}
+
+void
+afs_GetDynrootMountFid(struct VenusFid *fid)
+{
+    fid->Cell = afs_dynrootCell;
+    fid->Fid.Volume = AFS_DYNROOT_VOLUME;
+    fid->Fid.Vnode = AFS_DYNROOT_MOUNT_VNODE;
     fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
 }
 
@@ -119,10 +166,39 @@ afs_GetDynrootFid(struct VenusFid *fid)
  * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
  */
 int
-afs_IsDynroot(avc)
-    struct vcache *avc;
+afs_IsDynroot(struct vcache *avc)
 {
-    return afs_IsDynrootFid(&avc->fid);
+    return afs_IsDynrootFid(&avc->f.fid);
+}
+
+int
+afs_IsDynrootMount(struct vcache *avc)
+{
+    return afs_IsDynrootMountFid(&avc->f.fid);
+}
+
+int
+afs_IsDynrootAny(struct vcache *avc)
+{
+    return afs_IsDynrootAnyFid(&avc->f.fid);
+}
+
+/*
+ * Given the current page and chunk pointers in a directory, adjust them
+ * appropriately so that the given file name can be appended.  Used for
+ * computing the size of a directory.
+ */
+static void
+afs_dynroot_computeDirEnt(char *name, int *curPageP, int *curChunkP)
+{
+    int esize;
+
+    esize = afs_dir_NameBlobs(name);
+    if (*curChunkP + esize > EPP) {
+       *curPageP += 1;
+       *curChunkP = 1;
+    }
+    *curChunkP += esize;
 }
 
 /*
@@ -131,14 +207,10 @@ afs_IsDynroot(avc)
  * the necessary entry.
  */
 static void
-afs_dynroot_addDirEnt(dirHeader, curPageP, curChunkP, name, vnode)
-    struct DirHeader *dirHeader;
-    int *curPageP;
-    int *curChunkP;
-    char *name;
-    int vnode;
+afs_dynroot_addDirEnt(struct DirHeader *dirHeader, int *curPageP,
+                     int *curChunkP, char *name, int vnode)
 {
-    char *dirBase = (char *) dirHeader;
+    char *dirBase = (char *)dirHeader;
     struct PageHeader *pageHeader;
     struct DirEntry *dirEntry;
     int sizeOfEntry, i, t1, t2;
@@ -156,23 +228,23 @@ afs_dynroot_addDirEnt(dirHeader, curPageP, curChunkP, name, vnode)
        didNewPage = 1;
     }
 
-    pageHeader = (struct PageHeader *) (dirBase + curPage * AFS_PAGESIZE);
+    pageHeader = (struct PageHeader *)(dirBase + curPage * AFS_PAGESIZE);
     if (didNewPage) {
        pageHeader->pgcount = 0;
        pageHeader->tag = htons(1234);
        pageHeader->freecount = 0;
        pageHeader->freebitmap[0] = 0x01;
-       for (i = 1; i < EPP/8; i++)
+       for (i = 1; i < EPP / 8; i++)
            pageHeader->freebitmap[i] = 0;
 
        dirHeader->alloMap[curPage] = EPP - 1;
     }
 
-    dirEntry = (struct DirEntry *) (pageHeader + curChunk);
-    dirEntry->flag        = 1;
-    dirEntry->length      = 0;
-    dirEntry->next        = 0;
-    dirEntry->fid.vnode   = htonl(vnode);
+    dirEntry = (struct DirEntry *)(pageHeader + curChunk);
+    dirEntry->flag = 1;
+    dirEntry->length = 0;
+    dirEntry->next = 0;
+    dirEntry->fid.vnode = htonl(vnode);
     dirEntry->fid.vunique = htonl(1);
     strcpy(dirEntry->name, name);
 
@@ -197,31 +269,61 @@ afs_dynroot_addDirEnt(dirHeader, curPageP, curChunkP, name, vnode)
 }
 
 /*
+ * Invalidate the /afs vnode for dynroot; called when the underlying
+ * directory has changed and needs to be re-read.
+ */
+void
+afs_DynrootInvalidate(void)
+{
+    afs_int32 retry;
+    struct vcache *tvc;
+    struct VenusFid tfid;
+
+    if (!afs_dynrootEnable)
+       return;
+
+    ObtainWriteLock(&afs_dynrootDirLock, 687);
+    afs_dynrootVersion++;
+    afs_dynrootVersionHigh = osi_Time();
+    ReleaseWriteLock(&afs_dynrootDirLock);
+
+    afs_GetDynrootFid(&tfid);
+    do {
+       retry = 0;
+       ObtainReadLock(&afs_xvcache);
+       tvc = afs_FindVCache(&tfid, &retry, 0);
+       ReleaseReadLock(&afs_xvcache);
+    } while (retry);
+    if (tvc) {
+       tvc->f.states &= ~(CStatd | CUnique);
+       osi_dnlc_purgedp(tvc);
+       afs_PutVCache(tvc);
+    }
+}
+
+/*
  * Regenerates the dynroot contents from the current list of
  * cells.  Useful when the list of cells has changed due to
  * an AFSDB lookup, for instance.
  */
-void
-afs_RefreshDynroot()
+static void
+afs_RebuildDynroot(void)
 {
     int cellidx, maxcellidx, i;
+    int aliasidx, maxaliasidx;
     struct cell *c;
+    struct cell_alias *ca;
     int curChunk, curPage;
-    int dirSize, sizeOfCurEntry;
+    int dirSize, dotLen;
     char *newDir, *dotCell;
     struct DirHeader *dirHeader;
-    struct PageHeader *pageHeader;
-    struct DirEntry *dirEntry;
-    int doFlush = 0;
     int linkCount = 0;
     struct afs_dynSymlink *ts;
-    int newCellCount;
+    int newVersion;
 
-    /*
-     * Save afs_cellindex here, in case it changes between the
-     * two loops.
-     */
-    maxcellidx = afs_cellindex;
+    ObtainReadLock(&afs_dynrootDirLock);
+    newVersion = afs_dynrootVersion;
+    ReleaseReadLock(&afs_dynrootDirLock);
 
     /*
      * Compute the amount of space we need for the fake dir
@@ -229,39 +331,55 @@ afs_RefreshDynroot()
     curChunk = 13;
     curPage = 0;
 
-    for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
-       c = afs_GetCellByIndex(cellidx, READ_LOCK, 0 /* don't refresh */);
-       if (!c) continue;
+    /* Reserve space for "." and ".." */
+    curChunk += 2;
 
-       sizeOfCurEntry = afs_dir_NameBlobs(c->cellName);
-       if (curChunk + sizeOfCurEntry > EPP) {
-           curPage++;
-           curChunk = 1;
-       }
-       curChunk += sizeOfCurEntry;
+    /* Reserve space for the dynamic-mount directory */
+    afs_dynroot_computeDirEnt(AFS_DYNROOT_MOUNTNAME, &curPage, &curChunk);
 
-       dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
-       strcpy(dotCell, ".");
-       strcat(dotCell, c->cellName);
-       sizeOfCurEntry = afs_dir_NameBlobs(dotCell);
-       if (curChunk + sizeOfCurEntry > EPP) {
-           curPage++;
-           curChunk = 1;
+    for (cellidx = 0;; cellidx++) {
+       c = afs_GetCellByIndex(cellidx, READ_LOCK);
+       if (!c)
+           break;
+       if ((c->cellNum == afs_dynrootCell) || (c->states & CHush)) {
+           afs_PutCell(c, READ_LOCK);
+           continue;
        }
-       curChunk += sizeOfCurEntry;
+       dotLen = strlen(c->cellName) + 2;
+       dotCell = afs_osi_Alloc(dotLen);
+       strcpy(dotCell, ".");
+       afs_strcat(dotCell, c->cellName);
+
+       afs_dynroot_computeDirEnt(c->cellName, &curPage, &curChunk);
+       afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
 
+       afs_osi_Free(dotCell, dotLen);
        afs_PutCell(c, READ_LOCK);
     }
+    maxcellidx = cellidx;
+
+    for (aliasidx = 0;; aliasidx++) {
+       ca = afs_GetCellAlias(aliasidx);
+       if (!ca)
+           break;
+
+       dotLen = strlen(ca->alias) + 2;
+       dotCell = afs_osi_Alloc(dotLen);
+       strcpy(dotCell, ".");
+       afs_strcat(dotCell, ca->alias);
+
+       afs_dynroot_computeDirEnt(ca->alias, &curPage, &curChunk);
+       afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
+
+       afs_osi_Free(dotCell, dotLen);
+       afs_PutCellAlias(ca);
+    }
+    maxaliasidx = aliasidx;
 
     ObtainReadLock(&afs_dynSymlinkLock);
     ts = afs_dynSymlinkBase;
     while (ts) {
-       sizeOfCurEntry = afs_dir_NameBlobs(ts->name);
-       if (curChunk + sizeOfCurEntry > EPP) {
-           curPage++;
-           curChunk = 1;
-       }
-       curChunk += sizeOfCurEntry;
+       afs_dynroot_computeDirEnt(ts->name, &curPage, &curChunk);
        ts = ts->next;
     }
 
@@ -273,7 +391,7 @@ afs_RefreshDynroot()
      */
     curChunk = 13;
     curPage = 0;
-    dirHeader = (struct DirHeader *) newDir;
+    dirHeader = (struct DirHeader *)newDir;
 
     dirHeader->header.pgcount = 0;
     dirHeader->header.tag = htons(1234);
@@ -281,7 +399,7 @@ afs_RefreshDynroot()
 
     dirHeader->header.freebitmap[0] = 0xff;
     dirHeader->header.freebitmap[1] = 0x1f;
-    for (i = 2; i < EPP/8; i++)
+    for (i = 2; i < EPP / 8; i++)
        dirHeader->header.freebitmap[i] = 0;
     dirHeader->alloMap[0] = EPP - DHE - 1;
     for (i = 1; i < MAXPAGES; i++)
@@ -289,71 +407,113 @@ afs_RefreshDynroot()
     for (i = 0; i < NHASHENT; i++)
        dirHeader->hashTable[i] = 0;
 
-    /* Install "." and ".." */
+    /* Install ".", "..", and the dynamic mount directory */
     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
-    linkCount += 2;
+    afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
+                         AFS_DYNROOT_MOUNTNAME, AFS_DYNROOT_MOUNT_VNODE);
+    linkCount += 3;
 
     for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
-       c = afs_GetCellByIndex(cellidx, READ_LOCK, 0 /* don't refresh */);
-       afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
-                             c->cellName, VNUM_FROM_CIDX_RW(cellidx, 0));
+       c = afs_GetCellByIndex(cellidx, READ_LOCK);
+       if (!c)
+           continue;
+       if ((c->cellNum == afs_dynrootCell) || (c->states & CHush)) {
+           afs_PutCell(c, READ_LOCK);
+           continue;
+       }
 
-       dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
+       dotLen = strlen(c->cellName) + 2;
+       dotCell = afs_osi_Alloc(dotLen);
        strcpy(dotCell, ".");
-       strcat(dotCell, c->cellName);
-       afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
-                             dotCell, VNUM_FROM_CIDX_RW(cellidx, 1));
-
-       if (!(c->states & CAlias)) linkCount += 2;
+       afs_strcat(dotCell, c->cellName);
+       afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, c->cellName,
+                             VNUM_FROM_CIDX_RW(cellidx, 0));
+       afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, dotCell,
+                             VNUM_FROM_CIDX_RW(cellidx, 1));
+       afs_osi_Free(dotCell, dotLen);
+
+       linkCount += 2;
        afs_PutCell(c, READ_LOCK);
     }
 
+    for (aliasidx = 0; aliasidx < maxaliasidx; aliasidx++) {
+       ca = afs_GetCellAlias(aliasidx);
+       if (!ca)
+           continue;
+
+       dotLen = strlen(ca->alias) + 2;
+       dotCell = afs_osi_Alloc(dotLen);
+       strcpy(dotCell, ".");
+       afs_strcat(dotCell, ca->alias);
+       afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ca->alias,
+                             VNUM_FROM_CAIDX_RW(aliasidx, 0));
+       afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, dotCell,
+                             VNUM_FROM_CAIDX_RW(aliasidx, 1));
+       afs_osi_Free(dotCell, dotLen);
+       afs_PutCellAlias(ca);
+    }
+
     ts = afs_dynSymlinkBase;
     while (ts) {
        int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
-       afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
-                             ts->name, vnum);
+       afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ts->name, vnum);
        ts = ts->next;
     }
 
-    newCellCount = maxcellidx + afs_dynSymlinkIndex;
     ReleaseReadLock(&afs_dynSymlinkLock);
 
     ObtainWriteLock(&afs_dynrootDirLock, 549);
-    if (afs_dynrootDir) afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
+    if (afs_dynrootDir)
+       afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
     afs_dynrootDir = newDir;
     afs_dynrootDirLen = dirSize;
     afs_dynrootDirLinkcnt = linkCount;
-    if (afs_dynrootCellCount != newCellCount) {
-       /*
-        * New cells/symlinks added -- bump data version, invalidate vcache.
-        */
-       afs_dynrootCellCount = newCellCount;
-       afs_dynrootVersion++;
-       afs_dynrootVersionHigh = osi_Time();
-       doFlush = 1;
-    }
+    afs_dynrootDirVersion = newVersion;
     ReleaseWriteLock(&afs_dynrootDirLock);
+}
 
-    if (doFlush) {
-       afs_int32 retry;
-       struct vcache *tvc;
-       struct VenusFid tfid;
-
-       afs_GetDynrootFid(&tfid);
-       do {
-           retry = 0;
-           ObtainReadLock(&afs_xvcache);
-           tvc = afs_FindVCache(&tfid, 0, 0, &retry, 0);
-           ReleaseReadLock(&afs_xvcache);
-       } while (retry);
-       if (tvc) {
-           tvc->states &= ~(CStatd | CUnique);
-           osi_dnlc_purgedp(tvc);
-           afs_PutVCache(tvc);
-       }
-    }
+static void
+afs_RebuildDynrootMount(void)
+{
+    int i;
+    int curChunk, curPage;
+    char *newDir;
+    struct DirHeader *dirHeader;
+
+    newDir = afs_osi_Alloc(AFS_PAGESIZE);
+
+    /*
+     * Now actually construct the directory.
+     */
+    curChunk = 13;
+    curPage = 0;
+    dirHeader = (struct DirHeader *)newDir;
+
+    dirHeader->header.pgcount = 0;
+    dirHeader->header.tag = htons(1234);
+    dirHeader->header.freecount = 0;
+
+    dirHeader->header.freebitmap[0] = 0xff;
+    dirHeader->header.freebitmap[1] = 0x1f;
+    for (i = 2; i < EPP / 8; i++)
+       dirHeader->header.freebitmap[i] = 0;
+    dirHeader->alloMap[0] = EPP - DHE - 1;
+    for (i = 1; i < MAXPAGES; i++)
+       dirHeader->alloMap[i] = EPP;
+    for (i = 0; i < NHASHENT; i++)
+       dirHeader->hashTable[i] = 0;
+
+    /* Install "." and ".." */
+    afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
+    afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
+
+    ObtainWriteLock(&afs_dynrootDirLock, 549);
+    if (afs_dynrootMountDir)
+       afs_osi_Free(afs_dynrootMountDir, afs_dynrootMountDirLen);
+    afs_dynrootMountDir = newDir;
+    afs_dynrootMountDirLen = AFS_PAGESIZE;
+    ReleaseWriteLock(&afs_dynrootDirLock);
 }
 
 /*
@@ -361,41 +521,72 @@ afs_RefreshDynroot()
  * length thereof, and a FetchStatus.
  */
 void
-afs_GetDynroot(dynrootDir, dynrootLen, status)
-    char **dynrootDir;
-    int *dynrootLen;
-    struct AFSFetchStatus *status;
+afs_GetDynroot(char **dynrootDir, int *dynrootLen,
+              struct AFSFetchStatus *status)
 {
     ObtainReadLock(&afs_dynrootDirLock);
-    if (!afs_dynrootDir) {
+    if (!afs_dynrootDir || afs_dynrootDirVersion != afs_dynrootVersion) {
        ReleaseReadLock(&afs_dynrootDirLock);
-       afs_RefreshDynroot();
+       afs_RebuildDynroot();
        ObtainReadLock(&afs_dynrootDirLock);
     }
 
-    if (dynrootDir) *dynrootDir = afs_dynrootDir;
-    if (dynrootLen) *dynrootLen = afs_dynrootDirLen;
+    if (dynrootDir)
+       *dynrootDir = afs_dynrootDir;
+    if (dynrootLen)
+       *dynrootLen = afs_dynrootDirLen;
 
     if (status) {
        memset(status, 0, sizeof(struct AFSFetchStatus));
-       status->FileType        = Directory;
-       status->LinkCount       = afs_dynrootDirLinkcnt;
-       status->Length          = afs_dynrootDirLen;
-       status->DataVersion     = afs_dynrootVersion;
-       status->CallerAccess    = PRSFS_LOOKUP | PRSFS_READ;
+       status->FileType = Directory;
+       status->LinkCount = afs_dynrootDirLinkcnt;
+       status->Length = afs_dynrootDirLen;
+       status->DataVersion = afs_dynrootVersion;
+       status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
        status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
-       status->UnixModeBits    = 0755;
-       status->ParentVnode     = 1;
-       status->ParentUnique    = 1;
+       status->UnixModeBits = 0755;
+       status->ParentVnode = 1;
+       status->ParentUnique = 1;
        status->dataVersionHigh = afs_dynrootVersionHigh;
     }
 }
 
+void
+afs_GetDynrootMount(char **dynrootDir, int *dynrootLen,
+                   struct AFSFetchStatus *status)
+{
+    ObtainReadLock(&afs_dynrootDirLock);
+    if (!afs_dynrootMountDir) {
+       ReleaseReadLock(&afs_dynrootDirLock);
+       afs_RebuildDynrootMount();
+       ObtainReadLock(&afs_dynrootDirLock);
+    }
+
+    if (dynrootDir)
+       *dynrootDir = afs_dynrootMountDir;
+    if (dynrootLen)
+       *dynrootLen = afs_dynrootMountDirLen;
+
+    if (status) {
+       memset(status, 0, sizeof(struct AFSFetchStatus));
+       status->FileType = Directory;
+       status->LinkCount = 1;
+       status->Length = afs_dynrootMountDirLen;
+       status->DataVersion = 1;
+       status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
+       status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
+       status->UnixModeBits = 0755;
+       status->ParentVnode = 1;
+       status->ParentUnique = 1;
+       status->dataVersionHigh = 0;
+    }
+}
+
 /*
  * Puts back the dynroot read lock.
  */
 void
-afs_PutDynroot()
+afs_PutDynroot(void)
 {
     ReleaseReadLock(&afs_dynrootDirLock);
 }
@@ -406,45 +597,53 @@ afs_PutDynroot()
  * FetchStatus will be filled in.
  */
 int
-afs_DynrootNewVnode(avc, status)
-    struct vcache *avc;
-    struct AFSFetchStatus *status;
+afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
 {
-    if (!afs_dynrootEnable) return 0;
+    char *bp, tbuf[CVBS];
 
-    if (afs_IsDynroot(avc)) {
+    if (_afs_IsDynrootFid(&avc->f.fid)) {
+       if (!afs_dynrootEnable)
+           return 0;
        afs_GetDynroot(0, 0, status);
        afs_PutDynroot();
        return 1;
     }
 
+    if (afs_IsDynrootMount(avc)) {
+       afs_GetDynrootMount(0, 0, status);
+       afs_PutDynroot();
+       return 1;
+    }
+
     /*
      * Check if this is an entry under /afs, e.g. /afs/cellname.
      */
-    if (avc->fid.Cell       == AFS_DYNROOT_CELL &&
-       avc->fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
+    if (avc->f.fid.Cell == afs_dynrootCell
+       && avc->f.fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
 
        struct cell *c;
+       struct cell_alias *ca;
        int namelen, linklen, cellidx, rw;
 
        memset(status, 0, sizeof(struct AFSFetchStatus));
 
-       status->FileType        = SymbolicLink;
-       status->LinkCount       = 1;
-       status->DataVersion     = 1;
-       status->CallerAccess    = PRSFS_LOOKUP | PRSFS_READ;
+       status->FileType = SymbolicLink;
+       status->LinkCount = 1;
+       status->DataVersion = 1;
+       status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
        status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
-       status->ParentVnode     = 1;
-       status->ParentUnique    = 1;
+       status->ParentVnode = 1;
+       status->ParentUnique = 1;
 
-       if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_SYMLINK) {
+       if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_SYMLINK) {
            struct afs_dynSymlink *ts;
-           int index = VNUM_TO_VNID(avc->fid.Fid.Vnode);
+           int index = VNUM_TO_VNID(avc->f.fid.Fid.Vnode);
 
            ObtainReadLock(&afs_dynSymlinkLock);
            ts = afs_dynSymlinkBase;
            while (ts) {
-               if (ts->index == index) break;
+               if (ts->index == index)
+                   break;
                ts = ts->next;
            }
 
@@ -453,7 +652,7 @@ afs_DynrootNewVnode(avc, status)
                avc->linkData = afs_osi_Alloc(linklen + 1);
                strcpy(avc->linkData, ts->target);
 
-               status->Length       = linklen;
+               status->Length = linklen;
                status->UnixModeBits = 0755;
            }
            ReleaseReadLock(&afs_dynSymlinkLock);
@@ -461,45 +660,80 @@ afs_DynrootNewVnode(avc, status)
            return ts ? 1 : 0;
        }
 
-       if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_CELL) {
+       if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) != VN_TYPE_CELL
+           && VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) != VN_TYPE_ALIAS
+           && VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) != VN_TYPE_MOUNT) {
            afs_warn("dynroot vnode inconsistency, unknown VNTYPE %d\n",
-                    VNUM_TO_VNTYPE(avc->fid.Fid.Vnode));
+                    VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode));
            return 0;
        }
 
-       cellidx = VNUM_TO_CIDX(avc->fid.Fid.Vnode);
-       rw = VNUM_TO_RW(avc->fid.Fid.Vnode);
+       cellidx = VNUM_TO_CIDX(avc->f.fid.Fid.Vnode);
+       rw = VNUM_TO_RW(avc->f.fid.Fid.Vnode);
 
-       c = afs_GetCellByIndex(cellidx, READ_LOCK, 1 /* refresh */);
-       if (!c) {
-           afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
-                    cellidx);
-           return 0;
-       }
+       if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_ALIAS) {
+           char *realName;
+
+           ca = afs_GetCellAlias(cellidx);
+           if (!ca) {
+               afs_warn("dynroot vnode inconsistency, can't find alias %d\n",
+                        cellidx);
+               return 0;
+           }
 
-       if (c->states & CAlias) {
            /*
             * linkData needs to contain the name of the cell
             * we're aliasing for.
             */
-           char *realName = c->realName;
-
+           realName = ca->cell;
            if (!realName) {
                afs_warn("dynroot: alias %s missing real cell name\n",
-                        c->cellName);
+                        ca->alias);
+               avc->linkData = afs_strdup("unknown");
                linklen = 7;
-               avc->linkData = afs_osi_Alloc(linklen + 1);
-               strcpy(avc->linkData, "unknown");
            } else {
                int namelen = strlen(realName);
                linklen = rw + namelen;
                avc->linkData = afs_osi_Alloc(linklen + 1);
                strcpy(avc->linkData, rw ? "." : "");
-               strcat(avc->linkData, realName);
+               afs_strcat(avc->linkData, realName);
            }
 
            status->UnixModeBits = 0755;
+           afs_PutCellAlias(ca);
+
+       } else if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_MOUNT) {
+           c = afs_GetCellByIndex(cellidx, READ_LOCK);
+           if (!c) {
+               afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
+                        cellidx);
+               return 0;
+           }
+
+           /*
+            * linkData needs to contain "%cell:volumeid"
+            */
+           namelen = strlen(c->cellName);
+           bp = afs_cv2string(&tbuf[CVBS], avc->f.fid.Fid.Unique);
+           linklen = 2 + namelen + strlen(bp);
+           avc->linkData = afs_osi_Alloc(linklen + 1);
+           strcpy(avc->linkData, "%");
+           afs_strcat(avc->linkData, c->cellName);
+           afs_strcat(avc->linkData, ":");
+           afs_strcat(avc->linkData, bp);
+
+           status->UnixModeBits = 0644;
+           status->ParentVnode = AFS_DYNROOT_MOUNT_VNODE;
+           afs_PutCell(c, READ_LOCK);
+
        } else {
+           c = afs_GetCellByIndex(cellidx, READ_LOCK);
+           if (!c) {
+               afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
+                        cellidx);
+               return 0;
+           }
+
            /*
             * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
             */
@@ -507,14 +741,14 @@ afs_DynrootNewVnode(avc, status)
            linklen = 1 + namelen + 10;
            avc->linkData = afs_osi_Alloc(linklen + 1);
            strcpy(avc->linkData, rw ? "%" : "#");
-           strcat(avc->linkData, c->cellName);
-           strcat(avc->linkData, ":root.cell");
+           afs_strcat(avc->linkData, c->cellName);
+           afs_strcat(avc->linkData, ":root.cell");
 
            status->UnixModeBits = 0644;
+           afs_PutCell(c, READ_LOCK);
        }
 
        status->Length = linklen;
-       afs_PutCell(c, READ_LOCK);
        return 1;
     }
 
@@ -522,21 +756,34 @@ afs_DynrootNewVnode(avc, status)
 }
 
 /*
+ * Make sure dynroot initialization has been done.
+ */
+int
+afs_InitDynroot(void)
+{
+    if (afs_dynrootInit)
+       return 0;
+    AFS_RWLOCK_INIT(&afs_dynrootDirLock, "afs_dynrootDirLock");
+    AFS_RWLOCK_INIT(&afs_dynSymlinkLock, "afs_dynSymlinkLock");
+    afs_dynrootInit = 0;
+    return afs_dynrootCellInit();
+}
+
+/*
  * Enable or disable dynroot.  Returns 0 if successful.
  */
 int
-afs_SetDynrootEnable(enable)
-    int enable;
+afs_SetDynrootEnable(int enable)
 {
     afs_dynrootEnable = enable;
-    return 0;
+    return afs_InitDynroot();
 }
 
 /*
  * Check if dynroot support is enabled.
  */
 int
-afs_GetDynrootEnable()
+afs_GetDynrootEnable(void)
 {
     return afs_dynrootEnable;
 }
@@ -545,17 +792,17 @@ afs_GetDynrootEnable()
  * Remove a temporary symlink entry from /afs.
  */
 int
-afs_DynrootVOPRemove(avc, acred, aname)
-    struct vcache *avc;
-    struct AFS_UCRED *acred;
-    char *aname;
+afs_DynrootVOPRemove(struct vcache *avc, afs_ucred_t *acred, char *aname)
 {
     struct afs_dynSymlink **tpps;
     struct afs_dynSymlink *tps;
-    struct cell *c;
     int found = 0;
 
-    if (acred->cr_uid)
+#if defined(AFS_SUN510_ENV)
+    if (crgetruid(acred))
+#else
+    if (afs_cr_uid(acred))
+#endif
        return EPERM;
 
     ObtainWriteLock(&afs_dynSymlinkLock, 97);
@@ -575,42 +822,29 @@ afs_DynrootVOPRemove(avc, acred, aname)
     }
     ReleaseWriteLock(&afs_dynSymlinkLock);
     if (found) {
-       afs_RefreshDynroot();
+       afs_DynrootInvalidate();
        return 0;
     }
 
-    /* Check if this is an actual cell? */
-    c = afs_GetCellByName2(aname, READ_LOCK, 0 /* no AFSDB */);
-    if (c) {
-       afs_PutCell(c, READ_LOCK);
+    if (afs_CellOrAliasExists(aname))
        return EROFS;
-    } else {
+    else
        return ENOENT;
-    }
 }
 
 /*
  * Create a temporary symlink entry in /afs.
  */
 int
-afs_DynrootVOPSymlink(avc, acred, aname, atargetName)
-    struct vcache *avc;
-    struct AFS_UCRED *acred;
-    char *aname;
-    char *atargetName;
+afs_DynrootVOPSymlink(struct vcache *avc, afs_ucred_t *acred,
+                     char *aname, char *atargetName)
 {
     struct afs_dynSymlink *tps;
-    struct cell *c;
 
-    if (acred->cr_uid)
+    if (afs_cr_uid(acred))
        return EPERM;
-
-    /* Check if it's already a cell */
-    c = afs_GetCellByName2(aname, READ_LOCK, 0 /* no AFSDB */);
-    if (c) {
-       afs_PutCell(c, READ_LOCK);
+    if (afs_CellOrAliasExists(aname))
        return EEXIST;
-    }
 
     /* Check if it's already a symlink */
     ObtainWriteLock(&afs_dynSymlinkLock, 91);
@@ -634,6 +868,6 @@ afs_DynrootVOPSymlink(avc, acred, aname, atargetName)
     afs_dynSymlinkBase = tps;
     ReleaseWriteLock(&afs_dynSymlinkLock);
 
-    afs_RefreshDynroot();
+    afs_DynrootInvalidate();
     return 0;
 }