Change the meaning of the -fakestat switch to only enable fakestat
[openafs.git] / src / afsd / afsd.c
index d2497c7..5ba6687 100644 (file)
   *               This option is now disabled.
   *    -logfile   Place where to put the logfile (default in <cache>/etc/AFSLog.
   *    -waitclose make close calls always synchronous (slows em down, tho)
+  *    -files_per_subdir [n]   number of files per cache subdir. (def=2048)
   *    -shutdown  Shutdown afs daemons
   *---------------------------------------------------------------------------*/
 
-#include <afs/param.h>
 #include <afsconfig.h>
+#include <afs/param.h>
 
 RCSID("$Header$");
 
@@ -112,6 +113,14 @@ RCSID("$Header$");
 #include <sys/vfs.h>
 #endif
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
 #include <netinet/in.h>
 #include <afs/afs_args.h>
 #include <afs/cellconfig.h>
@@ -145,10 +154,14 @@ void set_staticaddrs(void);
 #if AFS_HAVE_STATVFS
 #include <sys/statvfs.h>
 #else
-#if !defined(AFS_OSF_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_FBSD_ENV)
+#if defined(AFS_SUN_ENV)
+#include <sys/vfs.h>
+#else
+#if !defined(AFS_OSF_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
 #include <sys/statfs.h>
 #endif
 #endif
+#endif
 
 #undef VIRTUE
 #undef VICE
@@ -157,6 +170,7 @@ void set_staticaddrs(void);
 #define        AFSLOGFILE      "AFSLog"
 #define        DCACHEFILE      "CacheItems"
 #define        VOLINFOFILE     "VolumeItems"
+#define CELLINFOFILE   "CellItems"
 
 #define MAXIPADDRS 1024
 
@@ -198,10 +212,11 @@ char cacheBaseDir[1024];          /*Where the workstation AFS cache lives*/
 char confDir[1024];                    /*Where the workstation AFS configuration lives*/
 char fullpn_DCacheFile[1024];          /*Full pathname of DCACHEFILE*/
 char fullpn_VolInfoFile[1024];         /*Full pathname of VOLINFOFILE*/
+char fullpn_CellInfoFile[1024];                /*Full pathanem of CELLINFOFILE*/
 char fullpn_AFSLogFile[1024];          /*Full pathname of AFSLOGFILE*/
 char fullpn_CacheInfo[1024];           /*Full pathname of CACHEINFO*/
 char fullpn_VFile[1024];               /*Full pathname of data cache files*/
-char *vFileNumber;                     /*Ptr to the number part of above pathname*/
+char *vFilePtr;                                /*Ptr to the number part of above pathname*/
 int sawCacheMountDir = 0;              /* from cmd line */
 int sawCacheBaseDir = 0;
 int sawCacheBlocks = 0;
@@ -211,10 +226,10 @@ char cacheMountDir[1024];         /*Mount directory for AFS*/
 char rootVolume[64] = "root.afs";      /*AFS root volume name*/
 afs_int32 cacheSetTime = 1;                    /*Keep checking time to avoid drift?*/
 afs_int32 isHomeCell;                  /*Is current cell info for the home cell?*/
-afs_int32 lookingForHomeCell;          /*Are we still looking for the home cell?*/
 int createAndTrunc = O_CREAT | O_TRUNC; /*Create & truncate on open*/
 int ownerRWmode        = 0600;                 /*Read/write OK by owner*/
 static int filesSet = 0;               /*True if number of files explicitly set*/
+static int nFilesPerDir = 2048;                /* # files per cache dir */
 static int nDaemons = 2;               /* Number of background daemons */
 static int chunkSize = 0;               /* 2^chunkSize bytes per chunk */
 static int dCacheSize = 300;            /* # of dcache entries */
@@ -229,6 +244,8 @@ static int enable_process_stats = 0;        /* enable rx stats */
 #ifdef AFS_AFSDB_ENV
 static int enable_afsdb = 0;           /* enable AFSDB support */
 #endif
+static int enable_dynroot = 0;         /* enable dynroot support */
+static int enable_fakestat = 0;                /* enable fakestat support */
 #ifdef notdef
 static int inodes = 60;                        /* VERY conservative, but has to be */
 #endif
@@ -241,12 +258,24 @@ int afsd_CloseSynch = 0;          /*Are closes synchronous or not? */
 #else
 #define AFSD_INO_T afs_uint32
 #endif
+struct afsd_file_list {
+  int                  fileNum;
+  struct afsd_file_list        *next;
+};
+struct afsd_file_list **cache_dir_filelist = NULL;
+int *cache_dir_list = NULL;            /* Array of cache subdirs */
+int *dir_for_V = NULL;                 /* Array: dir of each cache file.
+                                        * -1: file does not exist
+                                        * -2: file exists in top-level
+                                        * >=0: file exists in Dxxx
+                                        */
 AFSD_INO_T *inode_for_V;               /* Array of inodes for desired
                                         * cache files */
-int missing_DCacheFile = 1;            /*Is the DCACHEFILE missing?*/
-int missing_VolInfoFile        = 1;            /*Is the VOLINFOFILE missing?*/
-int afsd_rmtsys = 0;                           /* Default: don't support rmtsys */
-struct afs_cacheParams cparams;          /* params passed to cache manager */
+int missing_DCacheFile  = 1;           /*Is the DCACHEFILE missing?*/
+int missing_VolInfoFile  = 1;          /*Is the VOLINFOFILE missing?*/
+int missing_CellInfoFile = 1;          /*Is the CELLINFOFILE missing?*/
+int afsd_rmtsys = 0;                   /* Default: don't support rmtsys */
+struct afs_cacheParams cparams;         /* params passed to cache manager */
 
 static int HandleMTab();
 
@@ -319,8 +348,6 @@ int ParseCacheInfoFile()
            printf("\t%d out of 3 fields successfully parsed.\n",
                   parseResult);
 
-       printf("\tcacheMountDir: '%s'\n\tcacheBaseDir: '%s'\n\tcacheBlocks: %d\n",
-              cacheMountDir, cacheBaseDir, cacheBlocks);
        return(1);
     }
 
