salvager: convert salvager and salvagerserver to libutil logging
[openafs.git] / src / vol / vol-salvage.c
index 55bd240..190bf96 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  * All Rights Reserved.
- * 
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
@@ -71,7 +71,7 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
        Logs missing inode numbers.
 
     2.3 features:
-           Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk.  If the server crashes, it may have an older version.  Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on. 
+           Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk.  If the server crashes, it may have an older version.  Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
 
     2.4 features:
            Locks the file /vice/vol/salvage.lock before starting.  Aborts if it can't acquire the lock.
@@ -86,31 +86,25 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <afsconfig.h>
 #include <afs/param.h>
 
+#include <afs/procmgmt.h>
+#include <roken.h>
 
-#ifndef AFS_NT40_ENV
-#include <sys/param.h>
-#include <sys/file.h>
-#ifndef ITIMER_REAL
-#include <sys/time.h>
-#endif /* ITIMER_REAL */
+#ifdef HAVE_SYS_FILE_H
+# include <sys/file.h>
 #endif
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <time.h>
-#include <errno.h>
+
 #ifdef AFS_NT40_ENV
-#include <io.h>
 #include <WINNT/afsevent.h>
 #endif
 #ifndef WCOREDUMP
 #define WCOREDUMP(x)   ((x) & 0200)
 #endif
-#include <rx/xdr.h>
+#include <afs/opr.h>
+#ifdef AFS_PTHREAD_ENV
+# include <opr/lock.h>
+#endif
+
 #include <afs/afsint.h>
-#include <afs/assert.h>
 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
 #if defined(AFS_VFSINCL_ENV)
 #include <sys/vnode.h>
@@ -128,7 +122,7 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #ifdef AFS_OSF_ENV
 #include <ufs/inode.h>
 #else /* AFS_OSF_ENV */
-#if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
+#if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_DARWIN_ENV)
 #include <sys/inode.h>
 #endif
 #endif
@@ -139,17 +133,13 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <sys/lockf.h>
 #else
 #ifdef AFS_HPUX_ENV
-#include <unistd.h>
 #include <checklist.h>
 #else
 #if defined(AFS_SGI_ENV)
-#include <unistd.h>
-#include <fcntl.h>
 #include <mntent.h>
 #else
 #if    defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
 #ifdef   AFS_SUN5_ENV
-#include <unistd.h>
 #include <sys/mnttab.h>
 #include <sys/mntent.h>
 #else
@@ -160,7 +150,6 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #endif /* AFS_HPUX_ENV */
 #endif
 #endif
-#include <fcntl.h>
 #ifndef AFS_NT40_ENV
 #include <afs/osi_inode.h>
 #endif
@@ -168,10 +157,7 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <afs/dir.h>
 #include <afs/afsutil.h>
 #include <afs/fileutil.h>
-#include <afs/procmgmt.h>      /* signal(), kill(), wait(), etc. */
-#ifndef AFS_NT40_ENV
-#include <syslog.h>
-#endif
+#include <rx/rx_queue.h>
 
 #include "nfs.h"
 #include "lwp.h"
@@ -182,7 +168,9 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include "volume.h"
 #include "partition.h"
 #include "daemon_com.h"
+#include "daemon_com_inline.h"
 #include "fssync.h"
+#include "fssync_inline.h"
 #include "volume_inline.h"
 #include "salvsync.h"
 #include "viceinode.h"
@@ -202,32 +190,12 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <pthread.h>
 #endif
 
-/*@+fcnmacros +macrofcndecl@*/
-#ifdef O_LARGEFILE
-#ifdef S_SPLINT_S
-extern off64_t afs_lseek(int FD, off64_t O, int F);
-#endif /*S_SPLINT_S */
-#define afs_lseek(FD, O, F)    lseek64(FD, (off64_t) (O), F)
-#define afs_stat       stat64
-#define afs_fstat      fstat64
-#define afs_open       open64
-#define afs_fopen      fopen64
-#else /* !O_LARGEFILE */
-#ifdef S_SPLINT_S
-extern off_t afs_lseek(int FD, off_t O, int F);
-#endif /*S_SPLINT_S */
-#define afs_lseek(FD, O, F)    lseek(FD, (off_t) (O), F)
-#define afs_stat       stat
-#define afs_fstat      fstat
-#define afs_open       open
-#define afs_fopen      fopen
-#endif /* !O_LARGEFILE */
-/*@=fcnmacros =macrofcndecl@*/
+#define SALV_BUFFER_SIZE 1024
 
 #ifdef AFS_OSF_ENV
 extern void *calloc();
 #endif
-static char *TimeStamp(time_t clock, int precision);
+static char *TimeStamp(char *buffer, size_t size, time_t clock, int precision);
 
 
 int debug;                     /* -d flag */
@@ -239,16 +207,12 @@ int Parallel = 4;         /* -para X flag */
 int PartsPerDisk = 8;          /* Salvage up to 8 partitions on same disk sequentially */
 int forceR = 0;                        /* -b flag */
 int ShowLog = 0;               /* -showlog flag */
+char *ShowLogFilename = NULL;    /* log file name for -showlog */
 int ShowSuid = 0;              /* -showsuid flag */
 int ShowMounts = 0;            /* -showmounts flag */
 int orphans = ORPH_IGNORE;     /* -orphans option */
 int Showmode = 0;
-
-
-#ifndef AFS_NT40_ENV
-int useSyslog = 0;             /* -syslog flag */
-int useSyslogFacility = LOG_DAEMON;    /* -syslogfacility option */
-#endif
+int ClientMode = 0;            /* running as salvager server client */
 
 #ifdef AFS_NT40_ENV
 int canfork = 0;
@@ -262,55 +226,67 @@ int OKToZap;                      /* -o flag */
 int ForceSalvage;              /* If salvage should occur despite the DONT_SALVAGE flag
                                 * in the volume header */
 
-FILE *logFile = 0;     /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
-
 #define ROOTINODE      2       /* Root inode of a 4.2 Unix file system
                                 * partition */
-Device fileSysDevice;          /* The device number of the current
-                                * partition being salvaged */
-#ifdef AFS_NT40_ENV
-char fileSysPath[8];
-#else
-char *fileSysPath;             /* The path of the mounted partition currently
-                                * being salvaged, i.e. the directory
-                                * containing the volume headers */
-#endif
-char *fileSysPathName;         /* NT needs this to make name pretty in log. */
-IHandle_t *VGLinkH;            /* Link handle for current volume group. */
-int VGLinkH_cnt;               /* # of references to lnk handle. */
-struct DiskPartition64 *fileSysPartition;      /* Partition  being salvaged */
+/**
+ * information that is 'global' to a particular salvage job.
+ */
+struct SalvInfo {
+    Device fileSysDevice;    /**< The device number of the current partition
+                             *   being salvaged */
+    char fileSysPath[9];     /**< The path of the mounted partition currently
+                              *   being salvaged, i.e. the directory containing
+                              *   the volume headers */
+    char *fileSysPathName;   /**< NT needs this to make name pretty log. */
+    IHandle_t *VGLinkH;      /**< Link handle for current volume group. */
+    int VGLinkH_cnt;         /**< # of references to lnk handle. */
+    struct DiskPartition64 *fileSysPartition; /**< Partition being salvaged */
+
 #ifndef AFS_NT40_ENV
-char *fileSysDeviceName;       /* The block device where the file system
-                                * being salvaged was mounted */
-char *filesysfulldev;
+    char *fileSysDeviceName; /**< The block device where the file system being
+                              *   salvaged was mounted */
+    char *filesysfulldev;
 #endif
-int VolumeChanged;             /* Set by any routine which would change the volume in
-                                * a way which would require callback is to be broken if the
-                                * volume was put back on line by an active file server */
-
-VolumeDiskData VolInfo;                /* A copy of the last good or salvaged volume header dealt with */
-
-int nVolumesInInodeFile;       /* Number of read-write volumes summarized */
-int inodeFd;                   /* File descriptor for inode file */
-
-
-struct VnodeInfo vnodeInfo[nVNODECLASSES];
-
-
-struct VolumeSummary *volumeSummaryp = NULL;   /* Holds all the volumes in a part */
-int nVolumes;                  /* Number of volumes (read-write and read-only)
-                                * in volume summary */
+    int VolumeChanged;       /**< Set by any routine which would change the
+                              *   volume in a way which would require callbacks
+                              *   to be broken if the volume was put back on
+                              *   on line by an active file server */
+
+    VolumeDiskData VolInfo;  /**< A copy of the last good or salvaged volume
+                              *   header dealt with */
+
+    int nVolumesInInodeFile; /**< Number of read-write volumes summarized */
+    FD_t inodeFd;             /**< File descriptor for inode file */
+
+    struct VolumeSummary *volumeSummaryp; /**< Holds all the volumes in a part */
+    int nVolumes;            /**< Number of volumes (read-write and read-only)
+                              *   in volume summary */
+    struct InodeSummary *inodeSummary; /**< contains info on all the relevant
+                                        *   inodes */
+
+    struct VnodeInfo vnodeInfo[nVNODECLASSES]; /**< contains info on all of the
+                                                *   vnodes in the volume that
+                                                *   we are currently looking
+                                                *   at */
+    int useFSYNC; /**< 0 if the fileserver is unavailable; 1 if we should try
+                   *   to contact the fileserver over FSYNC */
+};
 
 char *tmpdir = NULL;
 
 
 
 /* Forward declarations */
-static int IsVnodeOrphaned(VnodeId vnode);
-static int AskVolumeSummary(VolumeId singleVolumeNumber);
+static void QuietExit(int) AFS_NORETURN;
+static void SalvageShowLog(void);
+static int IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode);
+static int AskVolumeSummary(struct SalvInfo *salvinfo,
+                            VolumeId singleVolumeNumber);
+static void MaybeAskOnline(struct SalvInfo *salvinfo, VolumeId volumeId);
+static void AskError(struct SalvInfo *salvinfo, VolumeId volumeId);
 
 #ifdef AFS_DEMAND_ATTACH_FS
-static int LockVolume(VolumeId volumeId);
+static int LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId);
 #endif /* AFS_DEMAND_ATTACH_FS */
 
 /* Uniquifier stored in the Inode */
@@ -329,7 +305,7 @@ IUnique(Unique u)
 }
 
 static int
