libafs: track dynroot locks for cmdebug
[openafs.git] / src / afs / afs_dynroot.c
index 892a63d..144a2d4 100644 (file)
  *
  * Implements:
  * afs_IsDynrootFid
+ * afs_IsDynrootMountFid
+ * afs_IsDynrootAnyFid
  * afs_GetDynrootFid
+ * afs_GetDynrootMountFid
  * afs_IsDynroot
+ * afs_IsDynrootMount
+ * afs_IsDynrootAny
  * afs_DynrootInvalidate
  * afs_GetDynroot
  * afs_PutDynroot
  */
 
 #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_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_ALIAS          0x02    /* Corresponds to a struct cell_alias */
-#define VN_TYPE_SYMLINK                0x03    /* 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))
-#define VNUM_FROM_CAIDX_RW(caidx, rw) \
-                               VNUM_FROM_TYPEID(VN_TYPE_ALIAS, \
-                                                ((caidx) << 2 | (rw) << 1))
-
+static int afs_dynrootInit = 0;
 static int afs_dynrootEnable = 0;
 static int afs_dynrootCell = 0;
 
-static afs_rwlock_t afs_dynrootDirLock;
+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_dynrootDirVersion;
 static int afs_dynrootVersion = 1;
@@ -85,7 +74,7 @@ struct afs_dynSymlink {
     char *target;
 };
 
-static afs_rwlock_t afs_dynSymlinkLock;
+afs_rwlock_t afs_dynSymlinkLock;
 /* Start of variables protected by afs_dynSymlinkLock */
 static struct afs_dynSymlink *afs_dynSymlinkBase = NULL;
 static int afs_dynSymlinkIndex = 0;
@@ -94,16 +83,18 @@ static int afs_dynSymlinkIndex = 0;
 /*
  * Set up a cell for dynroot if it's not there yet.
  */
-static int afs_dynrootCellInit()
+static int
+afs_dynrootCellInit(void)
 {
-    if (afs_dynrootEnable && !afs_dynrootCell) {
-       afs_int32 cellHosts[MAXCELLHOSTS];
+    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);
+       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);
@@ -119,33 +110,95 @@ static int afs_dynrootCellInit()
 /*
  * Returns non-zero iff fid corresponds to the top of the dynroot volume.
  */
-int afs_IsDynrootFid(struct VenusFid *fid)
+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 && _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
-       (afs_dynrootEnable &&
-        fid->Cell       == afs_dynrootCell    &&
-        fid->Fid.Volume == AFS_DYNROOT_VOLUME &&
-        fid->Fid.Vnode  == AFS_DYNROOT_VNODE  &&
-        fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
+    return (fid->Cell == afs_dynrootCell
+           && fid->Fid.Volume == AFS_DYNROOT_VOLUME);
 }
 
 /*
  * Obtain the magic dynroot volume Fid.
  */
-void afs_GetDynrootFid(struct VenusFid *fid) 
+void
+afs_GetDynrootFid(struct VenusFid *fid)
 {
-    fid->Cell       = afs_dynrootCell;
+    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;
 }
 
 /*
  * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
  */
-int afs_IsDynroot(struct vcache *avc)
+int
+afs_IsDynroot(struct vcache *avc)
+{
+    return afs_IsDynrootFid(&avc->f.fid);
+}
+
+int
+afs_IsDynrootMount(struct vcache *avc)
 {
-    return afs_IsDynrootFid(&avc->fid);
+    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;
 }
 
 /*
@@ -153,10 +206,11 @@ int afs_IsDynroot(struct vcache *avc)
  * caller has allocated the directory to be large enough to hold
  * the necessary entry.
  */
-static void afs_dynroot_addDirEnt(struct DirHeader *dirHeader, 
-       int *curPageP, int *curChunkP, char *name, int vnode)
+static void
+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;
@@ -174,23 +228,23 @@ static void afs_dynroot_addDirEnt(struct DirHeader *dirHeader,
        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);
 