@@ -403,6 +430,7 @@ int cs;
   *
   * Arguments:
   *    fname : Char ptr to the filename to parse.
+  *    max   : integer for the highest number to accept
   *
   * Returns:
   *    >= 0 iff the file is really a data cache file numbered from 0 to cacheFiles-1, or
@@ -415,21 +443,23 @@ int cs;
   *    None.
   *---------------------------------------------------------------------------*/
 
-int GetVFileNumber(fname)
+static int doGetXFileNumber(fname, filechar, maxNum)
     char *fname;
+    char filechar;
+    int maxNum;
 {
     int        computedVNumber;    /*The computed file number we return*/
     int        filenameLen;        /*Number of chars in filename*/
     int        currDigit;          /*Current digit being processed*/
 
     /*
-     * The filename must have at least two characters, the first of which must be a ``V''
+     * The filename must have at least two characters, the first of which must be a ``filechar''
      * and the second of which cannot be a zero unless the file is exactly two chars long.
      */
     filenameLen = strlen(fname);
     if (filenameLen < 2)
        return(-1);
-    if (fname[0] != 'V')
+    if (fname[0] != filechar)
        return(-1);
     if ((filenameLen > 2) && (fname[1] == '0'))
        return(-1);
@@ -453,6 +483,21 @@ int GetVFileNumber(fname)
        return(-1);
 }
 
+int GetVFileNumber(fname, maxFile)
+    char *fname;
+    int maxFile;
+{
+    return doGetXFileNumber(fname, 'V', maxFile);
+}
+
+int GetDDirNumber(fname, maxDir)
+    char *fname;
+    int maxDir;
+{
+    return doGetXFileNumber(fname, 'D', maxDir);
+}
+
+
 /*-----------------------------------------------------------------------------
   * CreateCacheFile
   *
@@ -462,6 +507,8 @@ int GetVFileNumber(fname)
   *
   * Arguments:
   *    fname : Full pathname of file to create.
+  *    statp : A pointer to a stat buffer which, if NON-NULL, will be
+  *            filled by fstat()
   *
   * Returns:
   *    0   iff the file was created,
@@ -474,8 +521,81 @@ int GetVFileNumber(fname)
   *    As described.
   *---------------------------------------------------------------------------*/
 
-int CreateCacheFile(fname)
+static int CreateCacheSubDir (basename, dirNum)
+     char *basename;
+     int dirNum;
+{
+    static char rn[] = "CreateCacheSubDir"; /* Routine Name */
+    char dir[1024];
+    int ret;
+
+    /* Build the new cache subdirectory */
+    sprintf (dir, "%s/D%d", basename, dirNum);
+
+    if (afsd_verbose)
+       printf("%s: Creating cache subdir '%s'\n",
+              rn, dir);
+
+    if ((ret = mkdir(dir, 0700)) != 0) {
+        printf("%s: Can't create '%s', error return is %d (%d)\n",
+              rn, dir, ret, errno);
+        if (errno != EEXIST)
+           return (-1);
+    }
+
+    /* Mark this directory as created */
+    cache_dir_list[dirNum] = 0;
+
+    /* And return success */
+    return (0);
+}
+
+static int MoveCacheFile (basename, fromDir, toDir, cacheFile, maxDir)
+     char *basename;
+     int fromDir, toDir, cacheFile, maxDir;
+{
+  static char rn[] = "MoveCacheFile";
+  char from[1024], to[1024];
+  int ret;
+
+  if (cache_dir_list[toDir] < 0 &&
+      (ret = CreateCacheSubDir(basename, toDir))) {
+    printf("%s: Can't create directory '%s/D%d'\n", rn, basename, toDir);
+    return ret;
+  }
+
+  /* Build the from,to dir */
+  if (fromDir < 0) {
+    /* old-style location */
+    snprintf (from, sizeof(from), "%s/V%d", basename, cacheFile);
+  } else {
+    snprintf (from, sizeof(from), "%s/D%d/V%d", basename, fromDir, cacheFile);
+  }
+
+  snprintf (to, sizeof(from), "%s/D%d/V%d", basename, toDir, cacheFile);
+
+  if (afsd_verbose)
+    printf("%s: Moving cacheFile from '%s' to '%s'\n",
+          rn, from, to);
+  
+  if ((ret = rename (from, to)) != 0) {
+    printf("%s: Can't rename '%s' to '%s', error return is %d (%d)\n",
+          rn, from, to, ret, errno);
+    return -1;
+  }
+
+  /* Reset directory pointer; fix file counts */
+  dir_for_V[cacheFile] = toDir;
+  cache_dir_list[toDir]++;
+  if (fromDir < maxDir && fromDir >= 0)
+    cache_dir_list[fromDir]--;
+  
+  return 0;
+}
+
+int CreateCacheFile(fname, statp)
     char *fname;
+    struct stat *statp;
 {
     static char        rn[] = "CreateCacheFile";   /*Routine name*/
     int        cfd;                                /*File descriptor to AFS cache file*/
@@ -490,6 +610,14 @@ int CreateCacheFile(fname)
               rn, fname, cfd, errno);
        return(-1);
     }