-BadError(register int aerror)
+BadError(int aerror)
 {
     if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
        return 1;
@@ -394,7 +370,7 @@ IsPartitionMounted(char *part)
     FILE *mntfp;
     struct mntent *mntent;
 
-    assert(mntfp = setmntent(MOUNTED, "r"));
+    opr_Verify(mntfp = setmntent(MOUNTED, "r"));
     while (mntent = getmntent(mntfp)) {
        if (!strcmp(part, mntent->mnt_dir))
            break;
@@ -407,11 +383,11 @@ IsPartitionMounted(char *part)
 /* Check if the given inode is the root of the filesystem. */
 #ifndef AFS_SGI_XFS_IOPS_ENV
 int
-IsRootInode(struct afs_stat *status)
+IsRootInode(struct afs_stat_st *status)
 {
     /*
      * The root inode is not a fixed value in XFS partitions. So we need to
-     * see if the partition is in the list of mounted partitions. This only 
+     * see if the partition is in the list of mounted partitions. This only
      * affects the SalvageFileSys path, so we check there.
      */
     return (status->st_ino == ROOTINODE);
@@ -457,35 +433,31 @@ int
 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
 {
 #define RES_LEN 256
-    char res[RES_LEN];
-    int d1, d2;
+    char res1[RES_LEN];
+    char res2[RES_LEN];
+
     static int dowarn = 1;
 
-    if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
+    if (!QueryDosDevice(p1->devName, res1, RES_LEN - 1))
        return 1;
-    if (strncmp(res, HDSTR, HDLEN)) {
+    if (strncmp(res1, HDSTR, HDLEN)) {
        if (dowarn) {
            dowarn = 0;
            Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
-               res, HDSTR, p1->devName);
+               res1, HDSTR, p1->devName);
        }
-       return 1;
     }
-    d1 = atoi(&res[HDLEN]);
-
-    if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
+    if (!QueryDosDevice(p2->devName, res2, RES_LEN - 1))
        return 1;
-    if (strncmp(res, HDSTR, HDLEN)) {
+    if (strncmp(res2, HDSTR, HDLEN)) {
        if (dowarn) {
            dowarn = 0;
            Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
-               res, HDSTR, p2->devName);
+               res2, HDSTR, p2->devName);
        }
-       return 1;
     }
-    d2 = atoi(&res[HDLEN]);
 
-    return d1 == d2;
+    return (0 == _strnicmp(res1, res2, RES_LEN - 1));
 }
 #else
 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
@@ -507,22 +479,19 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
     struct job *thisjob = 0;
     static int numjobs = 0;
     static int jobcount = 0;
-    char buf[1024];
     int wstatus;
     struct job *oldjob;
     int startjob;
     FILE *passLog;
-    char logFileName[256];
     int i, j, pid;
 
     if (partP) {
        /* We have a partition to salvage. Copy it into thisjob */
-       thisjob = (struct job *)malloc(sizeof(struct job));
+       thisjob = calloc(1, sizeof(struct job));
        if (!thisjob) {
            Log("Can't salvage '%s'. Not enough memory\n", partP->name);
            return;
        }
-       memset(thisjob, 0, sizeof(struct job));
        thisjob->partP = partP;
        thisjob->jobnumb = jobcount;
        jobcount++;
@@ -544,12 +513,12 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
     }
 
     if (thisjob) {
-       /* Check to see if thisjob is for a disk that we are already 
+       /* Check to see if thisjob is for a disk that we are already
         * salvaging. If it is, link it in as the next job to do. The
-        * jobs array has 1 entry per disk being salvages. numjobs is 
-        * the total number of disks currently being salvaged. In 
+        * jobs array has 1 entry per disk being salvages. numjobs is
+        * the total number of disks currently being salvaged. In
         * order to keep thejobs array compact, when a disk is
-        * completed, the hightest element in the jobs array is moved 
+        * completed, the hightest element in the jobs array is moved
         * down to now open slot.
         */
        for (j = 0; j < numjobs; j++) {
@@ -573,12 +542,12 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
             * job to finish. When it's done, clean up after it.
             */
            pid = wait(&wstatus);
-           assert(pid != -1);
+           opr_Assert(pid != -1);
            for (j = 0; j < numjobs; j++) {     /* Find which job it is */
                if (pid == jobs[j]->pid)
                    break;
            }
-           assert(j < numjobs);
+           opr_Assert(j < numjobs);
            if (WCOREDUMP(wstatus)) {   /* Say if the job core dumped */
                Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
            }
@@ -588,7 +557,7 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
            jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
            free(oldjob);       /* free the old job */
 
-           /* If there is another partition on the disk to salvage, then 
+           /* If there is another partition on the disk to salvage, then
             * say we will start it (startjob). If not, then put thisjob there
             * and say we will start it.
             */
@@ -634,27 +603,21 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
                numjobs++;
            } else {
                int fd;
+               char *logFileName;
 
-               ShowLog = 0;
                for (fd = 0; fd < 16; fd++)
                    close(fd);
-               open("/", 0);
+               open(OS_DIRSEP, 0);
                dup2(0, 1);
                dup2(0, 2);
-#ifndef AFS_NT40_ENV
-               if (useSyslog) {
-                   openlog("salvager", LOG_PID, useSyslogFacility);
-               } else
-#endif
-               {
-                   (void)afs_snprintf(logFileName, sizeof logFileName,
-                                      "%s.%d",
-                                      AFSDIR_SERVER_SLVGLOG_FILEPATH,
-                                      jobs[startjob]->jobnumb);
-                   logFile = afs_fopen(logFileName, "w");
+
+               ShowLog = 0; /* Child processes do not display. */
+               if (asprintf(&logFileName, "%s.%d",
+                            AFSDIR_SERVER_SLVGLOG_FILEPATH,
+                            jobs[startjob]->jobnumb) >= 0) {
+                   OpenLog(logFileName);
+                   free(logFileName);
                }
-               if (!logFile)
-                   logFile = stdout;
 
                SalvageFileSys1(jobs[startjob]->partP, 0);
                Exit(0);
@@ -665,22 +628,34 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
 
     /* If waited for all jobs to complete, now collect log files and return */
 #ifndef AFS_NT40_ENV
-    if (!useSyslog)            /* if syslogging - no need to collect */
+    if (!serverLogSyslog)              /* if syslogging - no need to collect */
 #endif
        if (!partP) {
-           for (i = 0; i < jobcount; i++) {
-               (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
-                                  AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
-               if ((passLog = afs_fopen(logFileName, "r"))) {
-                   while (fgets(buf, sizeof(buf), passLog)) {
-                       fputs(buf, logFile);
+           char *buf = calloc(1, SALV_BUFFER_SIZE);
+           char *logFileName;
+
+           if (buf == NULL) {
+               Log("out of memory");
+           } else {
+               for (i = 0; i < jobcount; i++) {
+                   if (asprintf(&logFileName, "%s.%d",
+                                AFSDIR_SERVER_SLVGLOG_FILEPATH, i) < 0) {
+                       Log("out of memory");
+                       break;
+                   }
+                   if ((passLog = afs_fopen(logFileName, "r"))) {
+                       while (fgets(buf, SALV_BUFFER_SIZE, passLog)) {
+                           WriteLogBuffer(buf, strlen(buf));
+                       }
+                       fclose(passLog);
                    }
-                   fclose(passLog);
+                   (void)unlink(logFileName);
+                   free(logFileName);
                }
-               (void)unlink(logFileName);
+               free(buf);
            }
-           fflush(logFile);
        }
+
     return;
 }
 
@@ -691,8 +666,7 @@ SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
     if (!canfork || debug || Fork() == 0) {
        SalvageFileSys1(partP, singleVolumeNumber);
        if (canfork && !debug) {
-           ShowLog = 0;
-           Exit(0);
+           QuietExit(0);
        }
     } else
        Wait("SalvageFileSys");
@@ -703,13 +677,13 @@ get_DevName(char *pbuffer, char *wpath)
 {
     char pbuf[128], *ptr;
     strcpy(pbuf, pbuffer);
-    ptr = (char *)strrchr(pbuf, '/');
+    ptr = (char *)strrchr(pbuf, OS_DIRSEPC);
     if (ptr) {
        *ptr = '\0';
        strcpy(wpath, pbuf);
     } else
        return NULL;
-    ptr = (char *)strrchr(pbuffer, '/');
+    ptr = (char *)strrchr(pbuffer, OS_DIRSEPC);
     if (ptr) {
        strcpy(pbuffer, ptr + 1);
        return pbuffer;
@@ -722,19 +696,23 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 {
     char *name, *tdir;
     char inodeListPath[256];
-    FILE *inodeFile = NULL;
+    FD_t inodeFile = INVALID_FD;
     static char tmpDevName[100];
     static char wpath[100];
     struct VolumeSummary *vsp, *esp;
     int i, j;
     int code;
     int tries = 0;
+    struct SalvInfo l_salvinfo;
+    struct SalvInfo *salvinfo = &l_salvinfo;
 
  retry:
+    memset(salvinfo, 0, sizeof(*salvinfo));
+
     tries++;
-    if (inodeFile) {
-       fclose(inodeFile);
-       inodeFile = NULL;
+    if (inodeFile != INVALID_FD) {
+       OS_CLOSE(inodeFile);
+       inodeFile = INVALID_FD;
     }
     if (tries > VOL_MAX_CHECKOUT_RETRIES) {
        Abort("Raced too many times with fileserver restarts while trying to "
@@ -748,20 +726,20 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-    fileSysPartition = partP;
-    fileSysDevice = fileSysPartition->device;
-    fileSysPathName = VPartitionPath(fileSysPartition);
+    salvinfo->fileSysPartition = partP;
+    salvinfo->fileSysDevice = salvinfo->fileSysPartition->device;
+    salvinfo->fileSysPathName = VPartitionPath(salvinfo->fileSysPartition);
 
 #ifdef AFS_NT40_ENV
     /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
-    (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
+    (void)sprintf(salvinfo->fileSysPath, "%s" OS_DIRSEP, salvinfo->fileSysPathName);
     name = partP->devName;
 #else
-    fileSysPath = fileSysPathName;
+    strlcpy(salvinfo->fileSysPath, salvinfo->fileSysPathName, sizeof(salvinfo->fileSysPath));
     strcpy(tmpDevName, partP->devName);
     name = get_DevName(tmpDevName, wpath);
-    fileSysDeviceName = name;
-    filesysfulldev = wpath;
+    salvinfo->fileSysDeviceName = name;
+    salvinfo->filesysfulldev = wpath;
 #endif
 
     if (singleVolumeNumber) {
@@ -778,19 +756,21 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
            Abort("Couldn't connect to file server\n");
        }
 
-       AskOffline(singleVolumeNumber, partP->name);
+       salvinfo->useFSYNC = 1;
+       AskOffline(salvinfo, singleVolumeNumber);
 #ifdef AFS_DEMAND_ATTACH_FS
-       if (LockVolume(singleVolumeNumber)) {
+       if (LockVolume(salvinfo, singleVolumeNumber)) {
            goto retry;
        }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
     } else {
+       salvinfo->useFSYNC = 0;
        VLockPartition(partP->name);
        if (ForceSalvage) {
            ForceSalvage = 1;
        } else {
-           ForceSalvage = UseTheForceLuke(fileSysPath);
+           ForceSalvage = UseTheForceLuke(salvinfo->fileSysPath);
        }
        if (!Showmode)
            Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
@@ -801,38 +781,38 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 
 
     /*
-     * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.* 
-     * files 
+     * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
+     * files
      */
     {
        DIR *dirp;
        struct dirent *dp;
 
-       assert((dirp = opendir(fileSysPath)) != NULL);
+       opr_Verify((dirp = opendir(salvinfo->fileSysPath)) != NULL);
        while ((dp = readdir(dirp))) {
            if (!strncmp(dp->d_name, "salvage.inodes.", 15)
                || !strncmp(dp->d_name, "salvage.temp.", 13)) {
                char npath[1024];
                Log("Removing old salvager temp files %s\n", dp->d_name);
-               strcpy(npath, fileSysPath);
-               strcat(npath, "/");
+               strcpy(npath, salvinfo->fileSysPath);
+               strcat(npath, OS_DIRSEP);
                strcat(npath, dp->d_name);
-               unlink(npath);
+               OS_UNLINK(npath);
            }
        }
        closedir(dirp);
     }
-    tdir = (tmpdir ? tmpdir : fileSysPath);
+    tdir = (tmpdir ? tmpdir : salvinfo->fileSysPath);
 #ifdef AFS_NT40_ENV
     (void)_putenv("TMP=");     /* If "TMP" is set, then that overrides tdir. */
     (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
 #else
-    snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
+    snprintf(inodeListPath, 255, "%s" OS_DIRSEP "salvage.inodes.%s.%d", tdir, name,
             getpid());
 #endif
 
-    inodeFile = fopen(inodeListPath, "w+b");
-    if (!inodeFile) {
+    inodeFile = OS_OPEN(inodeListPath, O_RDWR|O_TRUNC|O_CREAT, 0666);
+    if (inodeFile == INVALID_FD) {
        Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
     }
 #ifdef AFS_NT40_ENV
@@ -840,6 +820,12 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
      * semantics of unlink. In most places in the salvager, we really do
      * mean to unlink the file at that point. Those places have been
      * modified to actually do that so that the NT crt can be used there.
+     *
+     * jaltman - On NT delete on close cannot be applied to a file while the
+     * process has an open file handle that does not have DELETE file
+     * access and FILE_SHARE_DELETE.  fopen() calls CreateFile() without
+     * delete privileges.  As a result the nt_unlink() call will always
+     * fail.
      */
     code = nt_unlink(inodeListPath);
 #else
@@ -849,16 +835,24 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
        Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
     }
 
-    if (GetInodeSummary(inodeFile, singleVolumeNumber) < 0) {
-       fclose(inodeFile);
+    if (GetInodeSummary(salvinfo, inodeFile, singleVolumeNumber) < 0) {
+       OS_CLOSE(inodeFile);
        return;
     }
-    inodeFd = fileno(inodeFile);
-    if (inodeFd == -1)
+    salvinfo->inodeFd = inodeFile;
+    if (salvinfo->inodeFd == INVALID_FD)
        Abort("Temporary file %s is missing...\n", inodeListPath);
-    afs_lseek(inodeFd, 0L, SEEK_SET);
+    OS_SEEK(salvinfo->inodeFd, 0L, SEEK_SET);
     if (ListInodeOption) {
-       PrintInodeList();
+       PrintInodeList(salvinfo);
+       if (singleVolumeNumber) {
+           /* We've checked out the volume from the fileserver, and we need
+            * to give it back. We don't know if the volume exists or not,
+            * so we don't know whether to AskOnline or not. Try to determine
+            * if the volume exists by trying to read the volume header, and
+            * AskOnline if it is readable. */
+           MaybeAskOnline(salvinfo, singleVolumeNumber);
+       }
        return;
     }
     /* enumerate volumes in the partition.
@@ -867,33 +861,43 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
      * Fix up inodes on last volume in set (whether it is read-write
      * or read-only).
      */
-    if (GetVolumeSummary(singleVolumeNumber)) {
+    if (GetVolumeSummary(salvinfo, singleVolumeNumber)) {
        goto retry;
     }
 
-    for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
-        i < nVolumesInInodeFile; i = j) {
-       VolumeId rwvid = inodeSummary[i].RWvolumeId;
+    if (singleVolumeNumber) {
+       /* If we delete a volume during the salvage, we indicate as such by
+        * setting the volsummary->deleted field. We need to know if we
+        * deleted a volume or not in order to know which volumes to bring
+        * back online after the salvage. If we fork, we will lose this
+        * information, since volsummary->deleted will not get set in the
+        * parent. So, don't fork. */
+       canfork = 0;
+    }
+
+    for (i = j = 0, vsp = salvinfo->volumeSummaryp, esp = vsp + salvinfo->nVolumes;
+        i < salvinfo->nVolumesInInodeFile; i = j) {
+       VolumeId rwvid = salvinfo->inodeSummary[i].RWvolumeId;
        for (j = i;
-            j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
+            j < salvinfo->nVolumesInInodeFile && salvinfo->inodeSummary[j].RWvolumeId == rwvid;
             j++) {
-           VolumeId vid = inodeSummary[j].volumeId;
+           VolumeId vid = salvinfo->inodeSummary[j].volumeId;
            struct VolumeSummary *tsp;
            /* Scan volume list (from partition root directory) looking for the
             * current rw volume number in the volume list from the inode scan.
             * If there is one here that is not in the inode volume list,
             * delete it now. */
            for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
-               if (vsp->fileName)
-                   DeleteExtraVolumeHeaderFile(vsp);
+               if (vsp->unused)
+                   DeleteExtraVolumeHeaderFile(salvinfo, vsp);
            }
            /* Now match up the volume summary info from the root directory with the
             * entry in the volume list obtained from scanning inodes */
-           inodeSummary[j].volSummary = NULL;
+           salvinfo->inodeSummary[j].volSummary = NULL;
            for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
                if (tsp->header.id == vid) {
-                   inodeSummary[j].volSummary = tsp;
-                   tsp->fileName = 0;
+                   salvinfo->inodeSummary[j].volSummary = tsp;
+                   tsp->unused = 0;
                    break;
                }
            }
@@ -901,73 +905,113 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
        /* Salvage the group of volumes (several read-only + 1 read/write)
         * starting with the current read-only volume we're looking at.
         */
-       SalvageVolumeGroup(&inodeSummary[i], j - i);
+#ifdef AFS_NT40_ENV
+       nt_SalvageVolumeGroup(salvinfo, &salvinfo->inodeSummary[i], j - i);
+#else
+       DoSalvageVolumeGroup(salvinfo, &salvinfo->inodeSummary[i], j - i);
+#endif /* AFS_NT40_ENV */
+
     }
 
     /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
     for (; vsp < esp; vsp++) {
-       if (vsp->fileName)
-           DeleteExtraVolumeHeaderFile(vsp);
+       if (vsp->unused)
+           DeleteExtraVolumeHeaderFile(salvinfo, vsp);
     }
 
     if (!singleVolumeNumber)   /* Remove the FORCESALVAGE file */
-       RemoveTheForce(fileSysPath);
+       RemoveTheForce(salvinfo->fileSysPath);
 
     if (!Testing && singleVolumeNumber) {
+       int foundSVN = 0;
 #ifdef AFS_DEMAND_ATTACH_FS
        /* unlock vol headers so the fs can attach them when we AskOnline */
-       VLockFileReinit(&fileSysPartition->volLockFile);
+       VLockFileReinit(&salvinfo->fileSysPartition->volLockFile);
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-       AskOnline(singleVolumeNumber, fileSysPartition->name);
-
        /* Step through the volumeSummary list and set all volumes on-line.
-        * The volumes were taken off-line in GetVolumeSummary.
+        * Most volumes were taken off-line in GetVolumeSummary.
+        * If a volume was deleted, don't tell the fileserver anything, since
+        * we already told the fileserver the volume was deleted back when we
+        * we destroyed the volume header.
+        * Also, make sure we bring the singleVolumeNumber back online first.
         */
-       for (j = 0; j < nVolumes; j++) {
-           AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
+
+       for (j = 0; j < salvinfo->nVolumes; j++) {
+           if (salvinfo->volumeSummaryp[j].header.id == singleVolumeNumber) {
+               foundSVN = 1;
+               if (!salvinfo->volumeSummaryp[j].deleted) {
+                   AskOnline(salvinfo, singleVolumeNumber);
+               }
+           }
+       }
+
+       if (!foundSVN) {
+           /* If singleVolumeNumber is not in our volumeSummary, it means that
+            * at least one other volume in the VG is on the partition, but the
+            * RW volume is not. We've already AskOffline'd it by now, though,
+            * so make sure we don't still have the volume checked out. */
+           AskDelete(salvinfo, singleVolumeNumber);
+       }
+
+       for (j = 0; j < salvinfo->nVolumes; j++) {
+           if (salvinfo->volumeSummaryp[j].header.id != singleVolumeNumber) {
+               if (!salvinfo->volumeSummaryp[j].deleted) {
+                   AskOnline(salvinfo, salvinfo->volumeSummaryp[j].header.id);
+               }
+           }
        }
     } else {
        if (!Showmode)
            Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
-               fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
+               salvinfo->fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
     }
 
-    fclose(inodeFile);         /* SalvageVolumeGroup was the last which needed it. */
+    OS_CLOSE(inodeFile);               /* SalvageVolumeGroup was the last which needed it. */
 }
 
 void
-DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
+DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp)
 {
     char path[64];
-    sprintf(path, "%s/%s", fileSysPath, vsp->fileName);
+    char filename[VMAXPATHLEN];
+
+    if (vsp->deleted) {
+       return;
+    }
+
+    VolumeExternalName_r(vsp->header.id, filename, sizeof(filename));
+    sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, filename);
 
     if (!Showmode)
        Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
     if (!Testing) {
        afs_int32 code;
-       code = VDestroyVolumeDiskHeader(fileSysPartition, vsp->header.id, vsp->header.parent);
+       code = VDestroyVolumeDiskHeader(salvinfo->fileSysPartition, vsp->header.id, vsp->header.parent);
        if (code) {
-           Log("Error %ld destroying volume disk header for volume %lu\n",
+           Log("Error %ld destroying volume disk header for volume %" AFS_VOLID_FMT "\n",
                afs_printable_int32_ld(code),
-               afs_printable_uint32_lu(vsp->header.id));
+               afs_printable_VolumeId_lu(vsp->header.id));
        }
 
-       /* make sure we actually delete the fileName file; ENOENT
+       /* make sure we actually delete the header file; ENOENT
         * is fine, since VDestroyVolumeDiskHeader probably already
         * unlinked it */
        if (unlink(path) && errno != ENOENT) {
            Log("Unable to unlink %s (errno = %d)\n", path, errno);
        }
+       if (salvinfo->useFSYNC) {
+           AskDelete(salvinfo, vsp->header.id);
+       }
+       vsp->deleted = 1;
     }
-    vsp->fileName = 0;
 }
 
 int
 CompareInodes(const void *_p1, const void *_p2)
 {
-    register const struct ViceInodeInfo *p1 = _p1;
-    register const struct ViceInodeInfo *p2 = _p2;
+    const struct ViceInodeInfo *p1 = _p1;
+    const struct ViceInodeInfo *p2 = _p2;
     if (p1->u.vnode.vnodeNumber == INODESPECIAL
        || p2->u.vnode.vnodeNumber == INODESPECIAL) {
        VolumeId p1rwid, p2rwid;
@@ -1061,13 +1105,13 @@ CompareInodes(const void *_p1, const void *_p2)
 }
 
 void
-CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
-                 register struct InodeSummary *summary)
+CountVolumeInodes(struct ViceInodeInfo *ip, int maxInodes,
+                 struct InodeSummary *summary)
 {
     VolumeId volume = ip->u.vnode.volumeId;
     VolumeId rwvolume = volume;
-    register int n, nSpecial;
-    register Unique maxunique;
+    int n, nSpecial;
+    Unique maxunique;
     n = nSpecial = 0;
     maxunique = 0;
     while (maxInodes-- && volume == ip->u.vnode.volumeId) {
@@ -1091,7 +1135,7 @@ CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
 }
 
 int
-OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
+OnlyOneVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber, void *rock)
 {
     if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
        return (inodeinfo->u.special.parentId == singleVolumeNumber);
@@ -1105,33 +1149,37 @@ OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, vo
  * be unlinked by the caller.
  */
 int
-GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
+GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolumeNumber)
 {
-    struct afs_stat status;
     int forceSal, err;
     int code;
     struct ViceInodeInfo *ip, *ip_save;
     struct InodeSummary summary;
     char summaryFileName[50];
-    FILE *summaryFile;
+    FD_t summaryFile = INVALID_FD;
 #ifdef AFS_NT40_ENV
-    char *dev = fileSysPath;
-    char *wpath = fileSysPath;
+    char *dev = salvinfo->fileSysPath;
+    char *wpath = salvinfo->fileSysPath;
 #else
-    char *dev = fileSysDeviceName;
-    char *wpath = filesysfulldev;
+    char *dev = salvinfo->fileSysDeviceName;
+    char *wpath = salvinfo->filesysfulldev;
 #endif
-    char *part = fileSysPath;
+    char *part = salvinfo->fileSysPath;
     char *tdir;
+    int i;
+    int retcode = 0;
+    int deleted = 0;
+    afs_sfsize_t st_size;
 
     /* This file used to come from vfsck; cobble it up ourselves now... */
     if ((err =
-        ListViceInodes(dev, fileSysPath, inodeFile,
+        ListViceInodes(dev, salvinfo->fileSysPath, inodeFile,
                        singleVolumeNumber ? OnlyOneVolume : 0,
                        singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
        if (err == -2) {
            Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
-           return -1;
+           retcode = -1;
+           goto error;
        }
        Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
     }
@@ -1139,21 +1187,22 @@ GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
        Log("***Forced salvage of all volumes on this partition***\n");
        ForceSalvage = 1;
     }
-    fseek(inodeFile, 0L, SEEK_SET);
-    inodeFd = fileno(inodeFile);
-    if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
+    OS_SEEK(inodeFile, 0L, SEEK_SET);
+    salvinfo->inodeFd = inodeFile;
+    if (salvinfo->inodeFd == INVALID_FD ||
+        (st_size = OS_SIZE(salvinfo->inodeFd)) == -1) {
        Abort("No inode description file for \"%s\"; not salvaged\n", dev);
     }
     tdir = (tmpdir ? tmpdir : part);
 #ifdef AFS_NT40_ENV
     (void)_putenv("TMP=");     /* If "TMP" is set, then that overrides tdir. */
-    (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
+    (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp."));
 #else
-    (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
-                      "%s/salvage.temp.%d", tdir, getpid());
+    snprintf(summaryFileName, sizeof summaryFileName,
+            "%s" OS_DIRSEP "salvage.temp.%d", tdir, getpid());
 #endif
-    summaryFile = afs_fopen(summaryFileName, "a+");
-    if (summaryFile == NULL) {
+    summaryFile = OS_OPEN(summaryFileName, O_RDWR|O_APPEND|O_CREAT, 0666);
+    if (summaryFile == INVALID_FD) {
        Abort("Unable to create inode summary file\n");
     }
 
@@ -1162,6 +1211,9 @@ GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
      * semantics of unlink. In most places in the salvager, we really do
      * mean to unlink the file at that point. Those places have been
      * modified to actually do that so that the NT crt can be used there.
+     *
+     * jaltman - As commented elsewhere, this cannot work because fopen()
+     * does not open files with DELETE and FILE_SHARE_DELETE.
      */
     code = nt_unlink(summaryFileName);
 #else
@@ -1172,53 +1224,73 @@ GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
     }
 
     if (!canfork || debug || Fork() == 0) {
-       int nInodes;
-       unsigned long st_size=(unsigned long) status.st_size;
-       nInodes = st_size / sizeof(struct ViceInodeInfo);
+       int nInodes = st_size / sizeof(struct ViceInodeInfo);
        if (nInodes == 0) {
-           fclose(summaryFile);
+           OS_CLOSE(summaryFile);
            if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
-               RemoveTheForce(fileSysPath);
+               RemoveTheForce(salvinfo->fileSysPath);
            else {
                struct VolumeSummary *vsp;
                int i;
+               int foundSVN = 0;
 
-               GetVolumeSummary(singleVolumeNumber);
+               GetVolumeSummary(salvinfo, singleVolumeNumber);
 
-               for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
-                   if (vsp->fileName)
-                       DeleteExtraVolumeHeaderFile(vsp);
+               for (i = 0, vsp = salvinfo->volumeSummaryp; i < salvinfo->nVolumes; i++) {
+                   if (vsp->unused) {
+                       if (vsp->header.id == singleVolumeNumber) {
+                           foundSVN = 1;
+                       }
+                       DeleteExtraVolumeHeaderFile(salvinfo, vsp);
+                   }
+               }
+
+               if (!foundSVN) {
+                   if (Testing) {
+                       MaybeAskOnline(salvinfo, singleVolumeNumber);
+                   } else {
+                       /* make sure we get rid of stray .vol headers, even if
+                        * they're not in our volume summary (might happen if
+                        * e.g. something else created them and they're not in the
+                        * fileserver VGC) */
+                       VDestroyVolumeDiskHeader(salvinfo->fileSysPartition,
+                                                singleVolumeNumber, 0 /*parent*/);
+                       AskDelete(salvinfo, singleVolumeNumber);
+                   }
                }
            }
            Log("%s vice inodes on %s; not salvaged\n",
                singleVolumeNumber ? "No applicable" : "No", dev);
-           return -1;
+           retcode = -1;
+           deleted = 1;
+           goto error;
        }
-       ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
+       ip = malloc(nInodes*sizeof(struct ViceInodeInfo));
        if (ip == NULL) {
-           fclose(summaryFile);
+           OS_CLOSE(summaryFile);
            Abort
                ("Unable to allocate enough space to read inode table; %s not salvaged\n",
                 dev);
        }
-       if (read(inodeFd, ip, st_size) != st_size) {
-           fclose(summaryFile);
+       if (OS_READ(salvinfo->inodeFd, ip, st_size) != st_size) {
+           OS_CLOSE(summaryFile);
            Abort("Unable to read inode table; %s not salvaged\n", dev);
        }
        qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
-       if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
-           || write(inodeFd, ip, st_size) != st_size) {
-           fclose(summaryFile);
+       if (OS_SEEK(salvinfo->inodeFd, 0, SEEK_SET) == -1
+           || OS_WRITE(salvinfo->inodeFd, ip, st_size) != st_size) {
+           OS_CLOSE(summaryFile);
            Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
        }
        summary.index = 0;
        ip_save = ip;
        while (nInodes) {
            CountVolumeInodes(ip, nInodes, &summary);
-           if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
+           if (OS_WRITE(summaryFile, &summary, sizeof(summary)) != sizeof(summary)) {
                Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
-               fclose(summaryFile);
-               return -1;
+               OS_CLOSE(summaryFile);
+               retcode = -1;
+               goto error;
            }
            summary.index += (summary.nInodes);
            nInodes -= summary.nInodes;
@@ -1227,36 +1299,46 @@ GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
        free(ip_save);
        ip = ip_save = NULL;
        /* Following fflush is not fclose, because if it was debug mode would not work */
-       if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
+       if (OS_SYNC(summaryFile) == -1) {
            Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
-           fclose(summaryFile);
-           return -1;
+           OS_CLOSE(summaryFile);
+           retcode = -1;
+           goto error;
        }
        if (canfork && !debug) {
-           ShowLog = 0;
-           Exit(0);
+           QuietExit(0);
        }
     } else {
        if (Wait("Inode summary") == -1) {
-           fclose(summaryFile);
+           OS_CLOSE(summaryFile);
            Exit(1);            /* salvage of this partition aborted */
        }
     }
-    assert(afs_fstat(fileno(summaryFile), &status) != -1);
-    if (status.st_size != 0) {
+
+    st_size = OS_SIZE(summaryFile);
+    opr_Assert(st_size >= 0);
+    if (st_size != 0) {
        int ret;
-       unsigned long st_status=(unsigned long)status.st_size;
-       inodeSummary = (struct InodeSummary *)malloc(st_status);
-       assert(inodeSummary != NULL);
+       salvinfo->inodeSummary = malloc(st_size);
+       opr_Assert(salvinfo->inodeSummary != NULL);
        /* For GNU we need to do lseek to get the file pointer moved. */
-       assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
-       ret = read(fileno(summaryFile), inodeSummary, st_status);
-       assert(ret == st_status);
+       opr_Assert(OS_SEEK(summaryFile, 0, SEEK_SET) == 0);
+       ret = OS_READ(summaryFile, salvinfo->inodeSummary, st_size);
+       opr_Assert(ret == st_size);
     }
-    nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
-    Log("%d nVolumesInInodeFile %lu \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
-    fclose(summaryFile);
-    return 0;
+    salvinfo->nVolumesInInodeFile = st_size / sizeof(struct InodeSummary);
+    for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
+       salvinfo->inodeSummary[i].volSummary = NULL;
+    }
+    Log("%d nVolumesInInodeFile %lu \n",salvinfo->nVolumesInInodeFile,(unsigned long)st_size);
+    OS_CLOSE(summaryFile);
+
+ error:
+    if (retcode && singleVolumeNumber && !deleted) {
+       AskError(salvinfo, singleVolumeNumber);
+    }
+
+    return retcode;
 }
 
 /* Comparison routine for volume sort.
@@ -1265,8 +1347,8 @@ GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
 int
 CompareVolumes(const void *_p1, const void *_p2)
 {
-    register const struct VolumeSummary *p1 = _p1;
-    register const struct VolumeSummary *p2 = _p2;
+    const struct VolumeSummary *p1 = _p1;
+    const struct VolumeSummary *p2 = _p2;
     if (p1->header.parent != p2->header.parent)
        return p1->header.parent < p2->header.parent ? -1 : 1;
     if (p1->header.id == p1->header.parent)    /* p1 is rw volume */
@@ -1294,7 +1376,7 @@ CompareVolumes(const void *_p1, const void *_p2)
  * @note for non-DAFS, always returns 1
  */
 static int
-AskVolumeSummary(VolumeId singleVolumeNumber)
+AskVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
 {
     afs_int32 code = 1;
 #if defined(FSSYNC_BUILD_CLIENT) && defined(AFS_DEMAND_ATTACH_FS)
@@ -1308,7 +1390,7 @@ AskVolumeSummary(VolumeId singleVolumeNumber)
 
            memset(&res, 0, sizeof(res));
 
-           code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
+           code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
 
            /*
             * We must wait for the partition to finish scanning before
@@ -1323,7 +1405,7 @@ AskVolumeSummary(VolumeId singleVolumeNumber)
             */
            if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
                Log("waiting for fileserver to finish scanning partition %s...\n",
-                   fileSysPartition->name);
+                   salvinfo->fileSysPartition->name);
 
                for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
                    /* linearly ramp up from 1 to 10 seconds; nothing fancy,
@@ -1332,7 +1414,7 @@ AskVolumeSummary(VolumeId singleVolumeNumber)
                     * polled only once every ten seconds. */
                    sleep((i > 10) ? (i = 10) : i);
 
-                   code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
+                   code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
                }
            }
 
@@ -1348,23 +1430,23 @@ AskVolumeSummary(VolumeId singleVolumeNumber)
            }
 
            if (code) {
-               Log("fileserver refused VGCQuery request for volume %lu on "
+               Log("fileserver refused VGCQuery request for volume %" AFS_VOLID_FMT " on "
                    "partition %s, code %ld reason %ld\n",
-                   afs_printable_uint32_lu(singleVolumeNumber),
-                   fileSysPartition->name,
+                   afs_printable_VolumeId_lu(singleVolumeNumber),
+                   salvinfo->fileSysPartition->name,
                    afs_printable_int32_ld(code),
                    afs_printable_int32_ld(res.hdr.reason));
                goto done;
            }
 
            if (q_res.rw != singleVolumeNumber) {
-               Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
-                   afs_printable_uint32_lu(singleVolumeNumber),
-                   afs_printable_uint32_lu(q_res.rw));
+               Log("fileserver requested salvage of clone %" AFS_VOLID_FMT "; scheduling salvage of volume group %" AFS_VOLID_FMT "...\n",
+                   afs_printable_VolumeId_lu(singleVolumeNumber),
+                   afs_printable_VolumeId_lu(q_res.rw));
 #ifdef SALVSYNC_BUILD_CLIENT
                if (SALVSYNC_LinkVolume(q_res.rw,
                                       singleVolumeNumber,
-                                      fileSysPartition->name,
+                                      salvinfo->fileSysPartition->name,
                                       NULL) != SYNC_OK) {
                    Log("schedule request failed\n");
                }
@@ -1372,11 +1454,11 @@ AskVolumeSummary(VolumeId singleVolumeNumber)
                Exit(SALSRV_EXIT_VOLGROUP_LINK);
            }
 
-           volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
-           assert(volumeSummaryp != NULL);
+           salvinfo->volumeSummaryp = calloc(VOL_VG_MAX_VOLS, sizeof(struct VolumeSummary));
+           opr_Assert(salvinfo->volumeSummaryp != NULL);
 
-           nVolumes = 0;
-           vsp = volumeSummaryp;
+           salvinfo->nVolumes = 0;
+           vsp = salvinfo->volumeSummaryp;
 
            for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
                char name[VMAXPATHLEN];
@@ -1387,14 +1469,14 @@ AskVolumeSummary(VolumeId singleVolumeNumber)
 
                /* AskOffline for singleVolumeNumber was called much earlier */
                if (q_res.children[i] != singleVolumeNumber) {
-                   AskOffline(q_res.children[i], fileSysPartition->name);
-                   if (LockVolume(q_res.children[i])) {
+                   AskOffline(salvinfo, q_res.children[i]);
+                   if (LockVolume(salvinfo, q_res.children[i])) {
                        /* need to retry */
                        return -1;
                    }
                }
 
-               code = VReadVolumeDiskHeader(q_res.children[i], fileSysPartition, &diskHdr);
+               code = VReadVolumeDiskHeader(q_res.children[i], salvinfo->fileSysPartition, &diskHdr);
                if (code) {
                    Log("Cannot read header for %lu; trying to salvage group anyway\n",
                        afs_printable_uint32_lu(q_res.children[i]));
@@ -1404,12 +1486,12 @@ AskVolumeSummary(VolumeId singleVolumeNumber)
 
                DiskToVolumeHeader(&vsp->header, &diskHdr);
                VolumeExternalName_r(q_res.children[i], name, sizeof(name));
-               vsp->fileName = ToString(name);
-               nVolumes++;
+               vsp->unused = 1;
+               salvinfo->nVolumes++;
                vsp++;
            }
 
-           qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
+           qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
                  CompareVolumes);
        }
       done:
@@ -1456,6 +1538,7 @@ struct SalvageScanParams {
     afs_int32 totalVolumes;      /**< max # of vols we should encounter (the
                                   * # of vols we've alloc'd memory for) */
     int retry;  /**< do we need to retry vol lock/checkout? */
+    struct SalvInfo *salvinfo; /**< salvage job info */
 };
 
 /**
@@ -1485,10 +1568,14 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
     struct SalvageScanParams *params;
     struct VolumeSummary summary;
     VolumeId singleVolumeNumber;
+    struct SalvInfo *salvinfo;
 
     params = (struct SalvageScanParams *)rock;
 
+    memset(&summary, 0, sizeof(summary));
+
     singleVolumeNumber = params->singleVolumeNumber;
+    salvinfo = params->salvinfo;
 
     DiskToVolumeHeader(&summary.header, hdr);
 
@@ -1497,8 +1584,9 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
 
        if (programType == salvageServer) {
 #ifdef SALVSYNC_BUILD_CLIENT
-           Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
-               summary.header.id, summary.header.parent);
+           Log("fileserver requested salvage of clone %" AFS_VOLID_FMT "; scheduling salvage of volume group %" AFS_VOLID_FMT "...\n",
+               afs_printable_VolumeId_lu(summary.header.id),
+               afs_printable_VolumeId_lu(summary.header.parent));
            if (SALVSYNC_LinkVolume(summary.header.parent,
                                    summary.header.id,
                                    dp->name,
@@ -1509,8 +1597,8 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
            Exit(SALSRV_EXIT_VOLGROUP_LINK);
 
        } else {
-           Log("%u is a read-only volume; not salvaged\n",
-               singleVolumeNumber);
+           Log("%" AFS_VOLID_FMT " is a read-only volume; not salvaged\n",
+               afs_printable_VolumeId_lu(singleVolumeNumber));
            Exit(1);
        }
     }
@@ -1520,15 +1608,15 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
 
        /* check if the header file is incorrectly named */
        int badname = 0;
-       const char *base = strrchr(name, '/');
+       const char *base = strrchr(name, OS_DIRSEPC);
        if (base) {
            base++;
        } else {
            base = name;
        }
 
-       (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
-                          VFORMAT, afs_printable_uint32_lu(summary.header.id));
+       snprintf(nameShouldBe, sizeof nameShouldBe,
+                VFORMAT, afs_printable_VolumeId_lu(summary.header.id));
 
 
        if (strcmp(nameShouldBe, base)) {
@@ -1541,18 +1629,18 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
             * the last try looking at it; avoid AskOffline'ing the same vol
             * multiple times */
 
-           if (singleVolumeNumber 
+           if (singleVolumeNumber
                && summary.header.id != singleVolumeNumber) {
                /* don't offline singleVolumeNumber; we already did that
                 * earlier */
 
-               AskOffline(summary.header.id, fileSysPartition->name);
+               AskOffline(salvinfo, summary.header.id);
 
 #ifdef AFS_DEMAND_ATTACH_FS
                if (!badname) {
                    /* don't lock the volume if the header is bad, since we're
                     * about to delete it anyway. */
-                   if (LockVolume(summary.header.id)) {
+                   if (LockVolume(salvinfo, summary.header.id)) {
                        params->retry = 1;
                        return -1;
                    }
@@ -1570,7 +1658,7 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
            return 1;
        }
 
-       summary.fileName = ToString(base);
+       summary.unused = 1;
        params->nVolumes++;
 
        if (params->nVolumes > params->totalVolumes) {
@@ -1642,7 +1730,7 @@ UnlinkHeader(struct DiskPartition64 *dp, const char *name,
 }
 
 /**
- * Populates volumeSummaryp with volume summary information, either by asking
+ * Populates salvinfo->volumeSummaryp with volume summary information, either by asking
  * the fileserver for VG information, or by scanning the /vicepX partition.
  *
  * @param[in] singleVolumeNumber  the volume ID of the single volume group we
@@ -1655,13 +1743,13 @@ UnlinkHeader(struct DiskPartition64 *dp, const char *name,
  *             volumes must be retried
  */
 int
-GetVolumeSummary(VolumeId singleVolumeNumber)
+GetVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
 {
     afs_int32 nvols = 0;
     struct SalvageScanParams params;
     int code;
 
-    code = AskVolumeSummary(singleVolumeNumber);
+    code = AskVolumeSummary(salvinfo, singleVolumeNumber);
     if (code == 0) {
        /* we successfully got the vol information from the fileserver; no
         * need to scan the partition */
@@ -1674,10 +1762,10 @@ GetVolumeSummary(VolumeId singleVolumeNumber)
 
     if (!singleVolumeNumber) {
        /* Count how many volumes we have in /vicepX */
-       code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, CountHeader,
+       code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, CountHeader,
                                  NULL, &nvols);
        if (code < 0) {
-           Abort("Can't read directory %s; not salvaged\n", fileSysPath);
+           Abort("Can't read directory %s; not salvaged\n", salvinfo->fileSysPath);
        }
        if (!nvols)
            nvols = 1;
@@ -1685,18 +1773,19 @@ GetVolumeSummary(VolumeId singleVolumeNumber)
        nvols = VOL_VG_MAX_VOLS;
     }
 
-    volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
-    assert(volumeSummaryp != NULL);
+    salvinfo->volumeSummaryp = calloc(nvols, sizeof(struct VolumeSummary));
+    opr_Assert(salvinfo->volumeSummaryp != NULL);
 
     params.singleVolumeNumber = singleVolumeNumber;
-    params.vsp = volumeSummaryp;
+    params.vsp = salvinfo->volumeSummaryp;
     params.nVolumes = 0;
     params.totalVolumes = nvols;
     params.retry = 0;
+    params.salvinfo = salvinfo;
 
     /* walk the partition directory of volume headers and record the info
      * about them; unlinking invalid headers */
-    code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, RecordHeader,
+    code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, RecordHeader,
                               UnlinkHeader, &params);
     if (params.retry) {
        /* we apparently need to retry checking-out/locking volumes */
@@ -1705,19 +1794,20 @@ GetVolumeSummary(VolumeId singleVolumeNumber)
     if (code < 0) {
        Abort("Failed to get volume header summary\n");
     }
-    nVolumes = params.nVolumes;
+    salvinfo->nVolumes = params.nVolumes;
 
-    qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
+    qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
          CompareVolumes);
 
     return 0;
 }
 
-/* Find the link table. This should be associated with the RW volume or, if
- * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
+#ifdef AFS_NAMEI_ENV
+/* Find the link table. This should be associated with the RW volume, even
+ * if there is only an RO volume at this site.
  */
-Inode
-FindLinkHandle(register struct InodeSummary *isp, int nVols,
+static Inode
+FindLinkHandle(struct InodeSummary *isp, int nVols,
               struct ViceInodeInfo *allInodes)
 {
     int i, j;
@@ -1726,44 +1816,96 @@ FindLinkHandle(register struct InodeSummary *isp, int nVols,
     for (i = 0; i < nVols; i++) {
        ip = allInodes + isp[i].index;
        for (j = 0; j < isp[i].nSpecialInodes; j++) {
-           if (ip[j].u.special.type == VI_LINKTABLE)
+           if (ip[j].u.special.volumeId == isp->RWvolumeId &&
+               ip[j].u.special.parentId == isp->RWvolumeId &&
+               ip[j].u.special.type == VI_LINKTABLE) {
                return ip[j].inodeNumber;
+           }
        }
     }
     return (Inode) - 1;
 }
 
+static int
+CheckDupLinktable(struct SalvInfo *salvinfo, struct InodeSummary *isp, struct ViceInodeInfo *ip)
+{
+    afs_ino_str_t stmp;
+    if (ip->u.vnode.vnodeNumber != INODESPECIAL) {
+       /* not a linktable; process as a normal file */
+       return 0;
+    }
+    if (ip->u.special.type != VI_LINKTABLE) {
+       /* not a linktable; process as a normal file */
+       return 0;
+    }
+
+    /* make sure nothing inc/decs it */
+    ip->linkCount = 0;
+
+    if (ip->u.special.volumeId == ip->u.special.parentId) {
+       /* This is a little weird, but shouldn't break anything, and there is
+        * no known way that this can happen; just do nothing, in case deleting
+        * it would screw something up. */
+       Log("Inode %s appears to be a valid linktable for id (%u), but it's not\n",
+           PrintInode(stmp, ip->inodeNumber), ip->u.special.parentId);
+       Log("the linktable for our volume group (%u). This is unusual, since\n",
+           isp->RWvolumeId);
+       Log("there should only be one linktable per volume group. I'm leaving\n");
+       Log("it alone, just to be safe.\n");
+       return -1;
+    }
+
+    Log("Linktable %s appears to be invalid (parentid/volumeid mismatch: %u != %u)\n",
+        PrintInode(stmp, ip->inodeNumber), ip->u.special.parentId, ip->u.special.volumeId);
+    if (Testing) {
+       Log("Would have deleted linktable inode %s\n", PrintInode(stmp, ip->inodeNumber));
+    } else {
+       IHandle_t *tmpH;
+       namei_t ufs_name;
+
+       Log("Deleting linktable inode %s\n", PrintInode(stmp, ip->inodeNumber));
+       IH_INIT(tmpH, salvinfo->fileSysDevice, isp->RWvolumeId, ip->inodeNumber);
+       namei_HandleToName(&ufs_name, tmpH);
+       if (unlink(ufs_name.n_path) < 0) {
+           Log("Error %d unlinking path %s\n", errno, ufs_name.n_path);
+       }
+    }
+
+    return -1;
+}
+#endif
+
 int
-CreateLinkTable(register struct InodeSummary *isp, Inode ino)
+CreateLinkTable(struct SalvInfo *salvinfo, struct InodeSummary *isp, Inode ino)
 {
     struct versionStamp version;
     FdHandle_t *fdP;
 
     if (!VALID_INO(ino))
        ino =
-           IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
+           IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->RWvolumeId,
                      INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
     if (!VALID_INO(ino))
        Abort
-           ("Unable to allocate link table inode for volume %u (error = %d)\n",
-            isp->RWvolumeId, errno);
-    IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
-    fdP = IH_OPEN(VGLinkH);
+           ("Unable to allocate link table inode for volume %" AFS_VOLID_FMT " (error = %d)\n",
+            afs_printable_VolumeId_lu(isp->RWvolumeId), errno);
+    IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
+    fdP = IH_OPEN(salvinfo->VGLinkH);
     if (fdP == NULL)
-       Abort("Can't open link table for volume %u (error = %d)\n",
-             isp->RWvolumeId, errno);
+       Abort("Can't open link table for volume %" AFS_VOLID_FMT " (error = %d)\n",
+             afs_printable_VolumeId_lu(isp->RWvolumeId), errno);
 
     if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
-       Abort("Can't truncate link table for volume %u (error = %d)\n",
-             isp->RWvolumeId, errno);
+       Abort("Can't truncate link table for volume %" AFS_VOLID_FMT " (error = %d)\n",
+             afs_printable_VolumeId_lu(isp->RWvolumeId), errno);
 
     version.magic = LINKTABLEMAGIC;
     version.version = LINKTABLEVERSION;
 
-    if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
+    if (FDH_PWRITE(fdP, (char *)&version, sizeof(version), 0)
        != sizeof(version))
-       Abort("Can't truncate link table for volume %u (error = %d)\n",
-             isp->RWvolumeId, errno);
+       Abort("Can't truncate link table for volume %" AFS_VOLID_FMT " (error = %d)\n",
+             afs_printable_VolumeId_lu(isp->RWvolumeId), errno);
 
     FDH_REALLYCLOSE(fdP);
 
@@ -1781,12 +1923,12 @@ void *
 nt_SVG(void *arg)
 {
     SVGParms_t *parms = (SVGParms_t *) arg;
-    DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
+    DoSalvageVolumeGroup(parms->svgp_salvinfo, parms->svgp_inodeSummaryp, parms->svgp_count);
     return NULL;
 }
 
 void
-SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
+nt_SalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
 {
     pthread_t tid;
     pthread_attr_t tattr;
@@ -1794,13 +1936,14 @@ SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
     SVGParms_t parms;
 
     /* Initialize per volume global variables, even if later code does so */
-    VolumeChanged = 0;
-    VGLinkH = NULL;
-    VGLinkH_cnt = 0;
-    memset(&VolInfo, 0, sizeof(VolInfo));
+    salvinfo->VolumeChanged = 0;
+    salvinfo->VGLinkH = NULL;
+    salvinfo->VGLinkH_cnt = 0;
+    memset(&salvinfo->VolInfo, 0, sizeof(salvinfo->VolInfo));
 
     parms.svgp_inodeSummaryp = isp;
     parms.svgp_count = nVols;
+    parms.svgp_salvinfo = salvinfo;
     code = pthread_attr_init(&tattr);
     if (code) {
        Log("Failed to salvage volume group %u: pthread_attr_init()\n",
@@ -1823,7 +1966,7 @@ SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
 #endif /* AFS_NT40_ENV */
 
 void
-DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
+DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
 {
     struct ViceInodeInfo *inodes, *allInodes, *ip;
     int i, totalInodes, size, salvageTo;
@@ -1834,11 +1977,11 @@ DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
     int VGLinkH_p1 =0;
     FdHandle_t *fdP = NULL;
 
-    VGLinkH_cnt = 0;
+    salvinfo->VGLinkH_cnt = 0;
     haveRWvolume = (isp->volumeId == isp->RWvolumeId
                    && isp->nSpecialInodes > 0);
     if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
-       if (!ForceSalvage && QuickCheck(isp, nVols))
+       if (!ForceSalvage && QuickCheck(salvinfo, isp, nVols))
            return;
     }
     if (ShowMounts && !haveRWvolume)
@@ -1850,14 +1993,14 @@ DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
     for (i = 0, totalInodes = 0; i < nVols; i++)
        totalInodes += isp[i].nInodes;
     size = totalInodes * sizeof(struct ViceInodeInfo);
-    inodes = (struct ViceInodeInfo *)malloc(size);
+    inodes = malloc(size);
     allInodes = inodes - isp->index;   /* this would the base of all the inodes
                                         * for the partition, if all the inodes
                                         * had been read into memory */
-    assert(afs_lseek
-          (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
+    opr_Verify(OS_SEEK
+          (salvinfo->inodeFd, isp->index * sizeof(struct ViceInodeInfo),
            SEEK_SET) != -1);
-    assert(read(inodeFd, inodes, size) == size);
+    opr_Verify(OS_READ(salvinfo->inodeFd, inodes, size) == size);
 
     /* Don't try to salvage a read write volume if there isn't one on this
      * partition */
@@ -1866,38 +2009,47 @@ DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
 #ifdef AFS_NAMEI_ENV
     ino = FindLinkHandle(isp, nVols, allInodes);
     if (VALID_INO(ino)) {
-       IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
-       fdP = IH_OPEN(VGLinkH);
+       IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
+       fdP = IH_OPEN(salvinfo->VGLinkH);
+    }
+    if (VALID_INO(ino) && fdP != NULL) {
+       struct versionStamp header;
+       afs_sfsize_t nBytes;
+
+       nBytes = FDH_PREAD(fdP, (char *)&header, sizeof(struct versionStamp), 0);
+       if (nBytes != sizeof(struct versionStamp)
+           || header.magic != LINKTABLEMAGIC) {
+            Log("Bad linktable header for volume %" AFS_VOLID_FMT ".\n", afs_printable_VolumeId_lu(isp->RWvolumeId));
+           FDH_REALLYCLOSE(fdP);
+           fdP = NULL;
+       }
     }
     if (!VALID_INO(ino) || fdP == NULL) {
-       Log("%s link table for volume %u.\n",
-           Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
+       Log("%s link table for volume %" AFS_VOLID_FMT ".\n",
+           Testing ? "Would have recreated" : "Recreating", afs_printable_VolumeId_lu(isp->RWvolumeId));
        if (Testing) {
-           IH_INIT(VGLinkH, fileSysDevice, -1, -1);
+           IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
        } else {
-            int i, j;
-            struct ViceInodeInfo *ip;
-           CreateLinkTable(isp, ino);
-           fdP = IH_OPEN(VGLinkH);
-            /* Sync fake 1 link counts to the link table, now that it exists */
-            if (fdP) {
-               for (i = 0; i < nVols; i++) {
-                       ip = allInodes + isp[i].index;
-                        for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
-#ifdef AFS_NT40_ENV
-                                nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
-#else
-                                namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
-#endif
+           int i, j;
+           struct ViceInodeInfo *ip;
+           CreateLinkTable(salvinfo, isp, ino);
+           fdP = IH_OPEN(salvinfo->VGLinkH);
+           /* Sync fake 1 link counts to the link table, now that it exists */
+           if (fdP) {
+               for (i = 0; i < nVols; i++) {
+                   ip = allInodes + isp[i].index;
+                   for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
+                       namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 0);
+                       ip[j].linkCount = 1;
                    }
-               }
+               }
            }
        }
     }
     if (fdP)
        FDH_REALLYCLOSE(fdP);
 #else
-    IH_INIT(VGLinkH, fileSysDevice, -1, -1);
+    IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
 #endif
 
     /* Salvage in reverse order--read/write volume last; this way any
@@ -1908,28 +2060,46 @@ DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
        int rw = (i == 0);
        struct InodeSummary *lisp = &isp[i];
 #ifdef AFS_NAMEI_ENV
-       /* If only the RO is present on this partition, the link table
-        * shows up as a RW volume special file. Need to make sure the
-        * salvager doesn't try to salvage the non-existent RW.
-        */
-       if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
-           /* If this only special inode is the link table, continue */
-           if (inodes->u.special.type == VI_LINKTABLE) {
-               haveRWvolume = 0;
-               continue;
+       if (rw && (nVols > 1 || isp[i].nSpecialInodes == isp[i].nInodes)) {
+           /* If nVols > 1, we have more than one vol in this volgroup, so
+            * the RW inodes we detected may just be for the linktable, and
+            * there is no actual RW volume.
+            *
+            * Additionally, if we only have linktable inodes (no other
+            * special inodes, no data inodes), there is also no actual RW
+            * volume to salvage; this is just cruft left behind by something
+            * else. In that case nVols will only be 1, though, so also
+            * perform this linktables-only check if we don't have any
+            * non-special inodes. */
+           int inode_i;
+           int all_linktables = 1;
+           for (inode_i = 0; inode_i < isp[i].nSpecialInodes; inode_i++) {
+               if (inodes[inode_i].u.special.type != VI_LINKTABLE) {
+                   all_linktables = 0;
+                   break;
+               }
+           }
+           if (all_linktables) {
+               /* All we have are linktable special inodes, so skip salvaging
+                * the RW; there was never an RW volume here. If we don't do
+                * this, we risk creating a new "phantom" RW that the VLDB
+                * doesn't know about, which is confusing and can cause
+                * problems. */
+                haveRWvolume = 0;
+                continue;
            }
        }
 #endif
        if (!Showmode)
-           Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
-               lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
+           Log("%s VOLUME %" AFS_VOLID_FMT "%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
+               afs_printable_VolumeId_lu(lisp->volumeId), (Testing ? "(READONLY mode)" : ""));
        /* Check inodes twice.  The second time do things seriously.  This
         * way the whole RO volume can be deleted, below, if anything goes wrong */
        for (check = 1; check >= 0; check--) {
            int deleteMe;
-           if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
+           if (SalvageVolumeHeaderFile(salvinfo, lisp, allInodes, rw, check, &deleteMe)
                == -1) {
-               MaybeZapVolume(lisp, "Volume header", deleteMe, check);
+               MaybeZapVolume(salvinfo, lisp, "Volume header", deleteMe, check);
                if (rw && deleteMe) {
                    haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
                                         * volume won't be called */
@@ -1940,8 +2110,8 @@ DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
            }
            if (rw && check == 1)
                continue;
-           if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
-               MaybeZapVolume(lisp, "Vnode index", 0, check);
+           if (SalvageVnodes(salvinfo, isp, lisp, allInodes, check) == -1) {
+               MaybeZapVolume(salvinfo, lisp, "Vnode index", 0, check);
                break;
            }
        }
@@ -1949,37 +2119,52 @@ DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
 
     /* Fix actual inode counts */
     if (!Showmode) {
+       afs_ino_str_t stmp;
        Log("totalInodes %d\n",totalInodes);
        for (ip = inodes; totalInodes; ip++, totalInodes--) {
            static int TraceBadLinkCounts = 0;
 #ifdef AFS_NAMEI_ENV
-           if (VGLinkH->ih_ino == ip->inodeNumber) {
-               dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
+           if (salvinfo->VGLinkH->ih_ino == ip->inodeNumber) {
+               dec_VGLinkH = ip->linkCount - salvinfo->VGLinkH_cnt;
                VGLinkH_p1 = ip->u.param[0];
                continue;       /* Deal with this last. */
+           } else if (CheckDupLinktable(salvinfo, isp, ip)) {
+               /* Don't touch this inode; CheckDupLinktable has handled it */
+               continue;
            }
 #endif
            if (ip->linkCount != 0 && TraceBadLinkCounts) {
                TraceBadLinkCounts--;   /* Limit reports, per volume */
-               Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %llu, p=(%u,%u,%u,%u)\n", ip->linkCount, PrintInode(NULL, ip->inodeNumber), (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
+               Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %llu, p=(%u,%u,%u,%u)\n", ip->linkCount, PrintInode(stmp, ip->inodeNumber), (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]); /* VolumeId in param */
            }
+
+           /* If ip->linkCount is non-zero at this point, then the linkcount
+            * for the inode on disk is wrong. Initially linkCount is set to
+            * the actual link count of the inode on disk, and then we (the
+            * salvager) decrement it for every reference to that inode that we
+            * find. So if linkCount is still positive by this point, it means
+            * that the linkcount on disk is too high, so we should DEC the
+            * inode. If linkCount is negative, it means the linkcount is too
+            * low, so we should INC the inode.
+            *
+            * If we get an error while INC'ing or DEC'ing, that's a little
+            * odd and indicates a bug, but try to continue anyway, so the
+            * volume may still be made accessible. */
            while (ip->linkCount > 0) {
-               /* below used to assert, not break */
                if (!Testing) {
-                   if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
+                   if (IH_DEC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
                        Log("idec failed. inode %s errno %d\n",
-                           PrintInode(NULL, ip->inodeNumber), errno);
+                           PrintInode(stmp, ip->inodeNumber), errno);
                        break;
                    }
                }
                ip->linkCount--;
            }
            while (ip->linkCount < 0) {
-               /* these used to be asserts */
                if (!Testing) {
-                   if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
+                   if (IH_INC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
                        Log("iinc failed. inode %s errno %d\n",
-                           PrintInode(NULL, ip->inodeNumber), errno);
+                           PrintInode(stmp, ip->inodeNumber), errno);
                        break;
                    }
                }
@@ -1988,13 +2173,13 @@ DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
        }
 #ifdef AFS_NAMEI_ENV
        while (dec_VGLinkH > 0) {
-           if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
+           if (IH_DEC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
                Log("idec failed on link table, errno = %d\n", errno);
            }
            dec_VGLinkH--;
        }
        while (dec_VGLinkH < 0) {
-           if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
+           if (IH_INC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
                Log("iinc failed on link table, errno = %d\n", errno);
            }
            dec_VGLinkH++;
@@ -2004,20 +2189,19 @@ DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
     free(inodes);
     /* Directory consistency checks on the rw volume */
     if (haveRWvolume)
-       SalvageVolume(isp, VGLinkH);
-    IH_RELEASE(VGLinkH);
+       SalvageVolume(salvinfo, isp, salvinfo->VGLinkH);
+    IH_RELEASE(salvinfo->VGLinkH);
 
     if (canfork && !debug) {
-       ShowLog = 0;
-       Exit(0);
+       QuietExit(0);
     }
 }
 
 int
-QuickCheck(register struct InodeSummary *isp, int nVols)
+QuickCheck(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
 {
     /* Check headers BEFORE forking */
-    register int i;
+    int i;
     IHandle_t *h;
 
     for (i = 0; i < nVols; i++) {
@@ -2030,7 +2214,7 @@ QuickCheck(register struct InodeSummary *isp, int nVols)
                continue;
            return 0;
        }
-       IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
+       IH_INIT(h, salvinfo->fileSysDevice, vs->header.parent, vs->header.volumeInfo);
        if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
            == sizeof(volHeader)
            && volHeader.stamp.magic == VOLUMEINFOMAGIC
@@ -2066,16 +2250,18 @@ QuickCheck(register struct InodeSummary *isp, int nVols)
  */
 
 int
-SalvageVolumeHeaderFile(register struct InodeSummary *isp,
-                       register struct ViceInodeInfo *inodes, int RW,
+SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
+                       struct ViceInodeInfo *inodes, int RW,
                        int check, int *deleteMe)
 {
     int i;
-    register struct ViceInodeInfo *ip;
+    struct ViceInodeInfo *ip;
     int allinodesobsolete = 1;
     struct VolumeDiskHeader diskHeader;
     afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
     int *skip;
+    struct VolumeHeader tempHeader;
+    struct afs_inode_info stuff[MAXINODETYPE];
 
     /* keeps track of special inodes that are probably 'good'; they are
      * referenced in the vol header, and are included in the given inodes
@@ -2090,10 +2276,8 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
 
     memset(goodspecial, 0, sizeof(goodspecial));
 
-    skip = malloc(isp->nSpecialInodes * sizeof(*skip));
-    if (skip) {
-       memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
-    } else {
+    skip = calloc(isp->nSpecialInodes, sizeof(*skip));
+    if (skip == NULL) {
        Log("cannot allocate memory for inode skip array when salvaging "
            "volume %lu; not performing duplicate special inode recovery\n",
            afs_printable_uint32_lu(isp->volumeId));
@@ -2101,6 +2285,8 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
         * if we detect duplicate special inodes */
     }
 
+    init_inode_info(&tempHeader, stuff);
+
     /*
      * First, look at the special inodes and see if any are referenced by
      * the existing volume header. If we find duplicate special inodes, we
@@ -2142,8 +2328,8 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
                continue;
            }
            if (!Showmode) {
-               Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
-                   ip->u.special.type, isp->volumeId,
+               Log("Duplicate special %d inodes for volume %" AFS_VOLID_FMT " found (%s, %s);\n",
+                   ip->u.special.type, afs_printable_VolumeId_lu(isp->volumeId),
                    PrintInode(stmp1, ip->inodeNumber),
                    PrintInode(stmp2, (ip+1)->inodeNumber));
            }
@@ -2166,7 +2352,7 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
                }
            } else {
                if (!Showmode)
-                   Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
+                   Log("cannot determine which is correct; salvage of volume %" AFS_VOLID_FMT " aborted\n", afs_printable_VolumeId_lu(isp->volumeId));
                if (skip) {
                    free(skip);
                }
@@ -2175,11 +2361,12 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
        }
     }
     for (i = 0; i < isp->nSpecialInodes; i++) {
+       afs_ino_str_t stmp;
        ip = &inodes[isp->index + i];
        if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
            if (check) {
                Log("Rubbish header inode %s of type %d\n",
-                   PrintInode(NULL, ip->inodeNumber),
+                   PrintInode(stmp, ip->inodeNumber),
                    ip->u.special.type);
                if (skip) {
                    free(skip);
@@ -2187,17 +2374,17 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
                return -1;
            }
            Log("Rubbish header inode %s of type %d; deleted\n",
-               PrintInode(NULL, ip->inodeNumber),
+               PrintInode(stmp, ip->inodeNumber),
                ip->u.special.type);
        } else if (!stuff[ip->u.special.type - 1].obsolete) {
            if (skip && skip[i]) {
                if (orphans == ORPH_REMOVE) {
                    Log("Removing orphan special inode %s of type %d\n",
-                       PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
+                       PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
                    continue;
                } else {
                    Log("Ignoring orphan special inode %s of type %d\n",
-                       PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
+                       PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
                    /* fall through to the ip->linkCount--; line below */
                }
            } else {
@@ -2220,7 +2407,7 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
     }
 
     if (!check)
-       VGLinkH_cnt++;          /* one for every header. */
+       salvinfo->VGLinkH_cnt++;                /* one for every header. */
 
     if (!RW && !check && isp->volSummary) {
        ClearROInUseBit(isp->volSummary);
@@ -2231,33 +2418,33 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
        if (stuff[i].inodeType == VI_LINKTABLE) {
            /* Gross hack: SalvageHeader does a bcmp on the volume header.
             * And we may have recreated the link table earlier, so set the
-            * RW header as well.
+            * RW header as well. The header magic was already checked.
             */
-           if (VALID_INO(VGLinkH->ih_ino)) {
-               *stuff[i].inode = VGLinkH->ih_ino;
+           if (VALID_INO(salvinfo->VGLinkH->ih_ino)) {
+               *stuff[i].inode = salvinfo->VGLinkH->ih_ino;
            }
            continue;
        }
-       if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
+       if (SalvageHeader(salvinfo, &stuff[i], isp, check, deleteMe) == -1 && check)
            return -1;
     }
 
     if (isp->volSummary == NULL) {
        char path[64];
        char headerName[64];
-       (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
-       (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
+       snprintf(headerName, sizeof headerName, VFORMAT,
+                afs_printable_VolumeId_lu(isp->volumeId));
+       snprintf(path, sizeof path, "%s" OS_DIRSEP "%s",
+                salvinfo->fileSysPath, headerName);
        if (check) {
-           Log("No header file for volume %u\n", isp->volumeId);
+           Log("No header file for volume %" AFS_VOLID_FMT "\n", afs_printable_VolumeId_lu(isp->volumeId));
            return -1;
        }
        if (!Showmode)
-           Log("No header file for volume %u; %screating %s\n",
-               isp->volumeId, (Testing ? "it would have been " : ""),
+           Log("No header file for volume %" AFS_VOLID_FMT "; %screating %s\n",
+               afs_printable_VolumeId_lu(isp->volumeId), (Testing ? "it would have been " : ""),
                path);
-       isp->volSummary = (struct VolumeSummary *)
-           malloc(sizeof(struct VolumeSummary));
-       isp->volSummary->fileName = ToString(headerName);
+       isp->volSummary = calloc(1, sizeof(struct VolumeSummary));
 
        writefunc = VCreateVolumeDiskHeader;
     } else {
@@ -2270,14 +2457,9 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
        if (memcmp
            (&isp->volSummary->header, &tempHeader,
             sizeof(struct VolumeHeader))) {
-           /* We often remove the name before calling us, so we make a fake one up */
-           if (isp->volSummary->fileName) {
-               strcpy(headerName, isp->volSummary->fileName);
-           } else {
-               (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
-               isp->volSummary->fileName = ToString(headerName);
-           }
-           (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
+           VolumeExternalName_r(isp->volumeId, headerName, sizeof(headerName));
+           snprintf(path, sizeof path, "%s" OS_DIRSEP "%s",
+                    salvinfo->fileSysPath, headerName);
 
            Log("Header file %s is damaged or no longer valid%s\n", path,
                (check ? "" : "; repairing"));
@@ -2292,28 +2474,28 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
               sizeof(struct VolumeHeader));
        if (Testing) {
            if (!Showmode)
-               Log("It would have written a new header file for volume %u\n",
-                   isp->volumeId);
+               Log("It would have written a new header file for volume %" AFS_VOLID_FMT "\n",
+                   afs_printable_VolumeId_lu(isp->volumeId));
        } else {
            afs_int32 code;
            VolumeHeaderToDisk(&diskHeader, &tempHeader);
-           code = (*writefunc)(&diskHeader, fileSysPartition);
+           code = (*writefunc)(&diskHeader, salvinfo->fileSysPartition);
            if (code) {
-               Log("Error %ld writing volume header file for volume %lu\n",
+               Log("Error %ld writing volume header file for volume %" AFS_VOLID_FMT "\n",
                    afs_printable_int32_ld(code),
-                   afs_printable_uint32_lu(diskHeader.id));
+                   afs_printable_VolumeId_lu(diskHeader.id));
                return -1;
            }
        }
     }
-    IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
+    IH_INIT(isp->volSummary->volumeInfoHandle, salvinfo->fileSysDevice, isp->RWvolumeId,
            isp->volSummary->header.volumeInfo);
     return 0;
 }
 
 int
-SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
-             int *deleteMe)
+SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
+              struct InodeSummary *isp, int check, int *deleteMe)
 {
     union {
        VolumeDiskData volumeInfo;
@@ -2328,7 +2510,7 @@ SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
        return 0;
 #ifndef AFS_NAMEI_ENV
     if (sp->inodeType == VI_LINKTABLE)
-       return 0;
+       return 0; /* header magic was already checked */
 #endif
     if (*(sp->inode) == 0) {
        if (check) {
@@ -2340,7 +2522,7 @@ SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
                (Testing ? "it would have recreated it" : "recreating"));
        if (!Testing) {
            *(sp->inode) =
-               IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
+               IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->volumeId,
                          INODESPECIAL, sp->inodeType, isp->RWvolumeId);
            if (!VALID_INO(*(sp->inode)))
                Abort
@@ -2350,7 +2532,7 @@ SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
        recreate = 1;
     }
 
-    IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
+    IH_INIT(specH, salvinfo->fileSysDevice, isp->RWvolumeId, *(sp->inode));
     fdP = IH_OPEN(specH);
     if (OKToZap && (fdP == NULL) && BadError(errno)) {
        /* bail out early and destroy the volume */
@@ -2366,7 +2548,7 @@ SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
              sp->description, errno);
 
     if (!recreate
-       && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
+       && (FDH_PREAD(fdP, (char *)&header, sp->size, 0) != sp->size
            || header.fileHeader.magic != sp->stamp.magic)) {
        if (check) {
            Log("Part of the header (%s) is corrupted\n", sp->description);
@@ -2381,6 +2563,14 @@ SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
         * it below */
        memset(&header, 0, sizeof(header));
     }
+#ifdef AFS_NAMEI_ENV
+    if (namei_FixSpecialOGM(fdP, check)) {
+       Log("Error with namei header OGM data (%s)\n", sp->description);
+       FDH_REALLYCLOSE(fdP);
+       IH_RELEASE(specH);
+       return -1;
+    }
+#endif
     if (sp->inodeType == VI_VOLINFO
        && header.volumeInfo.destroyMe == DESTROY_ME) {
        if (deleteMe)
@@ -2406,25 +2596,20 @@ SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
            header.volumeInfo.stamp = sp->stamp;
            header.volumeInfo.id = isp->volumeId;
            header.volumeInfo.parentId = isp->RWvolumeId;
-           sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
-           Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
-               isp->volumeId, isp->volumeId);
+           sprintf(header.volumeInfo.name, "bogus.%" AFS_VOLID_FMT, afs_printable_VolumeId_lu(isp->volumeId));
+           Log("Warning: the name of volume %" AFS_VOLID_FMT " is now \"bogus.%" AFS_VOLID_FMT "\"\n",
+               afs_printable_VolumeId_lu(isp->volumeId), afs_printable_VolumeId_lu(isp->volumeId));
            header.volumeInfo.inService = 0;
            header.volumeInfo.blessed = 0;
            /* The + 1000 is a hack in case there are any files out in venus caches */
            header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
            header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
            header.volumeInfo.needsCallback = 0;
-           gettimeofday(&tp, 0);
+           gettimeofday(&tp, NULL);
            header.volumeInfo.creationDate = tp.tv_sec;
-           if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
-               Abort
-                   ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
-                    sp->description, errno);
-           }
            nBytes =
-               FDH_WRITE(fdP, (char *)&header.volumeInfo,
-                         sizeof(header.volumeInfo));
+               FDH_PWRITE(fdP, (char *)&header.volumeInfo,
+                          sizeof(header.volumeInfo), 0);
            if (nBytes != sizeof(header.volumeInfo)) {
                if (nBytes < 0)
                    Abort
@@ -2434,12 +2619,7 @@ SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
                      sp->description);
            }
        } else {
-           if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
-               Abort
-                   ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
-                    sp->description, errno);
-           }
-           nBytes = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
+           nBytes = FDH_PWRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp), 0);
            if (nBytes != sizeof(sp->stamp)) {
                if (nBytes < 0)
                    Abort
@@ -2454,19 +2634,22 @@ SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
     FDH_REALLYCLOSE(fdP);
     IH_RELEASE(specH);
     if (sp->inodeType == VI_VOLINFO) {
-       VolInfo = header.volumeInfo;
+       salvinfo->VolInfo = header.volumeInfo;
        if (check) {
-           char update[25];
-           if (VolInfo.updateDate) {
-               strcpy(update, TimeStamp(VolInfo.updateDate, 0));
+           char update[64];
+           char buffer[64];
+
+           if (salvinfo->VolInfo.updateDate) {
+               strcpy(update, TimeStamp(buffer, sizeof(buffer), salvinfo->VolInfo.updateDate, 0));
                if (!Showmode)
-                   Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
+                   Log("%s (%" AFS_VOLID_FMT ") %supdated %s\n", salvinfo->VolInfo.name,
+                       afs_printable_VolumeId_lu(salvinfo->VolInfo.id),
                        (Testing ? "it would have been " : ""), update);
            } else {
-               strcpy(update, TimeStamp(VolInfo.creationDate, 0));
+               strcpy(update, TimeStamp(buffer, sizeof(buffer), salvinfo->VolInfo.creationDate, 0));
                if (!Showmode)
-                   Log("%s (%u) not updated (created %s)\n", VolInfo.name,
-                       VolInfo.id, update);
+                   Log("%s (%" AFS_VOLID_FMT ") not updated (created %s)\n",
+                       salvinfo->VolInfo.name, afs_printable_VolumeId_lu(salvinfo->VolInfo.id), update);
            }
 
        }
@@ -2476,9 +2659,10 @@ SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
 }
 
 int
-SalvageVnodes(register struct InodeSummary *rwIsp,
-             register struct InodeSummary *thisIsp,
-             register struct ViceInodeInfo *inodes, int check)
+SalvageVnodes(struct SalvInfo *salvinfo,
+              struct InodeSummary *rwIsp,
+             struct InodeSummary *thisIsp,
+             struct ViceInodeInfo *inodes, int check)
 {
     int ilarge, ismall, ioffset, RW, nInodes;
     ioffset = rwIsp->index + rwIsp->nSpecialInodes;    /* first inode */
@@ -2487,22 +2671,21 @@ SalvageVnodes(register struct InodeSummary *rwIsp,
     RW = (rwIsp == thisIsp);
     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
     ismall =
-       SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
+       SalvageIndex(salvinfo, thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
                     &inodes[ioffset], nInodes, thisIsp->volSummary, check);
     if (check && ismall == -1)
        return -1;
     ilarge =
-       SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
+       SalvageIndex(salvinfo, thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
                     &inodes[ioffset], nInodes, thisIsp->volSummary, check);
     return (ilarge == 0 && ismall == 0 ? 0 : -1);
 }
 
 int
-SalvageIndex(Inode ino, VnodeClass class, int RW,
-            register struct ViceInodeInfo *ip, int nInodes,
-            struct VolumeSummary *volSummary, int check)
+SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
+            struct ViceInodeInfo *ip, int nInodes,
+             struct VolumeSummary *volSummary, int check)
 {
-    VolumeId volumeNumber;
     char buf[SIZEOF_LARGEDISKVNODE];
     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
     int err = 0;
@@ -2516,19 +2699,18 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
     IHandle_t *handle;
     FdHandle_t *fdP;
 
-    volumeNumber = volSummary->header.id;
-    IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
+    IH_INIT(handle, salvinfo->fileSysDevice, volSummary->header.parent, ino);
     fdP = IH_OPEN(handle);
-    assert(fdP != NULL);
+    opr_Assert(fdP != NULL);
     file = FDH_FDOPEN(fdP, "r+");
-    assert(file != NULL);
+    opr_Assert(file != NULL);
     vcp = &VnodeClassInfo[class];
     size = OS_SIZE(fdP->fd_fd);
-    assert(size != -1);
+    opr_Assert(size != -1);
     nVnodes = (size / vcp->diskSize) - 1;
     if (nVnodes > 0) {
-       assert((nVnodes + 1) * vcp->diskSize == size);
-       assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
+       opr_Assert((nVnodes + 1) * vcp->diskSize == size);
+       opr_Verify(STREAM_ASEEK(file, vcp->diskSize) == 0);
     } else {
        nVnodes = 0;
     }
@@ -2538,12 +2720,6 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
        if (vnode->type != vNull) {
            int vnodeChanged = 0;
            int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
-           /* Log programs that belong to root (potentially suid root);
-            * don't bother for read-only or backup volumes */
-#ifdef notdef                  /* This is done elsewhere */
-           if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
-               Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n", VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author, vnode->owner, vnode->modeBits);
-#endif
            if (VNDISK_GET_INO(vnode) == 0) {
                if (RW) {
                    /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
@@ -2553,6 +2729,14 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
            } else {
                if (vcp->magic != vnode->vnodeMagic) {
                    /* bad magic #, probably partially created vnode */
+                   if (check) {
+                      Log("Partially allocated vnode %d: bad magic (is %lx should be %lx)\n",
+                          vnodeNumber, afs_printable_uint32_lu(vnode->vnodeMagic),
+                          afs_printable_uint32_lu(vcp->magic));
+                      memset(vnode, 0, vcp->diskSize);
+                      err = -1;
+                      goto zooks;
+                   }
                    Log("Partially allocated vnode %d deleted.\n",
                        vnodeNumber);
                    memset(vnode, 0, vcp->diskSize);
@@ -2586,8 +2770,8 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
                    /* For RW volume, look for vnode with matching inode number;
                     * if no such match, take the first determined by our sort
                     * order */
-                   register struct ViceInodeInfo *lip = ip;
-                   register int lnInodes = nInodes;
+                   struct ViceInodeInfo *lip = ip;
+                   int lnInodes = nInodes;
                    while (lnInodes
                           && lip->u.vnode.vnodeNumber == vnodeNumber) {
                        if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
@@ -2611,7 +2795,7 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
                        /*
                         * Because of the possibility of the uniquifier overflows (> 4M)
                         * we compare them modulo the low 22-bits; we shouldn't worry
-                        * about mismatching since they shouldn't to many old 
+                        * about mismatching since they shouldn't to many old
                         * uniquifiers of the same vnode...
                         */
                        if (IUnique(vu) != IUnique(iu)) {
@@ -2700,13 +2884,14 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
                    ip++;
                    nInodes--;
                } else {        /* no matching inode */
+                   afs_ino_str_t stmp;
                    if (VNDISK_GET_INO(vnode) != 0
                        || vnode->type == vDirectory) {
                        /* No matching inode--get rid of the vnode */
                        if (check) {
                            if (VNDISK_GET_INO(vnode)) {
                                if (!Showmode) {
-                                   Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
+                                   Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)));
                                }
                            } else {
                                if (!Showmode)
@@ -2718,7 +2903,7 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
                        if (VNDISK_GET_INO(vnode)) {
                            if (!Showmode) {
                                time_t serverModifyTime = vnode->serverModifyTime;
-                               Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)), ctime(&serverModifyTime));
+                               Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)), ctime(&serverModifyTime));
                            }
                        } else {
                            if (!Showmode) {
@@ -2729,7 +2914,7 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
                        memset(vnode, 0, vcp->diskSize);
                        vnodeChanged = 1;
                    } else {
-                       /* Should not reach here becuase we checked for 
+                       /* Should not reach here becuase we checked for
                         * (inodeNumber == 0) above. And where we zero the vnode,
                         * we also goto vnodeDone.
                         */
@@ -2741,13 +2926,13 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
                }
            }                   /* VNDISK_GET_INO(vnode) != 0 */
          vnodeDone:
-           assert(!(vnodeChanged && check));
+           opr_Assert(!(vnodeChanged && check));
            if (vnodeChanged && !Testing) {
-               assert(IH_IWRITE
-                      (handle, vnodeIndexOffset(vcp, vnodeNumber),
-                       (char *)vnode, vcp->diskSize)
-                      == vcp->diskSize);
-               VolumeChanged = 1;      /* For break call back */
+               opr_Verify(IH_IWRITE(handle,
+                                    vnodeIndexOffset(vcp, vnodeNumber),
+                                    (char *)vnode, vcp->diskSize)
+                               == vcp->diskSize);
+               salvinfo->VolumeChanged = 1;    /* For break call back */
            }
        }
     }
@@ -2759,20 +2944,20 @@ SalvageIndex(Inode ino, VnodeClass class, int RW,
 }
 
 struct VnodeEssence *
-CheckVnodeNumber(VnodeId vnodeNumber)
+CheckVnodeNumber(struct SalvInfo *salvinfo, VnodeId vnodeNumber)
 {
     VnodeClass class;
     struct VnodeInfo *vip;
     int offset;
 
     class = vnodeIdToClass(vnodeNumber);
-    vip = &vnodeInfo[class];
+    vip = &salvinfo->vnodeInfo[class];
     offset = vnodeIdToBitNumber(vnodeNumber);
     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
 }
 
 void
-CopyOnWrite(register struct DirSummary *dir)
+CopyOnWrite(struct SalvInfo *salvinfo, struct DirSummary *dir)
 {
     /* Copy the directory unconditionally if we are going to change it:
      * not just if was cloned.
@@ -2787,31 +2972,33 @@ CopyOnWrite(register struct DirSummary *dir)
     DFlush();                  /* Well justified paranoia... */
 
     code =
-       IH_IREAD(vnodeInfo[vLarge].handle,
+       IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
                 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                 sizeof(vnode));
-    assert(code == sizeof(vnode));
+    opr_Assert(code == sizeof(vnode));
     oldinode = VNDISK_GET_INO(&vnode);
     /* Increment the version number by a whole lot to avoid problems with
      * clients that were promised new version numbers--but the file server
      * crashed before the versions were written to disk.
      */
     newinode =
-       IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
+       IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
                  dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
                  200);
-    assert(VALID_INO(newinode));
-    assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
+    opr_Assert(VALID_INO(newinode));
+    opr_Verify(CopyInode(salvinfo->fileSysDevice, oldinode, newinode,
+                        dir->rwVid) == 0);
     vnode.cloned = 0;
     VNDISK_SET_INO(&vnode, newinode);
     code =
-       IH_IWRITE(vnodeInfo[vLarge].handle,
+       IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                  sizeof(vnode));
-    assert(code == sizeof(vnode));
+    opr_Assert(code == sizeof(vnode));
 
     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
-                       fileSysDevice, newinode);
+                       salvinfo->fileSysDevice, newinode,
+                        &salvinfo->VolumeChanged);
     /* Don't delete the original inode right away, because the directory is
      * still being scanned.
      */
@@ -2819,13 +3006,13 @@ CopyOnWrite(register struct DirSummary *dir)
 }
 
 /*
- * This function should either successfully create a new dir, or give up 
- * and leave things the way they were.  In particular, if it fails to write 
- * the new dir properly, it should return w/o changing the reference to the 
+ * This function should either successfully create a new dir, or give up
+ * and leave things the way they were.  In particular, if it fails to write
+ * the new dir properly, it should return w/o changing the reference to the
  * old dir.
  */
 void
-CopyAndSalvage(register struct DirSummary *dir)
+CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
 {
     struct VnodeDiskObject vnode;
     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
@@ -2842,28 +3029,29 @@ CopyAndSalvage(register struct DirSummary *dir)
        return;
     Log("Salvaging directory %u...\n", dir->vnodeNumber);
     lcode =
-       IH_IREAD(vnodeInfo[vLarge].handle,
+       IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
                 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                 sizeof(vnode));
-    assert(lcode == sizeof(vnode));
+    opr_Assert(lcode == sizeof(vnode));
     oldinode = VNDISK_GET_INO(&vnode);
     /* Increment the version number by a whole lot to avoid problems with
      * clients that were promised new version numbers--but the file server
      * crashed before the versions were written to disk.
      */
     newinode =
-       IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
+       IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
                  dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
                  200);
-    assert(VALID_INO(newinode));
-    SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
+    opr_Assert(VALID_INO(newinode));
+    SetSalvageDirHandle(&newdir, dir->rwVid, salvinfo->fileSysDevice, newinode,
+                        &salvinfo->VolumeChanged);
 
-    /* Assign . and .. vnode numbers from dir and vnode.parent. 
+    /* Assign . and .. vnode numbers from dir and vnode.parent.
      * The uniquifier for . is in the vnode.
-     * The uniquifier for .. might be set to a bogus value of 1 and 
+     * The uniquifier for .. might be set to a bogus value of 1 and
      * the salvager will later clean it up.
      */
-    if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
+    if (vnode.parent && (vnodeEssence = CheckVnodeNumber(salvinfo, vnode.parent))) {
        parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
     }
     code =
@@ -2880,76 +3068,77 @@ CopyAndSalvage(register struct DirSummary *dir)
        if (code) {
            Log("also failed to decrement link count on new inode");
        }
-       assert(1 == 2);
+       opr_Assert(0);
     }
     Log("Checking the results of the directory salvage...\n");
     if (!DirOK(&newdir)) {
        Log("Directory salvage failed!!!; restoring old version of the directory.\n");
        code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
-       assert(code == 0);
-       assert(1 == 2);
+       opr_Assert(code == 0);
+       opr_Assert(0);
     }
     vnode.cloned = 0;
     VNDISK_SET_INO(&vnode, newinode);
-    length = Length(&newdir);
+    length = afs_dir_Length(&newdir);
     VNDISK_SET_LEN(&vnode, length);
     lcode =
-       IH_IWRITE(vnodeInfo[vLarge].handle,
+       IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                  sizeof(vnode));
-    assert(lcode == sizeof(vnode));
-#if 0
-#ifdef AFS_NT40_ENV
-    nt_sync(fileSysDevice);
-#else
-    sync();                    /* this is slow, but hopefully rarely called.  We don't have
-                                * an open FD on the file itself to fsync.
-                                */
-#endif
-#else
-    vnodeInfo[vLarge].handle->ih_synced = 1;
-#endif
+    opr_Assert(lcode == sizeof(vnode));
+    IH_CONDSYNC(salvinfo->vnodeInfo[vLarge].handle);
+
     /* make sure old directory file is really closed */
     fdP = IH_OPEN(dir->dirHandle.dirh_handle);
     FDH_REALLYCLOSE(fdP);
-    
+
     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
-    assert(code == 0);
+    opr_Assert(code == 0);
     dir->dirHandle = newdir;
 }
 
+/**
+ * arguments for JudgeEntry.
+ */
+struct judgeEntry_params {
+    struct DirSummary *dir;    /**< directory we're examining entries in */
+    struct SalvInfo *salvinfo; /**< SalvInfo for the current salvage job */
+};
+
 int
-JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
+JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
           afs_int32 unique)
 {
-    struct DirSummary *dir = (struct DirSummary *)dirVal;
+    struct judgeEntry_params *params = arock;
+    struct DirSummary *dir = params->dir;
+    struct SalvInfo *salvinfo = params->salvinfo;
     struct VnodeEssence *vnodeEssence;
     afs_int32 dirOrphaned, todelete;
 
-    dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
+    dirOrphaned = IsVnodeOrphaned(salvinfo, dir->vnodeNumber);
 
-    vnodeEssence = CheckVnodeNumber(vnodeNumber);
+    vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
     if (vnodeEssence == NULL) {
        if (!Showmode) {
-           Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
+           Log("dir vnode %u: invalid entry deleted: %s" OS_DIRSEP "%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
        }
        if (!Testing) {
-           CopyOnWrite(dir);
-           assert(Delete(&dir->dirHandle, name) == 0);
+           CopyOnWrite(salvinfo, dir);
+           opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
        }
        return 0;
     }
 #ifdef AFS_AIX_ENV
 #ifndef AFS_NAMEI_ENV
-    /* On AIX machines, don't allow entries to point to inode 0. That is a special 
+    /* On AIX machines, don't allow entries to point to inode 0. That is a special
      * mount inode for the partition. If this inode were deleted, it would crash
      * the machine.
      */
     if (vnodeEssence->InodeNumber == 0) {
-       Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
+       Log("dir vnode %d: invalid entry: %s" OS_DIRSEP "%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
        if (!Testing) {
-           CopyOnWrite(dir);
-           assert(Delete(&dir->dirHandle, name) == 0);
+           CopyOnWrite(salvinfo, dir);
+           opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
        }
        return 0;
     }
@@ -2959,15 +3148,15 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
     if (!(vnodeNumber & 1) && !Showmode
        && !(vnodeEssence->count || vnodeEssence->unique
             || vnodeEssence->modeBits)) {
-       Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
+       Log("dir vnode %u: invalid entry: %s" OS_DIRSEP "%s (vnode %u, unique %u)%s\n",
            dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
            vnodeNumber, unique,
            ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
             ""));
        if (!unique) {
            if (!Testing) {
-               CopyOnWrite(dir);
-               assert(Delete(&dir->dirHandle, name) == 0);
+               CopyOnWrite(salvinfo, dir);
+               opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
            }
            return 0;
        }
@@ -2978,49 +3167,59 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
      * or if the directory is orphaned.
      */
     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
-       if (!vnodeEssence->unique
-           && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
-           /* This is an orphaned directory. Don't delete the . or ..
-            * entry. Otherwise, it will get created in the next 
-            * salvage and deleted again here. So Just skip it.
-            */
-           return 0;
-       }
-
        todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
 
-       if (!Showmode) {
-           Log("dir vnode %u: %s/%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
-       }
-       if (!Testing) {
-           AFSFid fid;
-           fid.Vnode = vnodeNumber;
-           fid.Unique = vnodeEssence->unique;
-           CopyOnWrite(dir);
-           assert(Delete(&dir->dirHandle, name) == 0);
-           if (!todelete)
-               assert(Create(&dir->dirHandle, name, &fid) == 0);
+       if (todelete
+           && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
+               if (dirOrphaned) {
+                   /* This is an orphaned directory. Don't delete the . or ..
+                    * entry. Otherwise, it will get created in the next
+                    * salvage and deleted again here. So Just skip it.
+                    * */
+                   return 0;
+               }
+               /* (vnodeEssence->unique == 0 && ('.' || '..'));
+                * Entries arriving here should be deleted, but the directory
+                * is not orphaned. Therefore, the entry must be pointing at
+                * the wrong vnode.  Skip the 'else' clause and fall through;
+                * the code below will repair the entry so it correctly points
+                * at the vnode of the current directory (if '.') or the parent
+                * directory (if '..'). */
+       } else {
+           if (!Showmode) {
+               Log("dir vnode %u: %s" OS_DIRSEP "%s (vnode %u): unique changed from %u to %u %s\n",
+                   dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique,
+                   vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
+           }
+           if (!Testing) {
+               AFSFid fid;
+               fid.Vnode = vnodeNumber;
+               fid.Unique = vnodeEssence->unique;
+               CopyOnWrite(salvinfo, dir);
+               opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
+               if (!todelete)
+                   opr_Verify(afs_dir_Create(&dir->dirHandle, name, &fid) == 0);
+           }
+           if (todelete)
+               return 0;               /* no need to continue */
        }
-       if (todelete)
-           return 0;           /* no need to continue */
     }
 
     if (strcmp(name, ".") == 0) {
        if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
-           AFSFid fid;
            if (!Showmode)
                Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
            if (!Testing) {
-               CopyOnWrite(dir);
-               assert(Delete(&dir->dirHandle, ".") == 0);
+               AFSFid fid;
+               CopyOnWrite(salvinfo, dir);
+               opr_Verify(afs_dir_Delete(&dir->dirHandle, ".") == 0);
                fid.Vnode = dir->vnodeNumber;
                fid.Unique = dir->unique;
-               assert(Create(&dir->dirHandle, ".", &fid) == 0);
+               opr_Verify(afs_dir_Create(&dir->dirHandle, ".", &fid) == 0);
+               vnodeNumber = fid.Vnode;        /* Get the new Essence */
+               unique = fid.Unique;
+               vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
            }
-
-           vnodeNumber = fid.Vnode;    /* Get the new Essence */
-           unique = fid.Unique;
-           vnodeEssence = CheckVnodeNumber(vnodeNumber);
        }
        dir->haveDot = 1;
     } else if (strcmp(name, "..") == 0) {
@@ -3028,8 +3227,8 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
        if (dir->parent) {
            struct VnodeEssence *dotdot;
            pa.Vnode = dir->parent;
-           dotdot = CheckVnodeNumber(pa.Vnode);
-           assert(dotdot != NULL);     /* XXX Should not be assert */
+           dotdot = CheckVnodeNumber(salvinfo, pa.Vnode);
+           opr_Assert(dotdot != NULL); /* XXX Should not be assert */
            pa.Unique = dotdot->unique;
        } else {
            pa.Vnode = dir->vnodeNumber;
@@ -3039,14 +3238,14 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
            if (!Showmode)
                Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
            if (!Testing) {
-               CopyOnWrite(dir);
-               assert(Delete(&dir->dirHandle, "..") == 0);
-               assert(Create(&dir->dirHandle, "..", &pa) == 0);
+               CopyOnWrite(salvinfo, dir);
+               opr_Verify(afs_dir_Delete(&dir->dirHandle, "..") == 0);
+               opr_Verify(afs_dir_Create(&dir->dirHandle, "..", &pa) == 0);
            }
 
            vnodeNumber = pa.Vnode;     /* Get the new Essence */
            unique = pa.Unique;
-           vnodeEssence = CheckVnodeNumber(vnodeNumber);
+           vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
        }
        dir->haveDotDot = 1;
     } else if (strncmp(name, ".__afs", 6) == 0) {
@@ -3054,24 +3253,24 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
            Log("dir vnode %u: special old unlink-while-referenced file %s %s deleted (vnode %u)\n", dir->vnodeNumber, name, (Testing ? "would have been" : "is"), vnodeNumber);
        }
        if (!Testing) {
-           CopyOnWrite(dir);
-           assert(Delete(&dir->dirHandle, name) == 0);
+           CopyOnWrite(salvinfo, dir);
+           opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
        }
        vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
        vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
        return 0;
     } else {
        if (ShowSuid && (vnodeEssence->modeBits & 06000))
-           Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
+           Log("FOUND suid/sgid file: %s" OS_DIRSEP "%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
        if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
            && !(vnodeEssence->modeBits & 0111)) {
-           ssize_t nBytes;
+           afs_sfsize_t nBytes;
            afs_sfsize_t size;
            char buf[1025];
            IHandle_t *ihP;
            FdHandle_t *fdP;
 
-           IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
+           IH_INIT(ihP, salvinfo->fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
                    vnodeEssence->InodeNumber);
            fdP = IH_OPEN(ihP);
            if (fdP == NULL) {
@@ -3086,21 +3285,22 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
                IH_RELEASE(ihP);
                return 0;
            }
-       
+
            if (size > 1024)
                size = 1024;
-           nBytes = FDH_READ(fdP, buf, size);
+           nBytes = FDH_PREAD(fdP, buf, size, 0);
            if (nBytes == size) {
                buf[size] = '\0';
                if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
-                   Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
-                       dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
+                   Log("Volume %" AFS_VOLID_FMT " (%s) mount point %s" OS_DIRSEP "%s to '%s' invalid, %s to symbolic link\n",
+                       afs_printable_VolumeId_lu(dir->dirHandle.dirh_handle->ih_vid), dir->vname, dir->name ? dir->name : "??", name, buf,
                        Testing ? "would convert" : "converted");
                    vnodeEssence->modeBits |= 0111;
                    vnodeEssence->changed = 1;
-               } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
-                   dir->dirHandle.dirh_handle->ih_vid, dir->vname,
-                   dir->name ? dir->name : "??", name, buf);
+               } else if (ShowMounts) 
+                   Log("In volume %" AFS_VOLID_FMT " (%s) found mountpoint %s" OS_DIRSEP "%s to '%s'\n",
+                       afs_printable_VolumeId_lu(dir->dirHandle.dirh_handle->ih_vid),
+                       dir->vname, dir->name ? dir->name : "??", name, buf);
            } else {
                Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
                    dir->vname, vnodeNumber, (int)size, (int)nBytes);
@@ -3109,18 +3309,15 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
            IH_RELEASE(ihP);
        }
        if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
-           Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
+           Log("FOUND root file: %s" OS_DIRSEP "%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
        if (vnodeIdToClass(vnodeNumber) == vLarge
            && vnodeEssence->name == NULL) {
-           char *n;
-           if ((n = (char *)malloc(strlen(name) + 1)))
-               strcpy(n, name);
-           vnodeEssence->name = n;
+           vnodeEssence->name = strdup(name);
        }
 
        /* The directory entry points to the vnode. Check to see if the
-        * vnode points back to the directory. If not, then let the 
-        * directory claim it (else it might end up orphaned). Vnodes 
+        * vnode points back to the directory. If not, then let the
+        * directory claim it (else it might end up orphaned). Vnodes
         * already claimed by another directory are deleted from this
         * directory: hardlinks to the same vnode are not allowed
         * from different directories.
@@ -3132,7 +3329,7 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
                 * another non-orphaned dir).
                 */
                if (!Showmode) {
-                   Log("dir vnode %u: %s/%s (vnode %u, unique %u) -- parent vnode %schanged from %u to %u\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""), vnodeEssence->parent, dir->vnodeNumber);
+                   Log("dir vnode %u: %s" OS_DIRSEP "%s (vnode %u, unique %u) -- parent vnode %schanged from %u to %u\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""), vnodeEssence->parent, dir->vnodeNumber);
                }
                vnodeEssence->parent = dir->vnodeNumber;
                vnodeEssence->changed = 1;
@@ -3140,16 +3337,16 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
                /* Vnode was claimed by another directory */
                if (!Showmode) {
                    if (dirOrphaned) {
-                       Log("dir vnode %u: %s/%s parent vnode is %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
+                       Log("dir vnode %u: %s" OS_DIRSEP "%s parent vnode is %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
                    } else if (vnodeNumber == 1) {
-                       Log("dir vnode %d: %s/%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""));
+                       Log("dir vnode %d: %s" OS_DIRSEP "%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""));
                    } else {
-                       Log("dir vnode %u: %s/%s already claimed by directory vnode %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
+                       Log("dir vnode %u: %s" OS_DIRSEP "%s already claimed by directory vnode %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
                    }
                }
                if (!Testing) {
-                   CopyOnWrite(dir);
-                   assert(Delete(&dir->dirHandle, name) == 0);
+                   CopyOnWrite(salvinfo, dir);
+                   opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
                }
                return 0;
            }
@@ -3162,9 +3359,10 @@ JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
 }
 
 void
-DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
+DistilVnodeEssence(struct SalvInfo *salvinfo, VolumeId rwVId,
+                   VnodeClass class, Inode ino, Unique * maxu)
 {
-    register struct VnodeInfo *vip = &vnodeInfo[class];
+    struct VnodeInfo *vip = &salvinfo->vnodeInfo[class];
     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
     char buf[SIZEOF_LARGEDISKVNODE];
     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
@@ -3174,22 +3372,23 @@ DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
     int nVnodes;
     FdHandle_t *fdP;
 
-    IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
+    IH_INIT(vip->handle, salvinfo->fileSysDevice, rwVId, ino);
     fdP = IH_OPEN(vip->handle);
-    assert(fdP != NULL);
+    opr_Assert(fdP != NULL);
     file = FDH_FDOPEN(fdP, "r+");
-    assert(file != NULL);
+    opr_Assert(file != NULL);
     size = OS_SIZE(fdP->fd_fd);
-    assert(size != -1);
+    opr_Assert(size != -1);
     vip->nVnodes = (size / vcp->diskSize) - 1;
     if (vip->nVnodes > 0) {
-       assert((vip->nVnodes + 1) * vcp->diskSize == size);
-       assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
-       assert((vip->vnodes = (struct VnodeEssence *)
-               calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
+       opr_Assert((vip->nVnodes + 1) * vcp->diskSize == size);
+       opr_Verify(STREAM_ASEEK(file, vcp->diskSize) == 0);
+       opr_Verify((vip->vnodes = calloc(vip->nVnodes,
+                                        sizeof(struct VnodeEssence)))
+                       != NULL);
        if (class == vLarge) {
-           assert((vip->inodes = (Inode *)
-                   calloc(vip->nVnodes, sizeof(Inode))) != NULL);
+           opr_Verify((vip->inodes = calloc(vip->nVnodes, sizeof(Inode)))
+                           != NULL);
        } else {
            vip->inodes = NULL;
        }
@@ -3203,7 +3402,7 @@ DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
         nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
         nVnodes--, vnodeIndex++) {
        if (vnode->type != vNull) {
-           register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
+           struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
            afs_fsize_t vnodeLength;
            vip->nAllocatedVnodes++;
            vep->count = vnode->linkCount;
@@ -3224,11 +3423,11 @@ DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
                if (class != vLarge) {
                    VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
                    vip->nAllocatedVnodes--;
-                   memset(vnode, 0, sizeof(vnode));
-                   IH_IWRITE(vnodeInfo[vSmall].handle,
+                   memset(vnode, 0, sizeof(*vnode));
+                   IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
                              vnodeIndexOffset(vcp, vnodeNumber),
                              (char *)&vnode, sizeof(vnode));
-                   VolumeChanged = 1;
+                   salvinfo->VolumeChanged = 1;
                } else
                    vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
            }
@@ -3239,7 +3438,8 @@ DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
 }
 
 static char *
-GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
+GetDirName(struct SalvInfo *salvinfo, VnodeId vnode, struct VnodeEssence *vp,
+           char *path)
 {
     struct VnodeEssence *parentvp;
 
@@ -3247,9 +3447,9 @@ GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
        strcpy(path, ".");
        return path;
     }
-    if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
-       && GetDirName(vp->parent, parentvp, path)) {
-       strcat(path, "/");
+    if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(salvinfo, vp->parent))
+       && GetDirName(salvinfo, vp->parent, parentvp, path)) {
+       strcat(path, OS_DIRSEP);
        strcat(path, vp->name);
        return path;
     }
@@ -3260,7 +3460,7 @@ GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
  */
 static int
-IsVnodeOrphaned(VnodeId vnode)
+IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode)
 {
     struct VnodeEssence *vep;
 
@@ -3268,17 +3468,17 @@ IsVnodeOrphaned(VnodeId vnode)
        return (1);             /* Vnode zero does not exist */
     if (vnode == 1)
        return (0);             /* The root dir vnode is always claimed */
-    vep = CheckVnodeNumber(vnode);     /* Get the vnode essence */
+    vep = CheckVnodeNumber(salvinfo, vnode);   /* Get the vnode essence */
     if (!vep || !vep->claimed)
        return (1);             /* Vnode is not claimed - it is orphaned */
 
-    return (IsVnodeOrphaned(vep->parent));
+    return (IsVnodeOrphaned(salvinfo, vep->parent));
 }
 
 void
-SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
-          IHandle_t * alinkH, int i, struct DirSummary *rootdir,
-          int *rootdirfound)
+SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
+          struct VnodeInfo *dirVnodeInfo, IHandle_t * alinkH, int i,
+          struct DirSummary *rootdir, int *rootdirfound)
 {
     static struct DirSummary dir;
     static struct DirHandle dirHandle;
@@ -3303,9 +3503,9 @@ SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
            dirVnodeInfo->vnodes[i].changed = 1;
        }
     } else {
-       parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
+       parent = CheckVnodeNumber(salvinfo, dirVnodeInfo->vnodes[i].parent);
        if (parent && parent->salvaged == 0)
-           SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
+           SalvageDir(salvinfo, name, rwVid, dirVnodeInfo, alinkH,
                       vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
                       rootdir, rootdirfound);
     }
@@ -3317,8 +3517,8 @@ SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
     dir.parent = dirVnodeInfo->vnodes[i].parent;
     dir.haveDot = dir.haveDotDot = 0;
     dir.ds_linkH = alinkH;
-    SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
-                       dirVnodeInfo->inodes[i]);
+    SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, salvinfo->fileSysDevice,
+                       dirVnodeInfo->inodes[i], &salvinfo->VolumeChanged);
 
     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
     if (!dirok) {
@@ -3327,21 +3527,27 @@ SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
                (Testing ? "skipping" : "salvaging"));
        }
        if (!Testing) {
-           CopyAndSalvage(&dir);
+           CopyAndSalvage(salvinfo, &dir);
            dirok = 1;
+           dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
        }
     }
     dirHandle = dir.dirHandle;
 
     dir.name =
-       GetDirName(bitNumberToVnodeNumber(i, vLarge),
+       GetDirName(salvinfo, bitNumberToVnodeNumber(i, vLarge),
                   &dirVnodeInfo->vnodes[i], path);
 
     if (dirok) {
        /* If enumeration failed for random reasons, we will probably delete
         * too much stuff, so we guard against this instead.
         */
-       assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
+       struct judgeEntry_params judge_params;
+       judge_params.salvinfo = salvinfo;
+       judge_params.dir = &dir;
+
+       opr_Verify(afs_dir_EnumerateDir(&dirHandle, JudgeEntry,
+                                       &judge_params) == 0);
     }
 
     /* Delete the old directory if it was copied in order to salvage.
@@ -3352,7 +3558,7 @@ SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
     DFlush();
     if (dir.copied && !Testing) {
        code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
-       assert(code == 0);
+       opr_Assert(code == 0);
        dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
     }
 
@@ -3376,21 +3582,23 @@ SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
  *                          vnode
  */
 static void
-GetNewFID(VolumeDiskData *volHeader, VnodeClass class, AFSFid *afid,
-          Unique *maxunique)
+GetNewFID(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
+          VnodeClass class, AFSFid *afid, Unique *maxunique)
 {
     int i;
-    for (i = 0; i < vnodeInfo[class].nVnodes; i++) {
-       if (vnodeInfo[class].vnodes[i].type == vNull) {
+    for (i = 0; i < salvinfo->vnodeInfo[class].nVnodes; i++) {
+       if (salvinfo->vnodeInfo[class].vnodes[i].type == vNull) {
            break;
        }
     }
-    if (i == vnodeInfo[class].nVnodes) {
+    if (i == salvinfo->vnodeInfo[class].nVnodes) {
        /* no free vnodes; make a new one */
-       vnodeInfo[class].nVnodes++;
-       vnodeInfo[class].vnodes = realloc(vnodeInfo[class].vnodes,
-                                         sizeof(struct VnodeEssence) * (i+1));
-       vnodeInfo[class].vnodes[i].type = vNull;
+       salvinfo->vnodeInfo[class].nVnodes++;
+       salvinfo->vnodeInfo[class].vnodes =
+           realloc(salvinfo->vnodeInfo[class].vnodes,
+                   sizeof(struct VnodeEssence) * (i+1));
+
+       salvinfo->vnodeInfo[class].vnodes[i].type = vNull;
     }
 
     afid->Vnode = bitNumberToVnodeNumber(i, class);
@@ -3422,8 +3630,9 @@ GetNewFID(VolumeDiskData *volHeader, VnodeClass class, AFSFid *afid,
  *  @retval -1 error
  */
 static int
-CreateReadme(VolumeDiskData *volHeader, IHandle_t *alinkH,
-             VolumeId vid, Unique *maxunique, AFSFid *afid, Inode *ainode)
+CreateReadme(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
+             IHandle_t *alinkH, VolumeId vid, Unique *maxunique, AFSFid *afid,
+             Inode *ainode)
 {
     Inode readmeinode;
     struct VnodeDiskObject *rvnode = NULL;
@@ -3452,19 +3661,20 @@ CreateReadme(VolumeDiskData *volHeader, IHandle_t *alinkH,
     /* -1 for the trailing NUL */
     length = sizeof(readme) - 1;
 
-    GetNewFID(volHeader, vSmall, afid, maxunique);
+    GetNewFID(salvinfo, volHeader, vSmall, afid, maxunique);
 
-    vep = &vnodeInfo[vSmall].vnodes[vnodeIdToBitNumber(afid->Vnode)];
+    vep = &salvinfo->vnodeInfo[vSmall].vnodes[vnodeIdToBitNumber(afid->Vnode)];
 
     /* create the inode and write the contents */
-    readmeinode = IH_CREATE(alinkH, fileSysDevice, fileSysPath, 0, vid,
+    readmeinode = IH_CREATE(alinkH, salvinfo->fileSysDevice,
+                            salvinfo->fileSysPath, 0, vid,
                             afid->Vnode, afid->Unique, 1);
     if (!VALID_INO(readmeinode)) {
        Log("CreateReadme: readme IH_CREATE failed\n");
        goto error;
     }
 
-    IH_INIT(readmeH, fileSysDevice, vid, readmeinode);
+    IH_INIT(readmeH, salvinfo->fileSysDevice, vid, readmeinode);
     bytes = IH_IWRITE(readmeH, 0, readme, length);
     IH_RELEASE(readmeH);
 
@@ -3496,7 +3706,7 @@ CreateReadme(VolumeDiskData *volHeader, IHandle_t *alinkH,
     rvnode->group = 0;
     rvnode->vnodeMagic = VnodeClassInfo[vSmall].magic;
 
-    bytes = IH_IWRITE(vnodeInfo[vSmall].handle,
+    bytes = IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
                       vnodeIndexOffset(&VnodeClassInfo[vSmall], afid->Vnode),
                       (char*)rvnode, SIZEOF_SMALLDISKVNODE);
 
@@ -3507,10 +3717,10 @@ CreateReadme(VolumeDiskData *volHeader, IHandle_t *alinkH,
     }
 
     /* update VnodeEssence for new readme vnode */
-    vnodeInfo[vSmall].nAllocatedVnodes++;
+    salvinfo->vnodeInfo[vSmall].nAllocatedVnodes++;
     vep->count = 0;
     vep->blockCount = nBlocks(length);
-    vnodeInfo[vSmall].volumeBlockCount += vep->blockCount;
+    salvinfo->vnodeInfo[vSmall].volumeBlockCount += vep->blockCount;
     vep->parent = rvnode->parent;
     vep->unique = rvnode->uniquifier;
     vep->modeBits = rvnode->modeBits;
@@ -3561,8 +3771,9 @@ CreateReadme(VolumeDiskData *volHeader, IHandle_t *alinkH,
  *  @retval -1 error
  */
 static int
-CreateRootDir(VolumeDiskData *volHeader, IHandle_t *alinkH, VolumeId vid,
-              struct DirSummary *rootdir, Unique *maxunique)
+CreateRootDir(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
+              IHandle_t *alinkH, VolumeId vid, struct DirSummary *rootdir,
+              Unique *maxunique)
 {
     FileVersion dv;
     int decroot = 0, decreadme = 0;
@@ -3574,33 +3785,34 @@ CreateRootDir(VolumeDiskData *volHeader, IHandle_t *alinkH, VolumeId vid,
     Inode *ip;
     afs_sfsize_t bytes;
     struct VnodeEssence *vep;
-    Inode readmeinode;
+    Inode readmeinode = 0;
     time_t now = time(NULL);
 
-    if (!vnodeInfo[vLarge].vnodes && !vnodeInfo[vSmall].vnodes) {
+    if (!salvinfo->vnodeInfo[vLarge].vnodes && !salvinfo->vnodeInfo[vSmall].vnodes) {
        Log("Not creating new root dir; volume appears to lack any vnodes\n");
        goto error;
     }
 
-    if (!vnodeInfo[vLarge].vnodes) {
+    if (!salvinfo->vnodeInfo[vLarge].vnodes) {
        /* We don't have any large vnodes in the volume; allocate room
         * for one so we can recreate the root dir */
-       vnodeInfo[vLarge].nVnodes = 1;
-       vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
-       vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
+       salvinfo->vnodeInfo[vLarge].nVnodes = 1;
+       salvinfo->vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
+       salvinfo->vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
 
-       assert(vnodeInfo[vLarge].vnodes);
-       assert(vnodeInfo[vLarge].inodes);
+       opr_Assert(salvinfo->vnodeInfo[vLarge].vnodes);
+       opr_Assert(salvinfo->vnodeInfo[vLarge].inodes);
     }
 
-    vep = &vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
-    ip = &vnodeInfo[vLarge].inodes[vnodeIdToBitNumber(1)];
+    vep = &salvinfo->vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
+    ip = &salvinfo->vnodeInfo[vLarge].inodes[vnodeIdToBitNumber(1)];
     if (vep->type != vNull) {
        Log("Not creating new root dir; existing vnode 1 is non-null\n");
        goto error;
     }
 
-    if (CreateReadme(volHeader, alinkH, vid, maxunique, &readmeid, &readmeinode)) {
+    if (CreateReadme(salvinfo, volHeader, alinkH, vid, maxunique, &readmeid,
+                     &readmeinode) != 0) {
        goto error;
     }
     decreadme = 1;
@@ -3609,28 +3821,30 @@ CreateRootDir(VolumeDiskData *volHeader, IHandle_t *alinkH, VolumeId vid,
      * with a cached DV */
     dv = 1 << 30;
 
-    rootinode = IH_CREATE(alinkH, fileSysDevice, fileSysPath, 0, vid, 1, 1, dv);
+    rootinode = IH_CREATE(alinkH, salvinfo->fileSysDevice, salvinfo->fileSysPath,
+                          0, vid, 1, 1, dv);
     if (!VALID_INO(rootinode)) {
        Log("CreateRootDir: IH_CREATE failed\n");
        goto error;
     }
     decroot = 1;
 
-    SetSalvageDirHandle(&rootdir->dirHandle, vid, fileSysDevice, rootinode);
+    SetSalvageDirHandle(&rootdir->dirHandle, vid, salvinfo->fileSysDevice,
+                        rootinode, &salvinfo->VolumeChanged);
     did.Volume = vid;
     did.Vnode = 1;
     did.Unique = 1;
-    if (MakeDir(&rootdir->dirHandle, (afs_int32*)&did, (afs_int32*)&did)) {
+    if (afs_dir_MakeDir(&rootdir->dirHandle, (afs_int32*)&did, (afs_int32*)&did)) {
        Log("CreateRootDir: MakeDir failed\n");
        goto error;
     }
-    if (Create(&rootdir->dirHandle, "README.ROOTDIR", &readmeid)) {
+    if (afs_dir_Create(&rootdir->dirHandle, "README.ROOTDIR", &readmeid)) {
        Log("CreateRootDir: Create failed\n");
        goto error;
     }
     DFlush();
-    length = Length(&rootdir->dirHandle);
-    DZap((void *)&rootdir->dirHandle);
+    length = afs_dir_Length(&rootdir->dirHandle);
+    DZap(&rootdir->dirHandle);
 
     /* create the new root dir vnode */
     rootvnode = calloc(1, SIZEOF_LARGEDISKVNODE);
@@ -3667,7 +3881,7 @@ CreateRootDir(VolumeDiskData *volHeader, IHandle_t *alinkH, VolumeId vid,
     rootvnode->vnodeMagic = VnodeClassInfo[vLarge].magic;
 
     /* write it out to disk */
-    bytes = IH_IWRITE(vnodeInfo[vLarge].handle,
+    bytes = IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
              vnodeIndexOffset(&VnodeClassInfo[vLarge], 1),
              (char*)rootvnode, SIZEOF_LARGEDISKVNODE);
 
@@ -3680,10 +3894,10 @@ CreateRootDir(VolumeDiskData *volHeader, IHandle_t *alinkH, VolumeId vid,
     }
 
     /* update VnodeEssence for the new root vnode */
-    vnodeInfo[vLarge].nAllocatedVnodes++;
+    salvinfo->vnodeInfo[vLarge].nAllocatedVnodes++;
     vep->count = 0;
     vep->blockCount = nBlocks(length);
-    vnodeInfo[vLarge].volumeBlockCount += vep->blockCount;
+    salvinfo->vnodeInfo[vLarge].volumeBlockCount += vep->blockCount;
     vep->parent = rootvnode->parent;
     vep->unique = rootvnode->uniquifier;
     vep->modeBits = rootvnode->modeBits;
@@ -3731,13 +3945,23 @@ CreateRootDir(VolumeDiskData *volHeader, IHandle_t *alinkH, VolumeId vid,
     return -1;
 }
 
+/**
+ * salvage a volume group.
+ *
+ * @param[in] salvinfo information for the curent salvage job
+ * @param[in] rwIsp    inode summary for rw volume
+ * @param[in] alinkH   link table inode handle
+ *
+ * @return operation status
+ *   @retval 0 success
+ */
 int
-SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
+SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t * alinkH)
 {
     /* This routine, for now, will only be called for read-write volumes */
     int i, j, code;
     int BlocksInVolume = 0, FilesInVolume = 0;
-    register VnodeClass class;
+    VnodeClass class;
     struct DirSummary rootdir, oldrootdir;
     struct VnodeInfo *dirVnodeInfo;
     struct VnodeDiskObject vnode;
@@ -3757,25 +3981,25 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
     int newrootdir = 0;
 
     vid = rwIsp->volSummary->header.id;
-    IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
+    IH_INIT(h, salvinfo->fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
-    assert(nBytes == sizeof(volHeader));
-    assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
-    assert(volHeader.destroyMe != DESTROY_ME);
+    opr_Assert(nBytes == sizeof(volHeader));
+    opr_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
+    opr_Assert(volHeader.destroyMe != DESTROY_ME);
     /* (should not have gotten this far with DESTROY_ME flag still set!) */
 
-    DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
-                      &maxunique);
-    DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
-                      &maxunique);
+    DistilVnodeEssence(salvinfo, vid, vLarge,
+                       rwIsp->volSummary->header.largeVnodeIndex, &maxunique);
+    DistilVnodeEssence(salvinfo, vid, vSmall,
+                       rwIsp->volSummary->header.smallVnodeIndex, &maxunique);
 
-    dirVnodeInfo = &vnodeInfo[vLarge];
+    dirVnodeInfo = &salvinfo->vnodeInfo[vLarge];
     for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
-       SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
-                  &rootdirfound);
+       SalvageDir(salvinfo, volHeader.name, vid, dirVnodeInfo, alinkH, i,
+                  &rootdir, &rootdirfound);
     }
 #ifdef AFS_NT40_ENV
-    nt_sync(fileSysDevice);
+    nt_sync(salvinfo->fileSysDevice);
 #else
     sync();                            /* This used to be done lower level, for every dir */
 #endif
@@ -3789,11 +4013,12 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
        Log("Cannot find root directory for volume %lu; attempting to create "
            "a new one\n", afs_printable_uint32_lu(vid));
 
-       code = CreateRootDir(&volHeader, alinkH, vid, &rootdir, &maxunique);
+       code = CreateRootDir(salvinfo, &volHeader, alinkH, vid, &rootdir,
+                            &maxunique);
        if (code == 0) {
            rootdirfound = 1;
            newrootdir = 1;
-           VolumeChanged = 1;
+           salvinfo->VolumeChanged = 1;
        }
     }
 
@@ -3802,8 +4027,8 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
      */
     oldrootdir = rootdir;
     for (class = 0; class < nVNODECLASSES; class++) {
-       for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
-           vep = &(vnodeInfo[class].vnodes[v]);
+       for (v = 0; v < salvinfo->vnodeInfo[class].nVnodes; v++) {
+           vep = &(salvinfo->vnodeInfo[class].vnodes[v]);
            ThisVnode = bitNumberToVnodeNumber(v, class);
            ThisUnique = vep->unique;
 
@@ -3818,7 +4043,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
             */
            if (class == vLarge) {      /* directory vnode */
                pv = vnodeIdToBitNumber(vep->parent);
-               if (vnodeInfo[vLarge].vnodes[pv].unique != 0) {
+               if (salvinfo->vnodeInfo[vLarge].vnodes[pv].unique != 0) {
                    if (vep->parent == 1 && newrootdir) {
                        /* this vnode's parent was the volume root, and
                         * we just created the volume root. So, the parent
@@ -3829,7 +4054,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
                         /* noop */
 
                    } else {
-                       vnodeInfo[vLarge].vnodes[pv].count++;
+                       salvinfo->vnodeInfo[vLarge].vnodes[pv].count++;
                    }
                }
            }
@@ -3844,7 +4069,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
                LFVnode = rootdir.vnodeNumber;  /* Lost+Found vnode number */
                LFUnique = rootdir.unique;      /* Lost+Found uniquifier */
 
-               /* Update this orphaned vnode's info. Its parent info and 
+               /* Update this orphaned vnode's info. Its parent info and
                 * link count (do for orphaned directories and files).
                 */
                vep->parent = LFVnode;  /* Parent is the root dir */
@@ -3853,7 +4078,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
                vep->claimed = 1;
                vep->count--;   /* Inc link count (root dir will pt to it) */
 
-               /* If this orphaned vnode is a directory, change '..'. 
+               /* If this orphaned vnode is a directory, change '..'.
                 * The name of the orphaned dir/file is unknown, so we
                 * build a unique name. No need to CopyOnWrite the directory
                 * since it is not connected to tree in BK or RO volume and
@@ -3864,18 +4089,19 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
                    DirHandle dh;
 
                    /* Remove and recreate the ".." entry in this orphaned directory */
-                   SetSalvageDirHandle(&dh, vid, fileSysDevice,
-                                       vnodeInfo[class].inodes[v]);
+                   SetSalvageDirHandle(&dh, vid, salvinfo->fileSysDevice,
+                                       salvinfo->vnodeInfo[class].inodes[v],
+                                       &salvinfo->VolumeChanged);
                    pa.Vnode = LFVnode;
                    pa.Unique = LFUnique;
-                   assert(Delete(&dh, "..") == 0);
-                   assert(Create(&dh, "..", &pa) == 0);
+                   opr_Verify(afs_dir_Delete(&dh, "..") == 0);
+                   opr_Verify(afs_dir_Create(&dh, "..", &pa) == 0);
 
                    /* The original parent's link count was decremented above.
                     * Here we increment the new parent's link count.
                     */
                    pv = vnodeIdToBitNumber(LFVnode);
-                   vnodeInfo[vLarge].vnodes[pv].count--;
+                   salvinfo->vnodeInfo[vLarge].vnodes[pv].count--;
 
                }
 
@@ -3886,20 +4112,19 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
                    pa.Vnode = ThisVnode;
                    pa.Unique = ThisUnique;
 
-                   (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
-                                      ((class ==
-                                        vLarge) ? "__ORPHANDIR__" :
-                                       "__ORPHANFILE__"), ThisVnode,
-                                      ThisUnique);
+                   snprintf(npath, sizeof npath, "%s.%u.%u",
+                            ((class == vLarge) ? "__ORPHANDIR__"
+                                               : "__ORPHANFILE__"),
+                            ThisVnode, ThisUnique);
 
-                   CopyOnWrite(&rootdir);
-                   code = Create(&rootdir.dirHandle, npath, &pa);
+                   CopyOnWrite(salvinfo, &rootdir);
+                   code = afs_dir_Create(&rootdir.dirHandle, npath, &pa);
                    if (!code)
                        break;
 
                    ThisUnique += 50;   /* Try creating a different file */
                }
-               assert(code == 0);
+               opr_Assert(code == 0);
                Log("Attaching orphaned %s to volume's root dir as %s\n",
                    ((class == vLarge) ? "directory" : "file"), npath);
            }
@@ -3912,7 +4137,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
        code =
            IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
                   oldrootdir.rwVid);
-       assert(code == 0);
+       opr_Assert(code == 0);
        /* dirVnodeInfo->inodes[?] is not updated with new inode number */
     }
 
@@ -3926,20 +4151,20 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
      * will get removed here also (if requested).
      */
     for (class = 0; class < nVNODECLASSES; class++) {
-       int nVnodes = vnodeInfo[class].nVnodes;
+       afs_sfsize_t nVnodes = salvinfo->vnodeInfo[class].nVnodes;
        struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
-       struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
-       FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
-       BlocksInVolume += vnodeInfo[class].volumeBlockCount;
+       struct VnodeEssence *vnodes = salvinfo->vnodeInfo[class].vnodes;
+       FilesInVolume += salvinfo->vnodeInfo[class].nAllocatedVnodes;
+       BlocksInVolume += salvinfo->vnodeInfo[class].volumeBlockCount;
        for (i = 0; i < nVnodes; i++) {
-           register struct VnodeEssence *vnp = &vnodes[i];
+           struct VnodeEssence *vnp = &vnodes[i];
            VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
 
            /* If the vnode is good but is unclaimed (not listed in
             * any directory entries), then it is orphaned.
             */
            orphaned = -1;
-           if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
+           if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(salvinfo, vnodeNumber))) {
                vnp->claimed = 0;       /* Makes IsVnodeOrphaned calls faster */
                vnp->changed = 1;
            }
@@ -3947,21 +4172,21 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
            if (vnp->changed || vnp->count) {
                int oldCount;
                nBytes =
-                   IH_IREAD(vnodeInfo[class].handle,
+                   IH_IREAD(salvinfo->vnodeInfo[class].handle,
                             vnodeIndexOffset(vcp, vnodeNumber),
                             (char *)&vnode, sizeof(vnode));
-               assert(nBytes == sizeof(vnode));
+               opr_Assert(nBytes == sizeof(vnode));
 
                vnode.parent = vnp->parent;
                oldCount = vnode.linkCount;
                vnode.linkCount = vnode.linkCount - vnp->count;
 
                if (orphaned == -1)
-                   orphaned = IsVnodeOrphaned(vnodeNumber);
+                   orphaned = IsVnodeOrphaned(salvinfo, vnodeNumber);
                if (orphaned) {
                    if (!vnp->todelete) {
                        /* Orphans should have already been attached (if requested) */
-                       assert(orphans != ORPH_ATTACH);
+                       opr_Assert(orphans != ORPH_ATTACH);
                        oblocks += vnp->blockCount;
                        ofiles++;
                    }
@@ -3972,7 +4197,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
                        if (VNDISK_GET_INO(&vnode)) {
                            code =
                                IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
-                           assert(code == 0);
+                           opr_Assert(code == 0);
                        }
                        memset(&vnode, 0, sizeof(vnode));
                    }
@@ -3987,12 +4212,12 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
                vnode.dataVersion++;
                if (!Testing) {
                    nBytes =
-                       IH_IWRITE(vnodeInfo[class].handle,
+                       IH_IWRITE(salvinfo->vnodeInfo[class].handle,
                                  vnodeIndexOffset(vcp, vnodeNumber),
                                  (char *)&vnode, sizeof(vnode));
-                   assert(nBytes == sizeof(vnode));
+                   opr_Assert(nBytes == sizeof(vnode));
                }
-               VolumeChanged = 1;
+               salvinfo->VolumeChanged = 1;
            }
        }
     }
@@ -4004,7 +4229,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
     }
 
     for (class = 0; class < nVNODECLASSES; class++) {
-       register struct VnodeInfo *vip = &vnodeInfo[class];
+       struct VnodeInfo *vip = &salvinfo->vnodeInfo[class];
        for (i = 0; i < vip->nVnodes; i++)
            if (vip->vnodes[i].name)
                free(vip->vnodes[i].name);
@@ -4021,7 +4246,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
     /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
     if (volHeader.uniquifier < (maxunique + 1)) {
        if (!Showmode)
-           Log("Volume uniquifier is too low; fixed\n");
+           Log("Volume uniquifier %u is too low (max uniq %u); fixed\n", volHeader.uniquifier, maxunique);
        /* Plus 2,000 in case there are workstations out there with
         * cached vnodes that have since been deleted
         */
@@ -4035,40 +4260,60 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
            afs_printable_uint32_lu(vid));
     }
 
+    if (!Testing && salvinfo->VolumeChanged) {
 #ifdef FSSYNC_BUILD_CLIENT
-    if (!Testing && VolumeChanged) {
-       afs_int32 fsync_code;
-
-       fsync_code = FSYNC_VolOp(vid, NULL, FSYNC_VOL_BREAKCBKS, FSYNC_SALVAGE, NULL);
-       if (fsync_code) {
-           Log("Error trying to tell the fileserver to break callbacks for "
-               "changed volume %lu; error code %ld\n",
-               afs_printable_uint32_lu(vid),
-               afs_printable_int32_ld(fsync_code));
-       } else {
-           VolumeChanged = 0;
+       if (salvinfo->useFSYNC) {
+           afs_int32 fsync_code;
+
+           fsync_code = FSYNC_VolOp(vid, NULL, FSYNC_VOL_BREAKCBKS, FSYNC_SALVAGE, NULL);
+           if (fsync_code) {
+               Log("Error trying to tell the fileserver to break callbacks for "
+                   "changed volume %lu; error code %ld\n",
+                   afs_printable_uint32_lu(vid),
+                   afs_printable_int32_ld(fsync_code));
+           } else {
+               salvinfo->VolumeChanged = 0;
+           }
        }
-    }
 #endif /* FSSYNC_BUILD_CLIENT */
 
+#ifdef AFS_DEMAND_ATTACH_FS
+       if (!salvinfo->useFSYNC) {
+           /* A volume's contents have changed, but the fileserver will not
+            * break callbacks on the volume until it tries to load the vol
+            * header. So, to reduce the amount of time a client could have
+            * stale data, remove fsstate.dat, so the fileserver will init
+            * callback state with all clients. This is a very coarse hammer,
+            * and in the future we should just record which volumes have
+            * changed. */
+           code = unlink(AFSDIR_SERVER_FSSTATE_FILEPATH);
+           if (code && errno != ENOENT) {
+               Log("Error %d when trying to unlink FS state file %s\n", errno,
+                   AFSDIR_SERVER_FSSTATE_FILEPATH);
+           }
+       }
+#endif
+    }
+
     /* Turn off the inUse bit; the volume's been salvaged! */
     volHeader.inUse = 0;       /* clear flag indicating inUse@last crash */
     volHeader.needsSalvaged = 0;       /* clear 'damaged' flag */
     volHeader.inService = 1;   /* allow service again */
-    volHeader.needsCallback = (VolumeChanged != 0);
+    volHeader.needsCallback = (salvinfo->VolumeChanged != 0);
     volHeader.dontSalvage = DONT_SALVAGE;
-    VolumeChanged = 0;
+    salvinfo->VolumeChanged = 0;
     if (!Testing) {
        nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
-       assert(nBytes == sizeof(volHeader));
+       opr_Assert(nBytes == sizeof(volHeader));
     }
     if (!Showmode) {
-       Log("%sSalvaged %s (%u): %d files, %d blocks\n",
-           (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
+       Log("%sSalvaged %s (%" AFS_VOLID_FMT "): %d files, %d blocks\n",
+           (Testing ? "It would have " : ""), volHeader.name, afs_printable_VolumeId_lu(volHeader.id),
            FilesInVolume, BlocksInVolume);
     }
-    IH_RELEASE(vnodeInfo[vSmall].handle);
-    IH_RELEASE(vnodeInfo[vLarge].handle);
+
+    IH_RELEASE(salvinfo->vnodeInfo[vSmall].handle);
+    IH_RELEASE(salvinfo->vnodeInfo[vLarge].handle);
     IH_RELEASE(h);
     return 0;
 }
@@ -4082,15 +4327,15 @@ ClearROInUseBit(struct VolumeSummary *summary)
     VolumeDiskData volHeader;
 
     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
-    assert(nBytes == sizeof(volHeader));
-    assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
+    opr_Assert(nBytes == sizeof(volHeader));
+    opr_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
     volHeader.inUse = 0;
     volHeader.needsSalvaged = 0;
     volHeader.inService = 1;
     volHeader.dontSalvage = DONT_SALVAGE;
     if (!Testing) {
        nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
-       assert(nBytes == sizeof(volHeader));
+       opr_Assert(nBytes == sizeof(volHeader));
     }
 }
 
@@ -4100,46 +4345,52 @@ ClearROInUseBit(struct VolumeSummary *summary)
  * deleteMe - Always do so, only a partial volume.
  */
 void
-MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
-              int check)
+MaybeZapVolume(struct SalvInfo *salvinfo, struct InodeSummary *isp,
+               char *message, int deleteMe, int check)
 {
     if (readOnly(isp) || deleteMe) {
-       if (isp->volSummary && isp->volSummary->fileName) {
+       if (isp->volSummary && !isp->volSummary->deleted) {
            if (deleteMe) {
                if (!Showmode)
-                   Log("Volume %u (is only a partial volume--probably an attempt was made to move/restore it when a machine crash occured.\n", isp->volumeId);
+                   Log("Volume %" AFS_VOLID_FMT " (is only a partial volume--probably an attempt was made to move/restore it when a machine crash occured.\n", afs_printable_VolumeId_lu(isp->volumeId));
                if (!Showmode)
                    Log("It will be deleted on this server (you may find it elsewhere)\n");
            } else {
                if (!Showmode)
-                   Log("Volume %u needs to be salvaged.  Since it is read-only, however,\n", isp->volumeId);
+                   Log("Volume %" AFS_VOLID_FMT " needs to be salvaged.  Since it is read-only, however,\n", afs_printable_VolumeId_lu(isp->volumeId));
                if (!Showmode)
                    Log("it will be deleted instead.  It should be recloned.\n");
            }
            if (!Testing) {
                afs_int32 code;
                char path[64];
-               sprintf(path, "%s/%s", fileSysPath, isp->volSummary->fileName);
+               char filename[VMAXPATHLEN];
+               VolumeExternalName_r(isp->volumeId, filename, sizeof(filename));
+               sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, filename);
 
-               code = VDestroyVolumeDiskHeader(fileSysPartition, isp->volumeId, isp->RWvolumeId);
+               code = VDestroyVolumeDiskHeader(salvinfo->fileSysPartition, isp->volumeId, isp->RWvolumeId);
                if (code) {
-                   Log("Error %ld destroying volume disk header for volume %lu\n",
+                   Log("Error %ld destroying volume disk header for volume %" AFS_VOLID_FMT "\n",
                        afs_printable_int32_ld(code),
-                       afs_printable_uint32_lu(isp->volumeId));
+                       afs_printable_VolumeId_lu(isp->volumeId));
                }
 
-               /* make sure we actually delete the fileName file; ENOENT
+               /* make sure we actually delete the header file; ENOENT
                 * is fine, since VDestroyVolumeDiskHeader probably already
                 * unlinked it */
                if (unlink(path) && errno != ENOENT) {
                    Log("Unable to unlink %s (errno = %d)\n", path, errno);
                }
+               if (salvinfo->useFSYNC) {
+                   AskDelete(salvinfo, isp->volumeId);
+               }
+               isp->volSummary->deleted = 1;
            }
        }
     } else if (!check) {
-       Log("%s salvage was unsuccessful: read-write volume %u\n", message,
-           isp->volumeId);
-       Abort("Salvage of volume %u aborted\n", isp->volumeId);
+       Log("%s salvage was unsuccessful: read-write volume %" AFS_VOLID_FMT "\n", message,
+           afs_printable_VolumeId_lu(isp->volumeId));
+       Abort("Salvage of volume %" AFS_VOLID_FMT " aborted\n", afs_printable_VolumeId_lu(isp->volumeId));
     }
 }
 
@@ -4157,7 +4408,7 @@ MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
  * @note DAFS only
  */
 static int
-LockVolume(VolumeId volumeId)
+LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId)
 {
     afs_int32 code;
     int locktype;
@@ -4167,7 +4418,7 @@ LockVolume(VolumeId volumeId)
      * try to provide what we're logically doing. */
     locktype = VVolLockType(V_VOLUPD, 1);
 
-    code = VLockVolumeByIdNB(volumeId, fileSysPartition, locktype);
+    code = VLockVolumeByIdNB(volumeId, salvinfo->fileSysPartition, locktype);
     if (code) {
        if (code == EBUSY) {
            Abort("Someone else appears to be using volume %lu; Aborted\n",
@@ -4178,7 +4429,7 @@ LockVolume(VolumeId volumeId)
              afs_printable_uint32_lu(volumeId));
     }
 
-    code = FSYNC_VerifyCheckout(volumeId, fileSysPathName, FSYNC_VOL_OFF, FSYNC_SALVAGE);
+    code = FSYNC_VerifyCheckout(volumeId, salvinfo->fileSysPartition->name, FSYNC_VOL_OFF, FSYNC_SALVAGE);
     if (code == SYNC_DENIED) {
        /* need to retry checking out volumes */
        return -1;
@@ -4198,14 +4449,14 @@ LockVolume(VolumeId volumeId)
        struct VolumeDiskHeader diskHeader;
        struct VolumeDiskData volHeader;
 
-       code = VReadVolumeDiskHeader(volumeId, fileSysPartition, &diskHeader);
+       code = VReadVolumeDiskHeader(volumeId, salvinfo->fileSysPartition, &diskHeader);
        if (code) {
            return 0;
        }
 
        DiskToVolumeHeader(&header, &diskHeader);
 
-       IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
+       IH_INIT(h, salvinfo->fileSysDevice, header.parent, header.volumeInfo);
        if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
            volHeader.stamp.magic != VOLUMEINFOMAGIC) {
 
@@ -4222,7 +4473,8 @@ LockVolume(VolumeId volumeId)
         * case). But if it's there enough that we can read it, but
         * somehow we cannot write to it to signify we're salvaging it,
         * we've got a big problem and we cannot continue. */
-       assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
+       opr_Verify(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
+                      == sizeof(volHeader));
 
        IH_RELEASE(h);
     }
@@ -4231,8 +4483,23 @@ LockVolume(VolumeId volumeId)
 }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
+static void
+AskError(struct SalvInfo *salvinfo, VolumeId volumeId)
+{
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+    afs_int32 code;
+    code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
+                       FSYNC_VOL_FORCE_ERROR, FSYNC_WHATEVER, NULL);
+    if (code != SYNC_OK) {
+       Log("AskError: failed to force volume %lu into error state; "
+           "SYNC error code %ld (%s)\n", (long unsigned)volumeId,
+           (long)code, SYNC_res2string(code));
+    }
+#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
+}
+
 void
-AskOffline(VolumeId volumeId, char * partition)
+AskOffline(struct SalvInfo *salvinfo, VolumeId volumeId)
 {
     afs_int32 code, i;
     SYNC_response res;
@@ -4240,25 +4507,33 @@ AskOffline(VolumeId volumeId, char * partition)
     memset(&res, 0, sizeof(res));
 
     for (i = 0; i < 3; i++) {
-       code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
+       code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
+                          FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
 
        if (code == SYNC_OK) {
            break;
        } else if (code == SYNC_DENIED) {
-#ifdef DEMAND_ATTACH_ENABLE
-           Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
-#else
-           Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
-#endif
+           if (AskDAFS())
+               Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
+           else
+               Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
            Abort("Salvage aborted\n");
        } else if (code == SYNC_BAD_COMMAND) {
            Log("AskOffline:  fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
                FSYNC_VOL_OFF);
-#ifdef DEMAND_ATTACH_ENABLE
-           Log("AskOffline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
+           if (AskDAFS()) {
+#ifdef AFS_DEMAND_ATTACH_FS
+               Log("AskOffline:  please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
 #else
-           Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
+               Log("AskOffline:  fileserver is DAFS but we are not.\n");
 #endif
+           } else {
+#ifdef AFS_DEMAND_ATTACH_FS
+               Log("AskOffline:  fileserver is not DAFS but we are.\n");
+#else
+               Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
+#endif
+           }
            Abort("Salvage aborted\n");
        } else if (i < 2) {
            /* try it again */
@@ -4273,30 +4548,125 @@ AskOffline(VolumeId volumeId, char * partition)
     }
 }
 
+/* don't want to pass around state; remember it here */
+static int isDAFS = -1;
+int
+AskDAFS(void)
+{
+    SYNC_response res;
+    afs_int32 code = 1, i;
+
+    /* we don't care if we race. the answer shouldn't change */
+    if (isDAFS != -1)
+       return isDAFS;
+
+    memset(&res, 0, sizeof(res));
+
+    for (i = 0; code && i < 3; i++) {
+       code = FSYNC_VolOp(0, NULL, FSYNC_VOL_LISTVOLUMES, FSYNC_SALVAGE, &res);
+       if (code) {
+           Log("AskDAFS: FSYNC_VOL_LISTVOLUMES failed with code %ld reason "
+               "%ld (%s); trying again...\n", (long)code, (long)res.hdr.reason,
+               FSYNC_reason2string(res.hdr.reason));
+           FSYNC_clientFinis();
+           FSYNC_clientInit();
+       }
+    }
+
+    if (code) {
+       Log("AskDAFS: could not determine DAFS-ness, assuming not DAFS\n");
+       res.hdr.flags = 0;
+    }
+
+    if ((res.hdr.flags & SYNC_FLAG_DAFS_EXTENSIONS)) {
+       isDAFS = 1;
+    } else {
+       isDAFS = 0;
+    }
+
+    return isDAFS;
+}
+
+static void
+MaybeAskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
+{
+    struct VolumeDiskHeader diskHdr;
+    int code;
+    code = VReadVolumeDiskHeader(volumeId, salvinfo->fileSysPartition, &diskHdr);
+    if (code) {
+       /* volume probably does not exist; no need to bring back online */
+       return;
+    }
+    AskOnline(salvinfo, volumeId);
+}
+
 void
-AskOnline(VolumeId volumeId, char *partition)
+AskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
 {
     afs_int32 code, i;
 
     for (i = 0; i < 3; i++) {
-       code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
+       code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
+                          FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
 
        if (code == SYNC_OK) {
            break;
        } else if (code == SYNC_DENIED) {
-           Log("AskOnline:  file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
+           Log("AskOnline:  file server denied online request to volume %" AFS_VOLID_FMT " partition %s; trying again...\n", afs_printable_VolumeId_lu(volumeId), salvinfo->fileSysPartition->name);
        } else if (code == SYNC_BAD_COMMAND) {
            Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
                FSYNC_VOL_ON);
-#ifdef DEMAND_ATTACH_ENABLE
-           Log("AskOnline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
+           Log("AskOnline:  please make sure file server binaries are same version.\n");
+           break;
+       } else if (i < 2) {
+           /* try it again */
+           Log("AskOnline:  request for fileserver to put volume online failed; trying again...\n");
+           FSYNC_clientFinis();
+           FSYNC_clientInit();
+       }
+    }
+}
+
+void
+AskDelete(struct SalvInfo *salvinfo, VolumeId volumeId)
+{
+    afs_int32 code, i;
+    SYNC_response res;
+
+    for (i = 0; i < 3; i++) {
+       memset(&res, 0, sizeof(res));
+       code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
+                          FSYNC_VOL_DONE, FSYNC_SALVAGE, &res);
+
+       if (code == SYNC_OK) {
+           break;
+       } else if (code == SYNC_DENIED) {
+           Log("AskOnline:  file server denied DONE request to volume %" AFS_VOLID_FMT " partition %s; trying again...\n", afs_printable_VolumeId_lu(volumeId), salvinfo->fileSysPartition->name);
+       } else if (code == SYNC_BAD_COMMAND) {
+           Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
+               FSYNC_VOL_DONE);
+           if (AskDAFS()) {
+#ifdef AFS_DEMAND_ATTACH_FS
+               Log("AskOnline:  please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
 #else
-           Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
+               Log("AskOnline:  fileserver is DAFS but we are not.\n");
 #endif
+           } else {
+#ifdef AFS_DEMAND_ATTACH_FS
+               Log("AskOnline:  fileserver is not DAFS but we are.\n");
+#else
+               Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
+#endif
+           }
+           break;
+       } else if (code == SYNC_FAILED &&
+                    (res.hdr.reason == FSYNC_UNKNOWN_VOLID ||
+                     res.hdr.reason == FSYNC_WRONG_PART)) {
+           /* volume is already effectively 'deleted' */
            break;
        } else if (i < 2) {
            /* try it again */
-           Log("AskOnline:  request for fileserver to take volume offline failed; trying again...\n");
+           Log("AskOnline:  request for fileserver to delete volume failed; trying again...\n");
            FSYNC_clientFinis();
            FSYNC_clientInit();
        }
@@ -4313,15 +4683,18 @@ CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
     IHandle_t *srcH, *destH;
     FdHandle_t *srcFdP, *destFdP;
     ssize_t nBytes = 0;
+    afs_foff_t size = 0;
 
     IH_INIT(srcH, device, rwvolume, inode1);
     srcFdP = IH_OPEN(srcH);
-    assert(srcFdP != NULL);
+    opr_Assert(srcFdP != NULL);
     IH_INIT(destH, device, rwvolume, inode2);
     destFdP = IH_OPEN(destH);
-    while ((nBytes = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
-       assert(FDH_WRITE(destFdP, buf, nBytes) == nBytes);
-    assert(nBytes == 0);
+    while ((nBytes = FDH_PREAD(srcFdP, buf, sizeof(buf), size)) > 0) {
+       opr_Verify(FDH_PWRITE(destFdP, buf, nBytes, size) == nBytes);
+       size += nBytes;
+    }
+    opr_Assert(nBytes == 0);
     FDH_REALLYCLOSE(srcFdP);
     FDH_REALLYCLOSE(destFdP);
     IH_RELEASE(srcH);
@@ -4330,47 +4703,39 @@ CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
 }
 
 void
-PrintInodeList(void)
+PrintInodeList(struct SalvInfo *salvinfo)
 {
-    register struct ViceInodeInfo *ip;
+    struct ViceInodeInfo *ip;
     struct ViceInodeInfo *buf;
-    struct afs_stat status;
-    register int nInodes;
-
-    assert(afs_fstat(inodeFd, &status) == 0);
-    buf = (struct ViceInodeInfo *)malloc(status.st_size);
-    assert(buf != NULL);
-    nInodes = status.st_size / sizeof(struct ViceInodeInfo);
-    assert(read(inodeFd, buf, status.st_size) == status.st_size);
+    int nInodes;
+    afs_ino_str_t stmp;
+    afs_sfsize_t st_size;
+
+    st_size = OS_SIZE(salvinfo->inodeFd);
+    opr_Assert(st_size >= 0);
+    buf = malloc(st_size);
+    opr_Assert(buf != NULL);
+    nInodes = st_size / sizeof(struct ViceInodeInfo);
+    opr_Verify(OS_READ(salvinfo->inodeFd, buf, st_size) == st_size);
     for (ip = buf; nInodes--; ip++) {
-       Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
-           PrintInode(NULL, ip->inodeNumber), ip->linkCount,
-           (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
+       Log("Inode:%s, linkCount=%d, size=%#llx, p=(%" AFS_VOLID_FMT ",%u,%u,%u)\n", /* VolumeId in param */
+           PrintInode(stmp, ip->inodeNumber), ip->linkCount,
+           (afs_uintmax_t) ip->byteCount,
+           afs_printable_VolumeId_lu(ip->u.param[0]), ip->u.param[1],
            ip->u.param[2], ip->u.param[3]);
     }
     free(buf);
 }
 
 void
-PrintInodeSummary(void)
+PrintInodeSummary(struct SalvInfo *salvinfo)
 {
     int i;
     struct InodeSummary *isp;
 
-    for (i = 0; i < nVolumesInInodeFile; i++) {
-       isp = &inodeSummary[i];
-       Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n", isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes, isp->nSpecialInodes, isp->maxUniquifier);
-    }
-}
-
-void
-PrintVolumeSummary(void)
-{
-    int i;
-    struct VolumeSummary *vsp;
-
-    for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
-       Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
+    for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
+       isp = &salvinfo->inodeSummary[i];
+       Log("VID:%" AFS_VOLID_FMT ", RW:%" AFS_VOLID_FMT ", index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n", afs_printable_VolumeId_lu(isp->volumeId), afs_printable_VolumeId_lu(isp->RWvolumeId), isp->index, isp->nInodes, isp->nSpecialInodes, isp->maxUniquifier);
     }
 }
 
@@ -4380,10 +4745,10 @@ Fork(void)
     int f;
 #ifdef AFS_NT40_ENV
     f = 0;
-    assert(0);                 /* Fork is never executed in the NT code path */
+    opr_Assert(0);     /* Fork is never executed in the NT code path */
 #else
     f = fork();
-    assert(f >= 0);
+    opr_Assert(f >= 0);
 #ifdef AFS_DEMAND_ATTACH_FS
     if ((f == 0) && (programType == salvageServer)) {
        /* we are a salvageserver child */
@@ -4399,20 +4764,29 @@ Fork(void)
     return f;
 }
 
-void
-Exit(int code)
+static void
+QuietExit(int code)
 {
-    if (ShowLog)
-       showlog();
-
 #ifdef AFS_DEMAND_ATTACH_FS
     if (programType == salvageServer) {
-#ifdef SALVSYNC_BUILD_CLIENT
+       /* release all volume locks before closing down our SYNC channels.
+        * the fileserver may try to online volumes we have checked out when
+        * we close down FSSYNC, so we should make sure we don't have those
+        * volumes locked when it does */
+       struct DiskPartition64 *dp;
+       int i;
+       for (i = 0; i <= VOLMAXPARTS; i++) {
+           dp = VGetPartitionById(i, 0);
+           if (dp) {
+               VLockFileReinit(&dp->volLockFile);
+           }
+       }
+# ifdef SALVSYNC_BUILD_CLIENT
        VDisconnectSALV();
-#endif
-#ifdef FSSYNC_BUILD_CLIENT
+# endif
+# ifdef FSSYNC_BUILD_CLIENT
        VDisconnectFS();
-#endif
+# endif
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
@@ -4426,13 +4800,21 @@ Exit(int code)
 #endif
 }
 
+void
+Exit(int code)
+{
+    SalvageShowLog();
+    QuietExit(code);
+}
+
+
 int
 Wait(char *prog)
 {
     int status;
     int pid;
     pid = wait(&status);
-    assert(pid != -1);
+    opr_Assert(pid != -1);
     if (WCOREDUMP(status))
        Log("\"%s\" core dumped!\n", prog);
     if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
@@ -4441,153 +4823,91 @@ Wait(char *prog)
 }
 
 static char *
-TimeStamp(time_t clock, int precision)
+TimeStamp(char *buffer, size_t size, time_t clock, int precision)
 {
     struct tm *lt;
-    static char timestamp[20];
+    size_t nbytes;
+
     lt = localtime(&clock);
     if (precision)
-       (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
+       nbytes = strftime(buffer, size, "%m/%d/%Y %H:%M:%S", lt);
     else
-       (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
-    return timestamp;
+       nbytes = strftime(buffer, size, "%m/%d/%Y %H:%M", lt);
+    if (nbytes == 0)
+       memset(buffer, 0, size);
+    return buffer;
 }
 
-void
-CheckLogFile(char * log_path)
-{
-    char oldSlvgLog[AFSDIR_PATH_MAX];
-
-#ifndef AFS_NT40_ENV
-    if (useSyslog) {
-       ShowLog = 0;
-       return;
-    }
-#endif
-
-    strcpy(oldSlvgLog, log_path);
-    strcat(oldSlvgLog, ".old");
-    if (!logFile) {
-       renamefile(log_path, oldSlvgLog);
-       logFile = afs_fopen(log_path, "a");
-
-       if (!logFile) {         /* still nothing, use stdout */
-           logFile = stdout;
-           ShowLog = 0;
-       }
-#ifndef AFS_NAMEI_ENV
-       AFS_DEBUG_IOPS_LOG(logFile);
-#endif
-    }
-}
-
-#ifndef AFS_NT40_ENV
-void
-TimeStampLogFile(char * log_path)
-{
-    char stampSlvgLog[AFSDIR_PATH_MAX];
-    struct tm *lt;
-    time_t now;
-
-    now = time(0);
-    lt = localtime(&now);
-    (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
-                      "%s.%04d-%02d-%02d.%02d:%02d:%02d",
-                      log_path, lt->tm_year + 1900,
-                      lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
-                      lt->tm_sec);
-
-    /* try to link the logfile to a timestamped filename */
-    /* if it fails, oh well, nothing we can do */
-    link(log_path, stampSlvgLog);
-}
-#endif
-
-void
-showlog(void)
+static void
+SalvageShowLog(void)
 {
     char line[256];
+    FILE *logFile;
 
-#ifndef AFS_NT40_ENV
-    if (useSyslog) {
-       printf("Can't show log since using syslog.\n");
-       fflush(stdout);
+    if (ShowLog == 0 || ClientMode) {
        return;
     }
-#endif
 
-    if (logFile) {
-       rewind(logFile);
-       fclose(logFile);
+    if (ShowLogFilename == NULL) {
+       ShowLogFilename = strdup(AFSDIR_SERVER_SLVGLOG_FILEPATH);
     }
-
-    logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
-
+    CloseLog();
+    logFile = afs_fopen(ShowLogFilename, "r");
     if (!logFile)
-       printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
+       printf("Can't read %s, exiting\n", ShowLogFilename);
     else {
-       rewind(logFile);
        while (fgets(line, sizeof(line), logFile))
            printf("%s", line);
        fflush(stdout);
     }
 }
 
+static void
+vLog(const char *format, va_list args)
+{
+    if (!ClientMode) {
+       vFSLog(format, args);
+    } else {
+       struct timeval now;
+       char buffer[64];
+
+       gettimeofday(&now, NULL);
+       fprintf(stderr, "%s ", TimeStamp(buffer, sizeof(buffer), now.tv_sec, 1));
+       vfprintf(stderr, format, args);
+       fflush(stderr);
+    }
+}
+
 void
 Log(const char *format, ...)
 {
-    struct timeval now;
-    char tmp[1024];
     va_list args;
 
     va_start(args, format);
-    (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
+    vLog(format, args);
     va_end(args);
-#ifndef AFS_NT40_ENV
-    if (useSyslog) {
-       syslog(LOG_INFO, "%s", tmp);
-    } else
-#endif
-       if (logFile) {
-           gettimeofday(&now, 0);
-           fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
-           fflush(logFile);
-       }
 }
 
 void
 Abort(const char *format, ...)
 {
     va_list args;
-    char tmp[1024];
 
     va_start(args, format);
-    (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
+    vLog(format, args);
     va_end(args);
-#ifndef AFS_NT40_ENV
-    if (useSyslog) {
-       syslog(LOG_INFO, "%s", tmp);
-    } else
-#endif
-       if (logFile) {
-           fprintf(logFile, "%s", tmp);
-           fflush(logFile);
-           if (ShowLog)
-               showlog();
-       }
-
+    SalvageShowLog();
     if (debug)
        abort();
-    Exit(1);
+    QuietExit(1);
 }
 
 char *
 ToString(const char *s)
 {
-    register char *p;
-    p = (char *)malloc(strlen(s) + 1);
-    assert(p != NULL);
-    strcpy(p, s);
+    char *p;
+    p = strdup(s);
+    opr_Assert(p != NULL);
     return p;
 }
 
@@ -4596,7 +4916,7 @@ void
 RemoveTheForce(char *path)
 {
     char target[1024];
-    struct afs_stat force; /* so we can use afs_stat to find it */
+    struct afs_stat_st force; /* so we can use afs_stat to find it */
     strcpy(target,path);
     strcat(target,"/FORCESALVAGE");
     if (!Testing && ForceSalvage) {
@@ -4611,7 +4931,7 @@ RemoveTheForce(char *path)
 int
 UseTheForceLuke(char *path)
 {
-    struct afs_stat force;
+    struct afs_stat_st force;
     char target[1024];
     strcpy(target,path);
     strcat(target,"/FORCESALVAGE");
@@ -4664,7 +4984,7 @@ int
 nt_SetupPartitionSalvage(void *datap, int len)
 {
     childJob_t *jobp = (childJob_t *) datap;
-    char logname[AFSDIR_PATH_MAX];
+    char *logname;
 
     if (len != sizeof(childJob_t))
        return -1;
@@ -4673,11 +4993,11 @@ nt_SetupPartitionSalvage(void *datap, int len)
     myjob = *jobp;
 
     /* Open logFile */
-    (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
-                 myjob.cj_number);
-    logFile = afs_fopen(logname, "w");
-    if (!logFile)
-       logFile = stdout;
+    if (asprintf(&logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
+                myjob.cj_number) < 0)
+       return -1;
+    OpenLog(logname);
+    free(logname);
 
     return 0;
 }