@@ -203,7 +257,7 @@ static void afs_dynroot_addDirEnt(struct DirHeader *dirHeader,
     /*
      * Add the new entry to the correct hash chain.
      */
-    i = DirHash(name);
+    i = afs_dir_DirHash(name);
     dirEntry->next = dirHeader->hashTable[i];
     dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
 
@@ -218,7 +272,8 @@ static void afs_dynroot_addDirEnt(struct DirHeader *dirHeader,
  * Invalidate the /afs vnode for dynroot; called when the underlying
  * directory has changed and needs to be re-read.
  */
-void afs_DynrootInvalidate(void)
+void
+afs_DynrootInvalidate(void)
 {
     afs_int32 retry;
     struct vcache *tvc;
@@ -240,7 +295,7 @@ void afs_DynrootInvalidate(void)
        ReleaseReadLock(&afs_xvcache);
     } while (retry);
     if (tvc) {
-       tvc->states &= ~(CStatd | CUnique);
+       tvc->f.states &= ~(CStatd | CUnique);
        osi_dnlc_purgedp(tvc);
        afs_PutVCache(tvc);
     }
@@ -251,14 +306,15 @@ void afs_DynrootInvalidate(void)
  * cells.  Useful when the list of cells has changed due to
  * an AFSDB lookup, for instance.
  */
-static void afs_RebuildDynroot(void)
+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;
     int linkCount = 0;
@@ -278,53 +334,46 @@ static void afs_RebuildDynroot(void)
     /* Reserve space for "." and ".." */
     curChunk += 2;
 
-    for (cellidx = 0; ; cellidx++) {
-       c = afs_GetCellByIndex(cellidx, READ_LOCK);
-       if (!c) break;
-       if (c->cellNum == afs_dynrootCell) continue;
+    /* Reserve space for the dynamic-mount directory */
+    afs_dynroot_computeDirEnt(AFS_DYNROOT_MOUNTNAME, &curPage, &curChunk);
 
-       sizeOfCurEntry = afs_dir_NameBlobs(c->cellName);
-       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;
-
-       dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
+       dotLen = strlen(c->cellName) + 2;
+       dotCell = afs_osi_Alloc(dotLen);
+       osi_Assert(dotCell != NULL);
        strcpy(dotCell, ".");
-       strcat(dotCell, c->cellName);
-       sizeOfCurEntry = afs_dir_NameBlobs(dotCell);
-       if (curChunk + sizeOfCurEntry > EPP) {
-           curPage++;
-           curChunk = 1;
-       }
-       curChunk += sizeOfCurEntry;
+       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++) {
+    for (aliasidx = 0;; aliasidx++) {
        ca = afs_GetCellAlias(aliasidx);
-       if (!ca) break;
-
-       sizeOfCurEntry = afs_dir_NameBlobs(ca->alias);
-       if (curChunk + sizeOfCurEntry > EPP) {
-           curPage++;
-           curChunk = 1;
-       }
-       curChunk += sizeOfCurEntry;
+       if (!ca)
+           break;
 
-       dotCell = afs_osi_Alloc(strlen(ca->alias) + 2);
+       dotLen = strlen(ca->alias) + 2;
+       dotCell = afs_osi_Alloc(dotLen);
+       osi_Assert(dotCell != NULL);
        strcpy(dotCell, ".");
-       strcat(dotCell, ca->alias);
-       sizeOfCurEntry = afs_dir_NameBlobs(dotCell);
-       if (curChunk + sizeOfCurEntry > EPP) {
-           curPage++;
-           curChunk = 1;
-       }
-       curChunk += sizeOfCurEntry;
+       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;
@@ -332,24 +381,20 @@ static void afs_RebuildDynroot(void)
     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;
     }
 
     dirSize = (curPage + 1) * AFS_PAGESIZE;
     newDir = afs_osi_Alloc(dirSize);
+    osi_Assert(newDir != NULL);
 
     /*
      * Now actually construct the directory.
      */
     curChunk = 13;
     curPage = 0;
-    dirHeader = (struct DirHeader *) newDir;
+    dirHeader = (struct DirHeader *)newDir;
 
     dirHeader->header.pgcount = 0;
     dirHeader->header.tag = htons(1234);
@@ -357,31 +402,39 @@ static void afs_RebuildDynroot(void)
 
     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++)
        dirHeader->alloMap[i] = EPP;
-    for (i = 0; i < NHASHENT; i++)
-       dirHeader->hashTable[i] = 0;
+    memset(dirHeader->hashTable, 0, NHASHENT * sizeof(dirHeader->hashTable[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);
-       if (!c) continue;
-       if (c->cellNum == afs_dynrootCell) continue;
+       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);
+       osi_Assert(dotCell != NULL);
        strcpy(dotCell, ".");
-       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_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);
@@ -389,30 +442,34 @@ static void afs_RebuildDynroot(void)
 
     for (aliasidx = 0; aliasidx < maxaliasidx; aliasidx++) {
        ca = afs_GetCellAlias(aliasidx);
-       if (!ca) continue;
+       if (!ca)
+           continue;
 
-       dotCell = afs_osi_Alloc(strlen(ca->alias) + 2);
+       dotLen = strlen(ca->alias) + 2;
+       dotCell = afs_osi_Alloc(dotLen);
+       osi_Assert(dotCell != NULL);
        strcpy(dotCell, ".");
-       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_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;
     }
 
     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;
@@ -420,12 +477,56 @@ static void afs_RebuildDynroot(void)
     ReleaseWriteLock(&afs_dynrootDirLock);
 }
 
+static void
+afs_RebuildDynrootMount(void)
+{
+    int i;
+    int curChunk, curPage;
+    char *newDir;
+    struct DirHeader *dirHeader;
+
+    newDir = afs_osi_Alloc(AFS_PAGESIZE);
+    osi_Assert(newDir != NULL);
+
+    /*
+     * 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;
+    memset(dirHeader->hashTable, 0, NHASHENT * sizeof(dirHeader->hashTable[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);
+}
+
 /*
  * Returns a pointer to the base of the dynroot directory in memory,
  * length thereof, and a FetchStatus.
  */
-void afs_GetDynroot(char **dynrootDir, int *dynrootLen,
-       struct AFSFetchStatus *status)
+void
+afs_GetDynroot(char **dynrootDir, int *dynrootLen,
+              struct AFSFetchStatus *status)
 {
     ObtainReadLock(&afs_dynrootDirLock);
     if (!afs_dynrootDir || afs_dynrootDirVersion != afs_dynrootVersion) {
@@ -434,28 +535,62 @@ void afs_GetDynroot(char **dynrootDir, int *dynrootLen,
        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(void)
+void
+afs_PutDynroot(void)
 {
     ReleaseReadLock(&afs_dynrootDirLock);
 }
@@ -465,21 +600,30 @@ void afs_PutDynroot(void)
  * is non-zero if this vnode is handled by dynroot, in which case
  * FetchStatus will be filled in.
  */
-int afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
+int
+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;
+       goto succeed;
+    }
+
+    if (afs_IsDynrootMount(avc)) {
+       afs_GetDynrootMount(0, 0, status);
+       afs_PutDynroot();
+       goto succeed;
     }
 
     /*
      * Check if this is an entry under /afs, e.g. /afs/cellname.
      */
-    if (avc->fid.Cell       == afs_dynrootCell &&
-       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;
@@ -487,49 +631,55 @@ int afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
 
        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;
            }
 
            if (ts) {
                linklen = strlen(ts->target);
                avc->linkData = afs_osi_Alloc(linklen + 1);
+               osi_Assert(avc->linkData != NULL);
                strcpy(avc->linkData, ts->target);
 
-               status->Length       = linklen;
+               status->Length = linklen;
                status->UnixModeBits = 0755;
            }
            ReleaseReadLock(&afs_dynSymlinkLock);
 
-           return ts ? 1 : 0;
+           if (ts)
+               goto succeed;
+
+           return 0;
        }
 
-       if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_CELL &&
-           VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_ALIAS) {
+       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);
 
-       if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_ALIAS) {
+       if (VNUM_TO_VNTYPE(avc->f.fid.Fid.Vnode) == VN_TYPE_ALIAS) {
            char *realName;
 
            ca = afs_GetCellAlias(cellidx);
@@ -553,12 +703,39 @@ int afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
                int namelen = strlen(realName);
                linklen = rw + namelen;
                avc->linkData = afs_osi_Alloc(linklen + 1);
+               osi_Assert(avc->linkData != NULL);
                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);
+           osi_Assert(avc->linkData != NULL);
+           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) {
@@ -573,34 +750,72 @@ int afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
            namelen = strlen(c->cellName);
            linklen = 1 + namelen + 10;
            avc->linkData = afs_osi_Alloc(linklen + 1);
+           osi_Assert(avc->linkData != NULL);
            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;
-       return 1;
+       goto succeed;
     }
 
     return 0;
+
+    /* make sure we set type correctly when we do this. used to stay VREG */
+ succeed:
+    switch (status->FileType) {
+    case File:
+       vSetType(avc, VREG);
+       break;
+    case Directory:
+       vSetType(avc, VDIR);
+       break;
+    case SymbolicLink:
+       if (afs_fakestat_enable && (avc->f.m.Mode & 0111) == 0)
+           vSetType(avc, VDIR);
+       else
+           vSetType(avc, VLNK);
+       break;
+    default:
+       /* shouldn't happen */
+      ;
+    }
+    return 1;
+}
+
+/*
+ * 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(int enable)
+int
+afs_SetDynrootEnable(int enable)
 {
     afs_dynrootEnable = enable;
-    return afs_dynrootCellInit();
+    return afs_InitDynroot();
 }
 
 /*
  * Check if dynroot support is enabled.
  */