+    if (statp != NULL) {
+        closeResult = fstat (cfd, statp);
+       if (closeResult) {
+           printf("%s: Can't stat newly-created AFS cache file '%s' (code %d)\n",
+                  rn, fname, errno);
+           return(-1);
+       }
+    }
     closeResult = close(cfd);
     if (closeResult) {
        printf("%s: Can't close newly-created AFS cache file '%s' (code %d)\n",
@@ -500,12 +628,22 @@ int CreateCacheFile(fname)
     return(0);
 }
 
+static void CreateFileIfMissing(char *fullpn, int missing)
+{
+    if (missing) {
+       if (afsd_verbose)
+           printf("CreateFileIfMissing: Creating '%s'\n", fullpn);
+       if (CreateCacheFile(fullpn, NULL))
+           printf("CreateFileIfMissing: Can't create '%s'\n", fullpn);
+    }
+}
+
 /*-----------------------------------------------------------------------------
   * SweepAFSCache
   *
   * Description:
   *    Sweep through the AFS cache directory, recording the inode number for
-  *    each valid data cache file there.  Also, delete any file that doesn't beint32
+  *    each valid data cache file there.  Also, delete any file that doesn't belong
   *    in the cache directory during this sweep, and remember which of the other
   *    residents of this directory were seen.  After the sweep, we create any data
   *    cache files that were missing.
@@ -528,10 +666,14 @@ int CreateCacheFile(fname)
   *    explained above.
   *---------------------------------------------------------------------------*/
 
-int SweepAFSCache(vFilesFound)
-    int *vFilesFound;
+
+static int doSweepAFSCache(vFilesFound,directory,dirNum,maxDir)
+     int *vFilesFound;
+     char *directory;          /* /path/to/cache/directory */
+     int dirNum;               /* current directory number */
+     int maxDir;               /* maximum directory number */
 {
-    static char        rn[] = "SweepAFSCache"; /*Routine name*/
+    static char rn[] = "doSweepAFSCache"; /* Routine Name */
     char fullpn_FileToDelete[1024];    /*File to be deleted from cache*/
     char *fileToDelete;                        /*Ptr to last component of above*/
     DIR        *cdirp;                         /*Ptr to cache directory structure*/
@@ -541,36 +683,30 @@ int SweepAFSCache(vFilesFound)
     struct dirent *currp;              /*Current directory entry*/
 #endif
     int        vFileNum;                       /*Data cache file's associated number*/
-
-    if (cacheFlags & AFSCALL_INIT_MEMCACHE) {
-       if (afsd_debug)
-           printf("%s: Memory Cache, no cache sweep done\n", rn);
-       *vFilesFound = 0;
-       return 0;
-    }
+    int thisDir;                       /* A directory number */
+    int highDir = 0;
 
     if (afsd_debug)
-       printf("%s: Opening cache directory '%s'\n",
-              rn, cacheBaseDir);
+       printf("%s: Opening cache directory '%s'\n", rn, directory);
 
-    if (chmod(cacheBaseDir, 0700)) {           /* force it to be 700 */
-       printf("%s: Can't 'chmod 0700' the cache dir, '%s'.\n",
-              rn, cacheBaseDir);
+    if (chmod(directory, 0700)) {              /* force it to be 700 */
+       printf("%s: Can't 'chmod 0700' the cache dir, '%s'.\n", rn, directory);
        return (-1);
     }
-    cdirp = opendir(cacheBaseDir);
+    cdirp = opendir(directory);
     if (cdirp == (DIR *)0) {
-       printf("%s: Can't open AFS cache directory, '%s'.\n",
-              rn, cacheBaseDir);
+       printf("%s: Can't open AFS cache directory, '%s'.\n", rn, directory);
        return(-1);
     }
 
     /*
-     * Scan the directory entries, remembering data cache file inodes and the existance
-     * of other important residents.  Delete all files that don't belong here.
+     * Scan the directory entries, remembering data cache file inodes
+     * and the existance of other important residents.  Recurse into
+     * the data subdirectories.
+     *
+     * Delete all files and directories that don't belong here.
      */
-    *vFilesFound = 0;
-    sprintf(fullpn_FileToDelete, "%s/", cacheBaseDir);
+    sprintf(fullpn_FileToDelete, "%s/", directory);
     fileToDelete = fullpn_FileToDelete + strlen(fullpn_FileToDelete);
 
 #ifdef AFS_SGI62_ENV
@@ -592,29 +728,98 @@ int SweepAFSCache(vFilesFound)
        }
 
        /*
-        * Guess current entry is for a data cache file.
+        * If dirNum < 0, we are a top-level cache directory and should
+        * only contain sub-directories and other sundry files.  Therefore,
+        * V-files are valid only if dirNum >= 0, and Directories are only
+        * valid if dirNum < 0.
         */
-       vFileNum = GetVFileNumber(currp->d_name);
-       if (vFileNum >= 0) {
+
+       if (*(currp->d_name) == 'V' &&
+           ((vFileNum = GetVFileNumber(currp->d_name, cacheFiles)) >= 0)) {
            /*
-            * Found a valid data cache filename.  Remember this file's inode and bump
-            * the number of files found.
+            * Found a valid data cache filename.  Remember this
+            * file's inode, directory, and bump the number of files found
+            * total and in this directory.
             */
            inode_for_V[vFileNum] = currp->d_ino;
+           dir_for_V[vFileNum] = dirNum; /* remember this directory */
+
+           if (!maxDir) {
+             /* If we're in a real subdir, mark this file to be moved
+              * if we've already got too many files in this directory
+              */
+             assert(dirNum >= 0);
+             cache_dir_list[dirNum]++; /* keep directory's file count */
+             if (cache_dir_list[dirNum] > nFilesPerDir) {
+               /* Too many files -- add to filelist */
+               struct afsd_file_list *tmp = (struct afsd_file_list *)
+                 malloc(sizeof(*tmp));
+               if (!tmp)
+                 printf ("%s: MALLOC FAILED allocating file_list entry\n",
+                         rn);
+               else {
+                 tmp->fileNum = vFileNum;
+                 tmp->next = cache_dir_filelist[dirNum];
+                 cache_dir_filelist[dirNum] = tmp;
+               }
+             }
+           }
            (*vFilesFound)++;
        }
-       else if (strcmp(currp->d_name, DCACHEFILE) == 0) {
+       else if (dirNum < 0 && (*(currp->d_name) == 'D') &&
+                GetDDirNumber(currp->d_name, 1<<30) >= 0) {
+         int retval = 0;
+         if ((vFileNum = GetDDirNumber(currp->d_name, maxDir)) >= 0) {
+           /* Found a valid cachefile sub-Directory.  Remember this number
+            * and recurse into it.  Note that subdirs cannot have subdirs.
+            */
+           retval = 1;
+         } else if ((vFileNum = GetDDirNumber(currp->d_name,  1<<30)) >= 0) {
+           /* This directory is going away, but figure out if there
+            * are any cachefiles in here that should be saved by
+            * moving them to other cache directories.  This directory
+            * will be removed later.
+            */
+           retval = 2;
+         }
+
+         /* Save the highest directory number we've seen */
+         if (vFileNum > highDir)
+           highDir = vFileNum;
+
+         /* If this directory is staying, be sure to mark it as 'found' */
+         if (retval == 1) cache_dir_list[vFileNum] = 0;
+
+         /* Print the dirname for recursion */
+         sprintf(fileToDelete, "%s", currp->d_name);
+
+         /* Note: vFileNum is the directory number */
+         retval = doSweepAFSCache(vFilesFound, fullpn_FileToDelete,
+                                  vFileNum, (retval == 1 ? 0 : -1));
+         if (retval) {
+           printf ("%s: Recursive sweep failed on directory %s\n",
+                   rn, currp->d_name);
+           return retval;
+         }
+       }
+       else if (dirNum < 0 && strcmp(currp->d_name, DCACHEFILE) == 0) {
            /*
             * Found the file holding the dcache entries.
             */
            missing_DCacheFile = 0;
        }
-       else if (strcmp(currp->d_name, VOLINFOFILE) == 0) {
+       else if (dirNum < 0 && strcmp(currp->d_name, VOLINFOFILE) == 0) {
            /*
             * Found the file holding the volume info.
             */
            missing_VolInfoFile = 0;
        }
+       else if (dirNum < 0 && strcmp(currp->d_name, CELLINFOFILE) == 0) {
+           /*
+            * Found the file holding the cell info.
+            */
+           missing_CellInfoFile = 0;
+       }
        else  if ((strcmp(currp->d_name,          ".") == 0) ||
                  (strcmp(currp->d_name,         "..") == 0) ||
 #ifdef AFS_DECOSF_ENV
@@ -623,6 +828,10 @@ int SweepAFSCache(vFilesFound)
                  (strcmp(currp->d_name,         "quota.user") == 0) ||
                  (strcmp(currp->d_name,         "quota.group") == 0) ||
 #endif
+#ifdef AFS_LINUX22_ENV
+                 /* this is the ext3 journal file */
+                 (strcmp(currp->d_name,         ".journal") == 0) ||
+#endif
                  (strcmp(currp->d_name, "lost+found") == 0)) {
            /*
             * Don't do anything - this file is legit, and is to be left alone.
@@ -630,55 +839,136 @@ int SweepAFSCache(vFilesFound)
        }
        else {
            /*
-            * This file doesn't belong in the cache.  Nuke it.
+            * This file/directory doesn't belong in the cache.  Nuke it.
             */
            sprintf(fileToDelete, "%s", currp->d_name);
            if (afsd_verbose)
                printf("%s: Deleting '%s'\n",
                       rn, fullpn_FileToDelete);
            if (unlink(fullpn_FileToDelete)) {
-               printf("%s: Can't unlink '%s', errno is %d\n",
-                      rn, fullpn_FileToDelete, errno);
+               if (errno == EISDIR && *fileToDelete == 'D') {
+                   if (rmdir(fullpn_FileToDelete)) {
+                       printf("%s: Can't rmdir '%s', errno is %d\n",
+                              rn, fullpn_FileToDelete, errno);
+                   }
+               } else
+                   printf("%s: Can't unlink '%s', errno is %d\n",
+                          rn, fullpn_FileToDelete, errno);
            }
        }
     }
 
-    /*
-     * Create all the cache files that are missing.
-     */
-    if (missing_DCacheFile) {
-       if (afsd_verbose)
-           printf("%s: Creating '%s'\n",
-                  rn, fullpn_DCacheFile);
-       if (CreateCacheFile(fullpn_DCacheFile))
-           printf("%s: Can't create '%s'\n",
-                  rn, fullpn_DCacheFile);
-    }
-    if (missing_VolInfoFile) {
-       if (afsd_verbose)
-           printf("%s: Creating '%s'\n",
-                  rn, fullpn_VolInfoFile);
-       if (CreateCacheFile(fullpn_VolInfoFile))
-           printf("%s: Can't create '%s'\n",
-                  rn, fullpn_VolInfoFile);
-    }
+    if (dirNum < 0) {
 
-    if (*vFilesFound < cacheFiles) {
        /*
-        * We came up short on the number of data cache files found.  Scan through the inode
-        * list and create all missing files.
+        * Create all the cache files that are missing.
+        */
+       CreateFileIfMissing(fullpn_DCacheFile, missing_DCacheFile);
+       CreateFileIfMissing(fullpn_VolInfoFile, missing_VolInfoFile);
+       CreateFileIfMissing(fullpn_CellInfoFile, missing_CellInfoFile);
+
+       /* ADJUST CACHE FILES */
+
+       /* First, let's walk through the list of files and figure out
+        * if there are any leftover files in extra directories or
+        * missing files.  Move the former and create the latter in
+        * subdirs with extra space.
         */
-       for (vFileNum = 0; vFileNum < cacheFiles; vFileNum++)
-           if (inode_for_V[vFileNum] == (AFSD_INO_T)0) {
-               sprintf(vFileNumber, "%d", vFileNum);
-               if (afsd_verbose)
-                   printf("%s: Creating '%s'\n",
-                          rn, fullpn_VFile);
-               if (CreateCacheFile(fullpn_VFile))
-                   printf("%s: Can't create '%s'\n",
-                          rn, fullpn_VFile);
+
+       thisDir = 0;            /* Keep track of which subdir has space */
+
+       for (vFileNum = 0; vFileNum < cacheFiles; vFileNum++) {
+         if (dir_for_V[vFileNum] == -1) {
+           /* This file does not exist.  Create it in the first
+            * subdir that still has extra space.
+            */
+           while (thisDir < maxDir &&
+                  cache_dir_list[thisDir] >= nFilesPerDir)
+             thisDir++;
+           if (thisDir >= maxDir)
+             printf("%s: can't find directory to create V%d\n", rn, vFileNum);
+           else {
+             struct stat statb;
+             assert (inode_for_V[vFileNum] == (AFSD_INO_T)0);
+             sprintf(vFilePtr, "D%d/V%d", thisDir, vFileNum);
+             if (afsd_verbose)
+               printf("%s: Creating '%s'\n", rn, fullpn_VFile);
+             if (cache_dir_list[thisDir] < 0 &&
+                 CreateCacheSubDir(directory, thisDir))
+               printf("%s: Can't create directory for '%s'\n",
+                      rn, fullpn_VFile);
+             if (CreateCacheFile(fullpn_VFile, &statb))
+               printf("%s: Can't create '%s'\n", rn, fullpn_VFile);
+             else {
+               inode_for_V[vFileNum] = statb.st_ino;
+               dir_for_V[vFileNum] = thisDir;
+               cache_dir_list[thisDir]++;
+               (*vFilesFound)++;
+             }
            }
-    }
+
+         } else if (dir_for_V[vFileNum] >= maxDir ||
+                    dir_for_V[vFileNum] == -2) {
+           /* This file needs to move; move it to the first subdir
+            * that has extra space.  (-2 means it's in the toplevel)
+            */
+           while (thisDir < maxDir && cache_dir_list[thisDir] >= nFilesPerDir)
+             thisDir++;
+           if (thisDir >= maxDir)
+             printf("%s: can't find directory to move V%d\n", rn, vFileNum);
+           else {
+             if (MoveCacheFile (directory, dir_for_V[vFileNum], thisDir,
+                                vFileNum, maxDir)) {
+               /* Cannot move.  Ignore this file??? */
+               /* XXX */
+             }
+           }
+         }
+       } /* for */
+
+       /* At this point, we've moved all of the valid cache files
+        * into the valid subdirs, and created all the extra
+        * cachefiles we need to create.  Next, rebalance any subdirs
+        * with too many cache files into the directories with not
+        * enough cache files.  Note that thisDir currently sits at
+        * the lowest subdir that _may_ have room.
+        */
+
+       for (dirNum = 0; dirNum < maxDir; dirNum++) {
+         struct afsd_file_list *thisFile;
+
+         for (thisFile = cache_dir_filelist[dirNum];
+              thisFile && cache_dir_list[dirNum] >= nFilesPerDir;
+              thisFile = thisFile->next) {
+           while (thisDir < maxDir && cache_dir_list[thisDir] >= nFilesPerDir)
+             thisDir++;
+           if (thisDir >= maxDir)
+             printf("%s: can't find directory to move V%d\n", rn, vFileNum);
+           else {
+             if (MoveCacheFile (directory, dirNum, thisDir,
+                                thisFile->fileNum, maxDir)) {
+               /* Cannot move.  Ignore this file??? */
+               /* XXX */
+             }
+           }
+         } /* for each file to move */
+       } /* for each directory */
+
+       /* Remove any directories >= maxDir -- they should be empty */
+       for (; highDir >= maxDir; highDir--) {
+         sprintf(fileToDelete, "D%d", highDir);
+         if (unlink(fullpn_FileToDelete)) {
+           if (errno == EISDIR && *fileToDelete == 'D') {
+             if (rmdir(fullpn_FileToDelete)) {
+               printf("%s: Can't rmdir '%s', errno is %d\n",
+                      rn, fullpn_FileToDelete, errno);
+             }
+           } else
+             printf("%s: Can't unlink '%s', errno is %d\n",
+                    rn, fullpn_FileToDelete, errno);
+         }
+       }
+    } /* dirNum < 0 */
     
     /*
      * Close the directory, return success.
@@ -690,24 +980,74 @@ int SweepAFSCache(vFilesFound)
     return(0);
 }
 
+int SweepAFSCache(vFilesFound)
+    int *vFilesFound;
+{
+    static char        rn[] = "SweepAFSCache"; /*Routine name*/
+    int maxDir = (cacheFiles + nFilesPerDir - 1 ) / nFilesPerDir;
+    int i;
+
+    *vFilesFound = 0;
+
+    if (cacheFlags & AFSCALL_INIT_MEMCACHE) {
+       if (afsd_debug)
+           printf("%s: Memory Cache, no cache sweep done\n", rn);
+       return 0;
+    }
+
+    if (cache_dir_list == NULL) {
+        cache_dir_list = (int *) malloc (maxDir * sizeof(*cache_dir_list));
+       if (cache_dir_list == NULL) {
+           printf("%s: Malloc Failed!\n", rn);
+           return (-1);
+       }
+       for (i=0; i < maxDir; i++)
+         cache_dir_list[i] = -1; /* Does not exist */
+    }
+
+    if (cache_dir_filelist == NULL) {
+        cache_dir_filelist = (struct afsd_file_list **)
+         malloc (maxDir * sizeof(*cache_dir_filelist));
+       if (cache_dir_filelist == NULL) {
+           printf("%s: Malloc Failed!\n", rn);
+           return (-1);
+       }
+       memset (cache_dir_filelist, 0, maxDir * sizeof(*cache_dir_filelist));
+    }
+
+    if (dir_for_V == NULL) {
+        dir_for_V = (int *) malloc (cacheFiles * sizeof(*dir_for_V));
+       if (dir_for_V == NULL) {
+           printf("%s: Malloc Failed!\n", rn);
+           return (-1);
+       }
+       for (i=0; i < cacheFiles; i++)
+         dir_for_V[i] = -1;    /* Does not exist */
+    }
+
+    /* Note, setting dirNum to -2 here will cause cachefiles found in
+     * the toplevel directory to be marked in directory "-2".  This
+     * allows us to differentiate between 'file not seen' (-1) and
+     * 'file seen in top-level' (-2).  Then when we try to move the
+     * file into a subdirectory, we know it's in the top-level instead
+     * of some other cache subdir.
+     */
+    return doSweepAFSCache (vFilesFound, cacheBaseDir, -2, maxDir);
+}
+
 static ConfigCell(aci, arock, adir)
 register struct afsconf_cell *aci;
 char *arock;
 struct afsconf_dir *adir; {
     register int isHomeCell;
-    register int i;
-    afs_int32 cellFlags;
+    register int i, code;
+    afs_int32 cellFlags = 0;
     afs_int32 hosts[MAXHOSTSPERCELL];
     
     /* figure out if this is the home cell */
     isHomeCell = (strcmp(aci->name, LclCellName) == 0);
-    if (isHomeCell) {
-       lookingForHomeCell = 0;
-       cellFlags = 1;      /* home cell, suid is ok */
-    }
-    else {
+    if (!isHomeCell)
        cellFlags = 2;      /* not home, suid is forbidden */
-    }
     
     /* build address list */
     for(i=0;i<MAXHOSTSPERCELL;i++)
@@ -717,11 +1057,23 @@ struct afsconf_dir *adir; {
                                            for upwards compatibility */
 
     /* configure one cell */
-    call_syscall(AFSOP_ADDCELL2,
+    code = call_syscall(AFSOP_ADDCELL2,
             hosts,                     /* server addresses */
             aci->name,                 /* cell name */
             cellFlags,                 /* is this the home cell? */
             aci->linkedCell);          /* Linked cell, if any */
+    if (code)
+       printf("Adding cell '%s': error %d\n", aci->name, code);
+    return 0;
+}
+
+static ConfigCellAlias(aca, arock, adir)
+    register struct afsconf_cellalias *aca;
+    char *arock;
+    struct afsconf_dir *adir;
+{
+    /* push the alias into the kernel */
+    call_syscall(AFSOP_ADDCELLALIAS, aca->aliasName, aca->realName);
     return 0;
 }
 
@@ -734,6 +1086,10 @@ static AfsdbLookupHandler()
     struct afsconf_cell acellInfo;
     int i;
 
+    kernelMsg[0] = 0;
+    kernelMsg[1] = 0;
+    acellName[0] = '\0';
+
     while (1) {
        /* On some platforms you only get 4 args to an AFS call */
        int sizeArg = ((sizeof acellName) << 16) | (sizeof kernelMsg);
@@ -743,6 +1099,9 @@ static AfsdbLookupHandler()
            continue;
        }
 
+       if (*acellName == 1)    /* Shutting down */
+           break;
+
        code = afsconf_GetAfsdbInfo(acellName, 0, &acellInfo);
        if (code) {
            kernelMsg[0] = 0;
@@ -755,8 +1114,12 @@ static AfsdbLookupHandler()
                kernelMsg[1] = 0;
            for (i=0; i<acellInfo.numServers; i++)
                kernelMsg[i+2] = acellInfo.hostAddr[i].sin_addr.s_addr;
+           strncpy(acellName, acellInfo.name, sizeof(acellName));
+           acellName[sizeof(acellName) - 1] = '\0';
        }    
     }
+
+    exit(1);
 }
 #endif
 
@@ -932,7 +1295,11 @@ mainproc(as, arock)
         * Cold shutdown is the default
         */
        printf("afsd: Shutting down all afs processes and afs state\n");
-       call_syscall(AFSOP_SHUTDOWN, 1);
+       code = call_syscall(AFSOP_SHUTDOWN, 1);
+       if (code) {
+           printf("afsd: AFS still mounted; Not shutting down\n");
+           exit(1);
+       }
        exit(0);
     }
     if (as->parms[21].items) {
@@ -955,6 +1322,27 @@ mainproc(as, arock)
        printf("afsd: No AFSDB support; ignoring -afsdb");
 #endif
     }
+    if (as->parms[25].items) {
+        /* -files_per_subdir */
+        int res = atoi(as->parms[25].items->data);
+       if ( res < 10 || res > 2^30) {
+           printf("afsd:invalid number of files per subdir, \"%s\". Ignored\n", as->parms[25].items->data);
+       } else {
+           nFilesPerDir = res;
+       }
+    }
+    if (as->parms[26].items) {
+       /* -dynroot */
+       enable_dynroot = 1;
+    }
+    if (as->parms[27].items) {
+       /* -fakestat */
+       enable_fakestat = 2;
+    }
+    if (as->parms[28].items) {
+       /* -fakestat-all */
+       enable_fakestat = 1;
+    }
 
     /*
      * Pull out all the configuration info for the workstation's AFS cache and
@@ -984,7 +1372,7 @@ mainproc(as, arock)
 
     if ((logfd = fopen(fullpn_AFSLogFile,"r+")) == 0) {
        if (afsd_verbose)  printf("%s: Creating '%s'\n",  rn, fullpn_AFSLogFile);
-       if (CreateCacheFile(fullpn_AFSLogFile)) {
+       if (CreateCacheFile(fullpn_AFSLogFile, NULL)) {
            printf("%s: Can't create '%s' (You may want to use the -logfile option)\n",  rn, fullpn_AFSLogFile);
            exit(1);
        }
@@ -1086,10 +1474,11 @@ mainproc(as, arock)
     /*
      * Set up all the pathnames we'll need for later.
      */
-    sprintf(fullpn_DCacheFile,  "%s/%s", cacheBaseDir, DCACHEFILE);
-    sprintf(fullpn_VolInfoFile, "%s/%s", cacheBaseDir, VOLINFOFILE);
-    sprintf(fullpn_VFile,       "%s/V",  cacheBaseDir);
-    vFileNumber = fullpn_VFile + strlen(fullpn_VFile);
+    sprintf(fullpn_DCacheFile,   "%s/%s", cacheBaseDir, DCACHEFILE);
+    sprintf(fullpn_VolInfoFile,  "%s/%s", cacheBaseDir, VOLINFOFILE);
+    sprintf(fullpn_CellInfoFile, "%s/%s", cacheBaseDir, CELLINFOFILE);
+    sprintf(fullpn_VFile,       "%s/",  cacheBaseDir);
+    vFilePtr = fullpn_VFile + strlen(fullpn_VFile);
 
 #if 0
     fputs(AFS_GOVERNMENT_MESSAGE, stdout); 
@@ -1186,6 +1575,99 @@ mainproc(as, arock)
     }
 #endif
 
+    code = call_syscall(AFSOP_BASIC_INIT, 1);
+    if (code)
+       printf("%s: Error %d in basic initialization.\n", rn, code);
+
+    /*
+     * Tell the kernel some basic information about the workstation's cache.
+     */
+    if (afsd_verbose)
+       printf("%s: Calling AFSOP_CACHEINIT: %d stat cache entries, %d optimum cache files, %d blocks in the cache, flags = 0x%x, dcache entries %d\n",
+              rn, cacheStatEntries, cacheFiles, cacheBlocks, cacheFlags,
+              dCacheSize);
+    memset(&cparams, '\0', sizeof(cparams));
+    cparams.cacheScaches = cacheStatEntries;
+    cparams.cacheFiles = cacheFiles;
+    cparams.cacheBlocks = cacheBlocks;
+    cparams.cacheDcaches = dCacheSize;
+    cparams.cacheVolumes = vCacheSize;
+    cparams.chunkSize = chunkSize;
+    cparams.setTimeFlag = cacheSetTime;
+    cparams.memCacheFlag = cacheFlags;
+#ifdef notdef
+    cparams.inodes       = inodes;
+#endif
+    call_syscall(AFSOP_CACHEINIT, &cparams);
+    if (afsd_CloseSynch) 
+       call_syscall(AFSOP_CLOSEWAIT);
+
+    /*
+     * Sweep the workstation AFS cache directory, remembering the inodes of
+     * valid files and deleting extraneous files.  Keep sweeping until we
+     * have the right number of data cache files or we've swept too many
+     * times.
+     *
+     * This also creates files in the cache directory like VolumeItems and
+     * CellItems, and thus must be ran before those are sent to the kernel.
+     */
+    if (afsd_verbose)
+       printf("%s: Sweeping workstation's AFS cache directory.\n",
+              rn);
+    cacheIteration = 0;
+    /* Memory-cache based system doesn't need any of this */
+    if(!(cacheFlags & AFSCALL_INIT_MEMCACHE)) {
+       do {
+           cacheIteration++;
+           if (SweepAFSCache(&vFilesFound)) {
+               printf("%s: Error on sweep %d of workstation AFS cache \
+                       directory.\n", rn, cacheIteration);
+               exit(1);
+           }
+           if (afsd_verbose)
+               printf("%s: %d out of %d data cache files found in sweep %d.\n",
+                      rn, vFilesFound, cacheFiles, cacheIteration);
+       } while ((vFilesFound < cacheFiles) &&
+                (cacheIteration < MAX_CACHE_LOOPS));
+    } else if(afsd_verbose)
+       printf("%s: Using memory cache, not swept\n", rn);
+
+    /*
+     * Pass the kernel the name of the workstation cache file holding the 
+     * dcache entries.
+     */
+    if (afsd_debug)
+       printf("%s: Calling AFSOP_CACHEINFO: dcache file is '%s'\n",
+              rn, fullpn_DCacheFile);
+    /* once again, meaningless for a memory-based cache. */
+    if(!(cacheFlags & AFSCALL_INIT_MEMCACHE))
+       call_syscall(AFSOP_CACHEINFO, fullpn_DCacheFile);
+
+    /*
+     * Pass the kernel the name of the workstation cache file holding the
+     * cell information.
+     */
+    if (afsd_debug)
+       printf("%s: Calling AFSOP_CELLINFO: cell info file is '%s'\n",
+              rn, fullpn_CellInfoFile);
+    call_syscall(AFSOP_CELLINFO, fullpn_CellInfoFile);
+
+    if (enable_dynroot) {
+       if (afsd_verbose)
+           printf("%s: Enabling dynroot support in kernel.\n", rn);
+       code = call_syscall(AFSOP_SET_DYNROOT, 1);
+       if (code)
+           printf("%s: Error enabling dynroot support.\n", rn);
+    }
+
+    if (enable_fakestat) {
+       if (afsd_verbose)
+           printf("%s: Enabling fakestat support in kernel.\n", rn);
+       code = call_syscall(AFSOP_SET_FAKESTAT, enable_fakestat);
+       if (code)
+           printf("%s: Error enabling fakestat support.\n", rn);
+    }
+
     /* Initialize AFS daemon threads. */
     if (afsd_verbose)
        printf("%s: Forking AFS daemon.\n", rn);
@@ -1244,18 +1726,13 @@ mainproc(as, arock)
     /*
      * Tell the kernel about each cell in the configuration.
      */
-    lookingForHomeCell = 1;
-
-    afsconf_CellApply(cdir, ConfigCell, (char *) 0);
+    afsconf_CellApply(cdir, ConfigCell, NULL);
+    afsconf_CellAliasApply(cdir, ConfigCellAlias, NULL);
 
     /*
-     * If we're still looking for the home cell after the whole cell configuration database
-     * has been parsed, there's something wrong.
+     * Set the primary cell name.
      */
-    if (lookingForHomeCell) {
-       printf("%s: Can't find information for home cell '%s' in cell database!\n",
-              rn, LclCellName);
-    }
+    call_syscall(AFSOP_SET_THISCELL, LclCellName);
 
     /*
      * If the root volume has been explicitly set, tell the kernel.
@@ -1268,67 +1745,6 @@ mainproc(as, arock)
     }
 
     /*
-     * Tell the kernel some basic information about the workstation's cache.
-     */
-    if (afsd_verbose)
-       printf("%s: Calling AFSOP_CACHEINIT: %d stat cache entries, %d optimum cache files, %d blocks in the cache, flags = 0x%x, dcache entries %d\n",
-              rn, cacheStatEntries, cacheFiles, cacheBlocks, cacheFlags,
-              dCacheSize);
-    memset(&cparams, '\0', sizeof(cparams));
-    cparams.cacheScaches = cacheStatEntries;
-    cparams.cacheFiles = cacheFiles;
-    cparams.cacheBlocks = cacheBlocks;
-    cparams.cacheDcaches = dCacheSize;
-    cparams.cacheVolumes = vCacheSize;
-    cparams.chunkSize = chunkSize;
-    cparams.setTimeFlag = cacheSetTime;
-    cparams.memCacheFlag = cacheFlags;
-#ifdef notdef
-    cparams.inodes       = inodes;
-#endif
-    call_syscall(AFSOP_CACHEINIT, &cparams);
-    if (afsd_CloseSynch) 
-      call_syscall(AFSOP_CLOSEWAIT);
-
-    /*
-     * Sweep the workstation AFS cache directory, remembering the inodes of
-     * valid files and deleting extraneous files.  Keep sweeping until we
-     * have the right number of data cache files or we've swept too many
-     * times.
-     */
-    if (afsd_verbose)
-       printf("%s: Sweeping workstation's AFS cache directory.\n",
-              rn);
-    cacheIteration = 0;
-    /* Memory-cache based system doesn't need any of this */
-    if(!(cacheFlags & AFSCALL_INIT_MEMCACHE)) {
-       do {
-           cacheIteration++;
-           if (SweepAFSCache(&vFilesFound)) {
-               printf("%s: Error on sweep %d of workstation AFS cache \
-                       directory.\n", rn, cacheIteration);
-               exit(1);
-           }
-           if (afsd_verbose)
-               printf("%s: %d out of %d data cache files found in sweep %d.\n",
-                      rn, vFilesFound, cacheFiles, cacheIteration);
-       } while ((vFilesFound < cacheFiles) &&
-                (cacheIteration < MAX_CACHE_LOOPS));
-    } else if(afsd_verbose)
-       printf("%s: Using memory cache, not swept\n", rn);
-
-    /*
-     * Pass the kernel the name of the workstation cache file holding the 
-     * dcache entries.
-     */
-    if (afsd_debug)
-       printf("%s: Calling AFSOP_CACHEINFO: dcache file is '%s'\n",
-              rn, fullpn_DCacheFile);
-    /* once again, meaningless for a memory-based cache. */
-    if(!(cacheFlags & AFSCALL_INIT_MEMCACHE))
-       call_syscall(AFSOP_CACHEINFO, fullpn_DCacheFile);
-
-    /*
      * Pass the kernel the name of the workstation cache file holding the
      * volume information.
      */
@@ -1424,7 +1840,7 @@ mainproc(as, arock)
     if (aix_vmount()) {
 #else
 #if defined(AFS_HPUX100_ENV)
-    if ((mount("",cacheMountDir,mountFlags,"afs", (char *)0, 0)) < 0) {
+    if ((mount("",cacheMountDir,mountFlags,"afs", NULL, 0)) < 0) {
 #else
 #ifdef AFS_HPUX_ENV
 #if    defined(AFS_HPUX90_ENV)
@@ -1450,7 +1866,7 @@ mainproc(as, arock)
 #endif
 #else
 #ifdef AFS_SUN5_ENV
-    if ((mount("AFS",cacheMountDir,mountFlags,"afs", (char *)0, 0)) < 0) {
+    if ((mount("AFS",cacheMountDir,mountFlags,"afs", NULL, 0)) < 0) {
 #else
 #if defined(AFS_SGI_ENV)
     mountFlags = MS_FSS;
@@ -1502,7 +1918,7 @@ int argc;
 char **argv; {
     register struct cmd_syndesc *ts;
 
-    ts = cmd_CreateSyntax((char *) 0, mainproc, (char *) 0, "start AFS");
+    ts = cmd_CreateSyntax(NULL, mainproc, NULL, "start AFS");
     cmd_AddParm(ts, "-blocks", CMD_SINGLE, CMD_OPTIONAL, "1024 byte blocks in cache");
     cmd_AddParm(ts, "-files", CMD_SINGLE, CMD_OPTIONAL, "files in cache");
     cmd_AddParm(ts, "-rootvol", CMD_SINGLE, CMD_OPTIONAL, "name of AFS root volume");
@@ -1536,6 +1952,10 @@ char **argv; {
                | CMD_HIDE
 #endif
                ), "Enable AFSDB support");
+    cmd_AddParm(ts, "-files_per_subdir", CMD_SINGLE, CMD_OPTIONAL, "log(2) of the number of cache files per cache subdirectory");
+    cmd_AddParm(ts, "-dynroot", CMD_FLAG, CMD_OPTIONAL, "Enable dynroot support");
+    cmd_AddParm(ts, "-fakestat", CMD_FLAG, CMD_OPTIONAL, "Enable fakestat support for cross-cell mounts");
+    cmd_AddParm(ts, "-fakestat-all", CMD_FLAG, CMD_OPTIONAL, "Enable fakestat support for all mounts");
     return (cmd_Dispatch(argc, argv));
 }
 
@@ -1617,9 +2037,19 @@ call_syscall(param1, param2, param3, param4, param5, param6, param7)
 long param1, param2, param3, param4, param5, param6, param7;
 {
     int error;
+#ifdef AFS_LINUX20_ENV
+    long eparm[4];
+
+    eparm[0] = param4;
+    eparm[1] = param5;
+    eparm[2] = param6;
+    eparm[3] = param7;
+
+    param4 = eparm;
+#endif
 
     error = syscall(AFS_SYSCALL, AFSCALL_CALL, param1, param2, param3, param4, param5, param6, param7);
-    if (afsd_verbose) printf("SScall(%d, %d)=%d ", AFS_SYSCALL, AFSCALL_CALL, error);
+    if (afsd_verbose) printf("SScall(%d, %d, %d)=%d ", AFS_SYSCALL, AFSCALL_CALL, param1, error);
     return (error);
 }
 #else  /* AFS_AIX32_ENV */