-int afs_GetDynrootEnable(void)
+int
+afs_GetDynrootEnable(void)
 {
     return afs_dynrootEnable;
 }
@@ -608,14 +823,18 @@ int afs_GetDynrootEnable(void)
 /*
  * Remove a temporary symlink entry from /afs.
  */
-int afs_DynrootVOPRemove(struct vcache *avc, struct AFS_UCRED *acred,
-       char *aname)
+int
+afs_DynrootVOPRemove(struct vcache *avc, afs_ucred_t *acred, char *aname)
 {
     struct afs_dynSymlink **tpps;
     struct afs_dynSymlink *tps;
     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);
@@ -648,12 +867,13 @@ int afs_DynrootVOPRemove(struct vcache *avc, struct AFS_UCRED *acred,
 /*
  * Create a temporary symlink entry in /afs.
  */
-int afs_DynrootVOPSymlink(struct vcache *avc, struct AFS_UCRED *acred, 
-       char *aname, char *atargetName)
+int
+afs_DynrootVOPSymlink(struct vcache *avc, afs_ucred_t *acred,
+                     char *aname, char *atargetName)
 {
     struct afs_dynSymlink *tps;
 
-    if (acred->cr_uid)
+    if (afs_cr_uid(acred))
        return EPERM;
     if (afs_CellOrAliasExists(aname))
        return EEXIST;
@@ -671,11 +891,14 @@ int afs_DynrootVOPSymlink(struct vcache *avc, struct AFS_UCRED *acred,
 
     /* Doesn't already exist -- go ahead and create it */
     tps = afs_osi_Alloc(sizeof(*tps));
+    osi_Assert(tps != NULL);
     tps->index = afs_dynSymlinkIndex++;
     tps->next = afs_dynSymlinkBase;
     tps->name = afs_osi_Alloc(strlen(aname) + 1);
+    osi_Assert(tps->name != NULL);
     strcpy(tps->name, aname);
     tps->target = afs_osi_Alloc(strlen(atargetName) + 1);
+    osi_Assert(tps->target != NULL);
     strcpy(tps->target, atargetName);
     afs_dynSymlinkBase = tps;
     ReleaseWriteLock(&afs_dynSymlinkLock);