vol: Use FDH_SIZE more consistently
[openafs.git] / src / vol / vol-salvage.c
index f017392..f10e7b1 100644 (file)
@@ -99,9 +99,12 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #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/afs_assert.h>
 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
 #if defined(AFS_VFSINCL_ENV)
 #include <sys/vnode.h>
@@ -116,13 +119,9 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #endif
 #endif
 #else /* AFS_VFSINCL_ENV */
-#ifdef AFS_OSF_ENV
-#include <ufs/inode.h>
-#else /* AFS_OSF_ENV */
-#if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_ARM_DARWIN_ENV)
+#if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_DARWIN_ENV)
 #include <sys/inode.h>
 #endif
-#endif
 #endif /* AFS_VFSINCL_ENV */
 #endif /* AFS_SGI_ENV */
 #ifdef AFS_AIX_ENV
@@ -154,6 +153,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 <rx/rx_queue.h>
 
 #include "nfs.h"
 #include "lwp.h"
@@ -164,7 +164,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"
@@ -184,10 +186,9 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <pthread.h>
 #endif
 
-#ifdef AFS_OSF_ENV
-extern void *calloc();
-#endif
-static char *TimeStamp(time_t clock, int precision);
+#define SALV_BUFFER_SIZE 1024
+
+static char *TimeStamp(char *buffer, size_t size, time_t clock, int precision);
 
 
 int debug;                     /* -d flag */
@@ -199,16 +200,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;
@@ -222,8 +219,6 @@ 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 */
 /**
@@ -232,7 +227,7 @@ FILE *logFile = 0;  /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
 struct SalvInfo {
     Device fileSysDevice;    /**< The device number of the current partition
                              *   being salvaged */
-    char fileSysPath[8];     /**< The path of the mounted partition currently
+    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. */
@@ -275,14 +270,17 @@ char *tmpdir = NULL;
 
 
 /* Forward declarations */
+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);
 
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#ifdef AFS_DEMAND_ATTACH_FS
 static int LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId);
-#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
+#endif /* AFS_DEMAND_ATTACH_FS */
 
 /* Uniquifier stored in the Inode */
 static Unique
@@ -365,7 +363,7 @@ IsPartitionMounted(char *part)
     FILE *mntfp;
     struct mntent *mntent;
 
-    osi_Assert(mntfp = setmntent(MOUNTED, "r"));
+    opr_Verify(mntfp = setmntent(MOUNTED, "r"));
     while (mntent = getmntent(mntfp)) {
        if (!strcmp(part, mntent->mnt_dir))
            break;
@@ -474,22 +472,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++;
@@ -540,12 +535,12 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
             * job to finish. When it's done, clean up after it.
             */
            pid = wait(&wstatus);
-           osi_Assert(pid != -1);
+           opr_Assert(pid != -1);
            for (j = 0; j < numjobs; j++) {     /* Find which job it is */
                if (pid == jobs[j]->pid)
                    break;
            }
-           osi_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);
            }
@@ -601,26 +596,26 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
                numjobs++;
            } else {
                int fd;
+               char *filename;
+               struct logOptions logopts;
+
+               memset(&logopts, 0, sizeof(logopts));
+               logopts.lopt_dest = logDest_file;
 
-               ShowLog = 0;
                for (fd = 0; fd < 16; fd++)
                    close(fd);
                open(OS_DIRSEP, 0);
                dup2(0, 1);
                dup2(0, 2);
-#ifndef AFS_NT40_ENV
-               if (useSyslog) {
-                   openlog("salvager", LOG_PID, useSyslogFacility);
-               } else
-#endif
-               {
-                   snprintf(logFileName, sizeof logFileName, "%s.%d",
+
+               ShowLog = 0; /* Child processes do not display. */
+               if (asprintf(&filename, "%s.%d",
                             AFSDIR_SERVER_SLVGLOG_FILEPATH,
-                            jobs[startjob]->jobnumb);
-                   logFile = afs_fopen(logFileName, "w");
+                            jobs[startjob]->jobnumb) >= 0) {
+                   logopts.lopt_filename = filename;
+                   OpenLog(&logopts);
+                   free(filename);
                }
-               if (!logFile)
-                   logFile = stdout;
 
                SalvageFileSys1(jobs[startjob]->partP, 0);
                Exit(0);
@@ -629,24 +624,37 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
        }
     }                          /* while ( thisjob || (!partP && numjobs > 0) ) */
 
-    /* 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 */
-#endif
+    /*
+     * If waited for all jobs to complete, now collect log files and return.
+     * No files can be collected when logging to the system log (syslog).
+     */
+    if (GetLogDest() == logDest_file) {
        if (!partP) {
-           for (i = 0; i < jobcount; i++) {
-               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;
                    }
-                   fclose(passLog);
+                   if ((passLog = afs_fopen(logFileName, "r"))) {
+                       while (fgets(buf, SALV_BUFFER_SIZE, passLog)) {
+                           WriteLogBuffer(buf, strlen(buf));
+                       }
+                       fclose(passLog);
+                   }
+                   (void)unlink(logFileName);
+                   free(logFileName);
                }
-               (void)unlink(logFileName);
+               free(buf);
            }
-           fflush(logFile);
        }
+    }
     return;
 }
 
@@ -657,8 +665,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");
@@ -687,7 +694,7 @@ void
 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 {
     char *name, *tdir;
-    char inodeListPath[256];
+    char *inodeListPath = NULL;
     FD_t inodeFile = INVALID_FD;
     static char tmpDevName[100];
     static char wpath[100];
@@ -710,13 +717,13 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
        Abort("Raced too many times with fileserver restarts while trying to "
              "checkout/lock volumes; Aborted\n");
     }
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#ifdef AFS_DEMAND_ATTACH_FS
     if (tries > 1) {
        /* unlock all previous volume locks, since we're about to lock them
         * again */
        VLockFileReinit(&partP->volLockFile);
     }
-#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
+#endif /* AFS_DEMAND_ATTACH_FS */
 
     salvinfo->fileSysPartition = partP;
     salvinfo->fileSysDevice = salvinfo->fileSysPartition->device;
@@ -735,11 +742,11 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 #endif
 
     if (singleVolumeNumber) {
-#if !(defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL))
+#ifndef AFS_DEMAND_ATTACH_FS
        /* only non-DAFS locks the partition when salvaging a single volume;
         * DAFS will lock the individual volumes in the VG */
        VLockPartition(partP->name);
-#endif /* !(AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL) */
+#endif /* !AFS_DEMAND_ATTACH_FS */
 
        ForceSalvage = 1;
 
@@ -750,11 +757,11 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 
        salvinfo->useFSYNC = 1;
        AskOffline(salvinfo, singleVolumeNumber);
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#ifdef AFS_DEMAND_ATTACH_FS
        if (LockVolume(salvinfo, singleVolumeNumber)) {
            goto retry;
        }
-#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
+#endif /* AFS_DEMAND_ATTACH_FS */
 
     } else {
        salvinfo->useFSYNC = 0;
@@ -780,7 +787,7 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
        DIR *dirp;
        struct dirent *dp;
 
-       osi_Assert((dirp = opendir(salvinfo->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)) {
@@ -797,10 +804,16 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
     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);
+    inodeListPath = strdup(_tempnam(tdir, "salvage.inodes."));
+    if (inodeListPath == NULL) {
+       Abort("Error allocating memory for inodeListPath\n");
+    }
 #else
-    snprintf(inodeListPath, 255, "%s" OS_DIRSEP "salvage.inodes.%s.%d", tdir, name,
+    code = asprintf(&inodeListPath, "%s" OS_DIRSEP "salvage.inodes.%s.%d", tdir, name,
             getpid());
+    if (code == -1) {
+       Abort("Error allocating memory for inodeListPath\n");
+    }
 #endif
 
     inodeFile = OS_OPEN(inodeListPath, O_RDWR|O_TRUNC|O_CREAT, 0666);
@@ -829,16 +842,16 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 
     if (GetInodeSummary(salvinfo, inodeFile, singleVolumeNumber) < 0) {
        OS_CLOSE(inodeFile);
-       if (singleVolumeNumber) {
-           /* the volume group -- let alone the volume -- does not exist,
-            * but we checked it out, so give it back to the fileserver */
-           AskDelete(salvinfo, singleVolumeNumber);
-       }
+       free(inodeListPath);
        return;
     }
     salvinfo->inodeFd = inodeFile;
     if (salvinfo->inodeFd == INVALID_FD)
        Abort("Temporary file %s is missing...\n", inodeListPath);
+
+    free(inodeListPath);
+    inodeListPath = NULL;
+
     OS_SEEK(salvinfo->inodeFd, 0L, SEEK_SET);
     if (ListInodeOption) {
        PrintInodeList(salvinfo);
@@ -862,6 +875,16 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
        goto retry;
     }
 
+    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;
@@ -875,7 +898,7 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
             * 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)
+               if (vsp->unused)
                    DeleteExtraVolumeHeaderFile(salvinfo, vsp);
            }
            /* Now match up the volume summary info from the root directory with the
@@ -884,7 +907,7 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
            for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
                if (tsp->header.id == vid) {
                    salvinfo->inodeSummary[j].volSummary = tsp;
-                   tsp->fileName = 0;
+                   tsp->unused = 0;
                    break;
                }
            }
@@ -892,12 +915,17 @@ 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(salvinfo, &salvinfo->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)
+       if (vsp->unused)
            DeleteExtraVolumeHeaderFile(salvinfo, vsp);
     }
 
@@ -906,10 +934,10 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 
     if (!Testing && singleVolumeNumber) {
        int foundSVN = 0;
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#ifdef AFS_DEMAND_ATTACH_FS
        /* unlock vol headers so the fs can attach them when we AskOnline */
        VLockFileReinit(&salvinfo->fileSysPartition->volLockFile);
-#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
+#endif /* AFS_DEMAND_ATTACH_FS */
 
        /* Step through the volumeSummary list and set all volumes on-line.
         * Most volumes were taken off-line in GetVolumeSummary.
@@ -955,8 +983,15 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 void
 DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp)
 {
-    char path[64];
-    sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, vsp->fileName);
+    char path[VMAXPATHLEN + 10];
+    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 " : ""));
@@ -964,12 +999,12 @@ DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp
        afs_int32 code;
        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) {
@@ -980,7 +1015,6 @@ DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp
        }
        vsp->deleted = 1;
     }
-    vsp->fileName = 0;
 }
 
 int
@@ -1111,7 +1145,7 @@ CountVolumeInodes(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);
@@ -1143,6 +1177,8 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
     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... */
@@ -1152,7 +1188,8 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
                        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);
     }
@@ -1205,19 +1242,40 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
            else {
                struct VolumeSummary *vsp;
                int i;
+               int foundSVN = 0;
 
                GetVolumeSummary(salvinfo, singleVolumeNumber);
 
                for (i = 0, vsp = salvinfo->volumeSummaryp; i < salvinfo->nVolumes; i++) {
-                   if (vsp->fileName)
+                   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) {
            OS_CLOSE(summaryFile);
            Abort
@@ -1241,7 +1299,8 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
            if (OS_WRITE(summaryFile, &summary, sizeof(summary)) != sizeof(summary)) {
                Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
                OS_CLOSE(summaryFile);
-               return -1;
+               retcode = -1;
+               goto error;
            }
            summary.index += (summary.nInodes);
            nInodes -= summary.nInodes;
@@ -1253,11 +1312,11 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
        if (OS_SYNC(summaryFile) == -1) {
            Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
            OS_CLOSE(summaryFile);
-           return -1;
+           retcode = -1;
+           goto error;
        }
        if (canfork && !debug) {
-           ShowLog = 0;
-           Exit(0);
+           QuietExit(0);
        }
     } else {
        if (Wait("Inode summary") == -1) {
@@ -1267,15 +1326,15 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
     }
 
     st_size = OS_SIZE(summaryFile);
-    osi_Assert(st_size >= 0);
+    opr_Assert(st_size >= 0);
     if (st_size != 0) {
        int ret;
-       salvinfo->inodeSummary = (struct InodeSummary *)malloc(st_size);
-       osi_Assert(salvinfo->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. */
-       osi_Assert(OS_SEEK(summaryFile, 0, SEEK_SET) == 0);
+       opr_Assert(OS_SEEK(summaryFile, 0, SEEK_SET) == 0);
        ret = OS_READ(summaryFile, salvinfo->inodeSummary, st_size);
-       osi_Assert(ret == st_size);
+       opr_Assert(ret == st_size);
     }
     salvinfo->nVolumesInInodeFile = st_size / sizeof(struct InodeSummary);
     for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
@@ -1283,7 +1342,13 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
     }
     Log("%d nVolumesInInodeFile %lu \n",salvinfo->nVolumesInInodeFile,(unsigned long)st_size);
     OS_CLOSE(summaryFile);
-    return 0;
+
+ error:
+    if (retcode && singleVolumeNumber && !deleted) {
+       AskError(salvinfo, singleVolumeNumber);
+    }
+
+    return retcode;
 }
 
 /* Comparison routine for volume sort.
@@ -1375,9 +1440,9 @@ AskVolumeSummary(struct SalvInfo *salvinfo, 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),
+                   afs_printable_VolumeId_lu(singleVolumeNumber),
                    salvinfo->fileSysPartition->name,
                    afs_printable_int32_ld(code),
                    afs_printable_int32_ld(res.hdr.reason));
@@ -1385,9 +1450,9 @@ AskVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
            }
 
            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,
@@ -1400,7 +1465,7 @@ AskVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
            }
 
            salvinfo->volumeSummaryp = calloc(VOL_VG_MAX_VOLS, sizeof(struct VolumeSummary));
-           osi_Assert(salvinfo->volumeSummaryp != NULL);
+           opr_Assert(salvinfo->volumeSummaryp != NULL);
 
            salvinfo->nVolumes = 0;
            vsp = salvinfo->volumeSummaryp;
@@ -1431,7 +1496,7 @@ AskVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
 
                DiskToVolumeHeader(&vsp->header, &diskHdr);
                VolumeExternalName_r(q_res.children[i], name, sizeof(name));
-               vsp->fileName = ToString(name);
+               vsp->unused = 1;
                salvinfo->nVolumes++;
                vsp++;
            }
@@ -1517,6 +1582,8 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
 
     params = (struct SalvageScanParams *)rock;
 
+    memset(&summary, 0, sizeof(summary));
+
     singleVolumeNumber = params->singleVolumeNumber;
     salvinfo = params->salvinfo;
 
@@ -1527,8 +1594,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,
@@ -1539,8 +1607,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);
        }
     }
@@ -1558,7 +1626,7 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
        }
 
        snprintf(nameShouldBe, sizeof nameShouldBe,
-                VFORMAT, afs_printable_uint32_lu(summary.header.id));
+                VFORMAT, afs_printable_VolumeId_lu(summary.header.id));
 
 
        if (strcmp(nameShouldBe, base)) {
@@ -1578,7 +1646,7 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
 
                AskOffline(salvinfo, summary.header.id);
 
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#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. */
@@ -1587,7 +1655,7 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
                        return -1;
                    }
                }
-#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
+#endif /* AFS_DEMAND_ATTACH_FS */
            }
        }
        if (badname) {
@@ -1600,7 +1668,7 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
            return 1;
        }
 
-       summary.fileName = ToString(base);
+       summary.unused = 1;
        params->nVolumes++;
 
        if (params->nVolumes > params->totalVolumes) {
@@ -1716,7 +1784,7 @@ GetVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
     }
 
     salvinfo->volumeSummaryp = calloc(nvols, sizeof(struct VolumeSummary));
-    osi_Assert(salvinfo->volumeSummaryp != NULL);
+    opr_Assert(salvinfo->volumeSummaryp != NULL);
 
     params.singleVolumeNumber = singleVolumeNumber;
     params.vsp = salvinfo->volumeSummaryp;
@@ -1744,10 +1812,11 @@ GetVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
     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
+static Inode
 FindLinkHandle(struct InodeSummary *isp, int nVols,
               struct ViceInodeInfo *allInodes)
 {
@@ -1757,13 +1826,65 @@ FindLinkHandle(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(struct SalvInfo *salvinfo, struct InodeSummary *isp, Inode ino)
 {
@@ -1772,29 +1893,29 @@ CreateLinkTable(struct SalvInfo *salvinfo, struct InodeSummary *isp, Inode ino)
 
     if (!VALID_INO(ino))
        ino =
-           IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->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);
+           ("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_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);
 
@@ -1817,7 +1938,7 @@ nt_SVG(void *arg)
 }
 
 void
-SalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
+nt_SalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
 {
     pthread_t tid;
     pthread_attr_t tattr;
@@ -1882,14 +2003,14 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
     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 */
-    osi_Assert(OS_SEEK
+    opr_Verify(OS_SEEK
           (salvinfo->inodeFd, isp->index * sizeof(struct ViceInodeInfo),
            SEEK_SET) != -1);
-    osi_Assert(OS_READ(salvinfo->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 */
@@ -1901,24 +2022,37 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
        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(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
        } else {
-            int i, j;
-            struct ViceInodeInfo *ip;
+           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, 1);
+           /* 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;
                    }
-               }
+               }
            }
        }
     }
@@ -1936,21 +2070,39 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
        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--) {
@@ -1986,14 +2138,29 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
                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(stmp, 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(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
                        Log("idec failed. inode %s errno %d\n",
@@ -2004,7 +2171,6 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
                ip->linkCount--;
            }
            while (ip->linkCount < 0) {
-               /* these used to be asserts */
                if (!Testing) {
                    if (IH_INC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
                        Log("iinc failed. inode %s errno %d\n",
@@ -2037,8 +2203,7 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
     IH_RELEASE(salvinfo->VGLinkH);
 
     if (canfork && !debug) {
-       ShowLog = 0;
-       Exit(0);
+       QuietExit(0);
     }
 }
 
@@ -2121,10 +2286,8 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, 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));
@@ -2175,8 +2338,8 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, 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));
            }
@@ -2199,7 +2362,7 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, 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);
                }
@@ -2265,7 +2428,7 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, 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(salvinfo->VGLinkH->ih_ino)) {
                *stuff[i].inode = salvinfo->VGLinkH->ih_ino;
@@ -2277,26 +2440,25 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
     }
 
     if (isp->volSummary == NULL) {
-       char path[64];
+       char path[VMAXPATHLEN];
        char headerName[64];
        snprintf(headerName, sizeof headerName, VFORMAT,
-                afs_printable_uint32_lu(isp->volumeId));
+                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 = calloc(1, sizeof(struct VolumeSummary));
-       isp->volSummary->fileName = ToString(headerName);
 
        writefunc = VCreateVolumeDiskHeader;
     } else {
-       char path[64];
+       char path[VMAXPATHLEN];
        char headerName[64];
        /* hack: these two fields are obsolete... */
        isp->volSummary->header.volumeAcl = 0;
@@ -2305,14 +2467,7 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, 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 {
-               snprintf(headerName, sizeof headerName, VFORMAT,
-                        afs_printable_uint32_lu(isp->volumeId));
-               isp->volSummary->fileName = ToString(headerName);
-           }
+           VolumeExternalName_r(isp->volumeId, headerName, sizeof(headerName));
            snprintf(path, sizeof path, "%s" OS_DIRSEP "%s",
                     salvinfo->fileSysPath, headerName);
 
@@ -2329,16 +2484,16 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, 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, 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;
            }
        }
@@ -2365,7 +2520,7 @@ SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
        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) {
@@ -2418,6 +2573,14 @@ SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
         * 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)
@@ -2443,16 +2606,16 @@ SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
            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;
            nBytes =
                FDH_PWRITE(fdP, (char *)&header.volumeInfo,
@@ -2483,19 +2646,20 @@ SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
     if (sp->inodeType == VI_VOLINFO) {
        salvinfo->VolInfo = header.volumeInfo;
        if (check) {
-           char update[25];
+           char update[64];
+           char buffer[64];
 
            if (salvinfo->VolInfo.updateDate) {
-               strcpy(update, TimeStamp(salvinfo->VolInfo.updateDate, 0));
+               strcpy(update, TimeStamp(buffer, sizeof(buffer), salvinfo->VolInfo.updateDate, 0));
                if (!Showmode)
-                   Log("%s (%u) %supdated %s\n", salvinfo->VolInfo.name,
-                       salvinfo->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(salvinfo->VolInfo.creationDate, 0));
+               strcpy(update, TimeStamp(buffer, sizeof(buffer), salvinfo->VolInfo.creationDate, 0));
                if (!Showmode)
-                   Log("%s (%u) not updated (created %s)\n",
-                       salvinfo->VolInfo.name, salvinfo->VolInfo.id, update);
+                   Log("%s (%" AFS_VOLID_FMT ") not updated (created %s)\n",
+                       salvinfo->VolInfo.name, afs_printable_VolumeId_lu(salvinfo->VolInfo.id), update);
            }
 
        }
@@ -2547,16 +2711,16 @@ SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
 
     IH_INIT(handle, salvinfo->fileSysDevice, volSummary->header.parent, ino);
     fdP = IH_OPEN(handle);
-    osi_Assert(fdP != NULL);
+    opr_Assert(fdP != NULL);
     file = FDH_FDOPEN(fdP, "r+");
-    osi_Assert(file != NULL);
+    opr_Assert(file != NULL);
     vcp = &VnodeClassInfo[class];
-    size = OS_SIZE(fdP->fd_fd);
-    osi_Assert(size != -1);
+    size = FDH_SIZE(fdP);
+    opr_Assert(size != -1);
     nVnodes = (size / vcp->diskSize) - 1;
     if (nVnodes > 0) {
-       osi_Assert((nVnodes + 1) * vcp->diskSize == size);
-       osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
+       opr_Assert((nVnodes + 1) * vcp->diskSize == size);
+       opr_Verify(STREAM_ASEEK(file, vcp->diskSize) == 0);
     } else {
        nVnodes = 0;
     }
@@ -2772,12 +2936,12 @@ SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
                }
            }                   /* VNDISK_GET_INO(vnode) != 0 */
          vnodeDone:
-           osi_Assert(!(vnodeChanged && check));
+           opr_Assert(!(vnodeChanged && check));
            if (vnodeChanged && !Testing) {
-               osi_Assert(IH_IWRITE
-                      (handle, vnodeIndexOffset(vcp, vnodeNumber),
-                       (char *)vnode, vcp->diskSize)
-                      == vcp->diskSize);
+               opr_Verify(IH_IWRITE(handle,
+                                    vnodeIndexOffset(vcp, vnodeNumber),
+                                    (char *)vnode, vcp->diskSize)
+                               == vcp->diskSize);
                salvinfo->VolumeChanged = 1;    /* For break call back */
            }
        }
@@ -2821,7 +2985,7 @@ CopyOnWrite(struct SalvInfo *salvinfo, struct DirSummary *dir)
        IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
                 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                 sizeof(vnode));
-    osi_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
@@ -2831,15 +2995,16 @@ CopyOnWrite(struct SalvInfo *salvinfo, struct DirSummary *dir)
        IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
                  dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
                  200);
-    osi_Assert(VALID_INO(newinode));
-    osi_Assert(CopyInode(salvinfo->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(salvinfo->vnodeInfo[vLarge].handle,
                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                  sizeof(vnode));
-    osi_Assert(code == sizeof(vnode));
+    opr_Assert(code == sizeof(vnode));
 
     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
                        salvinfo->fileSysDevice, newinode,
@@ -2877,7 +3042,7 @@ CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
        IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
                 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                 sizeof(vnode));
-    osi_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
@@ -2887,7 +3052,7 @@ CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
        IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
                  dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
                  200);
-    osi_Assert(VALID_INO(newinode));
+    opr_Assert(VALID_INO(newinode));
     SetSalvageDirHandle(&newdir, dir->rwVid, salvinfo->fileSysDevice, newinode,
                         &salvinfo->VolumeChanged);
 
@@ -2913,41 +3078,32 @@ CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
        if (code) {
            Log("also failed to decrement link count on new inode");
        }
-       osi_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);
-       osi_Assert(code == 0);
-       osi_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(salvinfo->vnodeInfo[vLarge].handle,
                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                  sizeof(vnode));
-    osi_Assert(lcode == sizeof(vnode));
-#if 0
-#ifdef AFS_NT40_ENV
-    nt_sync(salvinfo->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
-    salvinfo->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);
-    osi_Assert(code == 0);
+    opr_Assert(code == 0);
     dir->dirHandle = newdir;
 }
 
@@ -2978,7 +3134,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        }
        if (!Testing) {
            CopyOnWrite(salvinfo, dir);
-           osi_Assert(Delete(&dir->dirHandle, name) == 0);
+           opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
        }
        return 0;
     }
@@ -2992,7 +3148,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        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(salvinfo, dir);
-           osi_Assert(Delete(&dir->dirHandle, name) == 0);
+           opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
        }
        return 0;
     }
@@ -3010,7 +3166,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        if (!unique) {
            if (!Testing) {
                CopyOnWrite(salvinfo, dir);
-               osi_Assert(Delete(&dir->dirHandle, name) == 0);
+               opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
            }
            return 0;
        }
@@ -3021,49 +3177,59 @@ JudgeEntry(void *arock, 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" 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);
-           osi_Assert(Delete(&dir->dirHandle, name) == 0);
-           if (!todelete)
-               osi_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) {
+               AFSFid fid;
                CopyOnWrite(salvinfo, dir);
-               osi_Assert(Delete(&dir->dirHandle, ".") == 0);
+               opr_Verify(afs_dir_Delete(&dir->dirHandle, ".") == 0);
                fid.Vnode = dir->vnodeNumber;
                fid.Unique = dir->unique;
-               osi_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(salvinfo, vnodeNumber);
        }
        dir->haveDot = 1;
     } else if (strcmp(name, "..") == 0) {
@@ -3072,7 +3238,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
            struct VnodeEssence *dotdot;
            pa.Vnode = dir->parent;
            dotdot = CheckVnodeNumber(salvinfo, pa.Vnode);
-           osi_Assert(dotdot != NULL); /* XXX Should not be assert */
+           opr_Assert(dotdot != NULL); /* XXX Should not be assert */
            pa.Unique = dotdot->unique;
        } else {
            pa.Vnode = dir->vnodeNumber;
@@ -3083,8 +3249,8 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
                Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
            if (!Testing) {
                CopyOnWrite(salvinfo, dir);
-               osi_Assert(Delete(&dir->dirHandle, "..") == 0);
-               osi_Assert(Create(&dir->dirHandle, "..", &pa) == 0);
+               opr_Verify(afs_dir_Delete(&dir->dirHandle, "..") == 0);
+               opr_Verify(afs_dir_Create(&dir->dirHandle, "..", &pa) == 0);
            }
 
            vnodeNumber = pa.Vnode;     /* Get the new Essence */
@@ -3098,7 +3264,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        }
        if (!Testing) {
            CopyOnWrite(salvinfo, dir);
-           osi_Assert(Delete(&dir->dirHandle, name) == 0);
+           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 */
@@ -3136,14 +3302,15 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
            if (nBytes == size) {
                buf[size] = '\0';
                if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
-                   Log("Volume %u (%s) mount point %s" OS_DIRSEP "%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" OS_DIRSEP "%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);
@@ -3155,10 +3322,7 @@ JudgeEntry(void *arock, char *name, afs_int32 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
@@ -3192,7 +3356,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
                }
                if (!Testing) {
                    CopyOnWrite(salvinfo, dir);
-                   osi_Assert(Delete(&dir->dirHandle, name) == 0);
+                   opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
                }
                return 0;
            }
@@ -3220,20 +3384,21 @@ DistilVnodeEssence(struct SalvInfo *salvinfo, VolumeId rwVId,
 
     IH_INIT(vip->handle, salvinfo->fileSysDevice, rwVId, ino);
     fdP = IH_OPEN(vip->handle);
-    osi_Assert(fdP != NULL);
+    opr_Assert(fdP != NULL);
     file = FDH_FDOPEN(fdP, "r+");
-    osi_Assert(file != NULL);
-    size = OS_SIZE(fdP->fd_fd);
-    osi_Assert(size != -1);
+    opr_Assert(file != NULL);
+    size = FDH_SIZE(fdP);
+    opr_Assert(size != -1);
     vip->nVnodes = (size / vcp->diskSize) - 1;
     if (vip->nVnodes > 0) {
-       osi_Assert((vip->nVnodes + 1) * vcp->diskSize == size);
-       osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
-       osi_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) {
-           osi_Assert((vip->inodes = (Inode *)
-                   calloc(vip->nVnodes, sizeof(Inode))) != NULL);
+           opr_Verify((vip->inodes = calloc(vip->nVnodes, sizeof(Inode)))
+                           != NULL);
        } else {
            vip->inodes = NULL;
        }
@@ -3268,7 +3433,7 @@ DistilVnodeEssence(struct SalvInfo *salvinfo, VolumeId rwVId,
                if (class != vLarge) {
                    VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
                    vip->nAllocatedVnodes--;
-                   memset(vnode, 0, sizeof(vnode));
+                   memset(vnode, 0, sizeof(*vnode));
                    IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
                              vnodeIndexOffset(vcp, vnodeNumber),
                              (char *)&vnode, sizeof(vnode));
@@ -3391,7 +3556,8 @@ SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
        judge_params.salvinfo = salvinfo;
        judge_params.dir = &dir;
 
-       osi_Assert(EnumerateDir(&dirHandle, JudgeEntry, &judge_params) == 0);
+       opr_Verify(afs_dir_EnumerateDir(&dirHandle, JudgeEntry,
+                                       &judge_params) == 0);
     }
 
     /* Delete the old directory if it was copied in order to salvage.
@@ -3402,7 +3568,7 @@ SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
     DFlush();
     if (dir.copied && !Testing) {
        code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
-       osi_Assert(code == 0);
+       opr_Assert(code == 0);
        dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
     }
 
@@ -3629,7 +3795,7 @@ CreateRootDir(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
     Inode *ip;
     afs_sfsize_t bytes;
     struct VnodeEssence *vep;
-    Inode readmeinode;
+    Inode readmeinode = 0;
     time_t now = time(NULL);
 
     if (!salvinfo->vnodeInfo[vLarge].vnodes && !salvinfo->vnodeInfo[vSmall].vnodes) {
@@ -3644,8 +3810,8 @@ CreateRootDir(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
        salvinfo->vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
        salvinfo->vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
 
-       osi_Assert(salvinfo->vnodeInfo[vLarge].vnodes);
-       osi_Assert(salvinfo->vnodeInfo[vLarge].inodes);
+       opr_Assert(salvinfo->vnodeInfo[vLarge].vnodes);
+       opr_Assert(salvinfo->vnodeInfo[vLarge].inodes);
     }
 
     vep = &salvinfo->vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
@@ -3678,17 +3844,17 @@ CreateRootDir(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
     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);
@@ -3827,9 +3993,9 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
     vid = rwIsp->volSummary->header.id;
     IH_INIT(h, salvinfo->fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
-    osi_Assert(nBytes == sizeof(volHeader));
-    osi_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
-    osi_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(salvinfo, vid, vLarge,
@@ -3886,8 +4052,15 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
             * link count was not incremented in JudgeEntry().
             */
            if (class == vLarge) {      /* directory vnode */
-               pv = vnodeIdToBitNumber(vep->parent);
-               if (salvinfo->vnodeInfo[vLarge].vnodes[pv].unique != 0) {
+               struct VnodeEssence *parent_vep;
+
+               parent_vep = CheckVnodeNumber(salvinfo, vep->parent);
+
+               if (!parent_vep)
+                   Log("Vnode %d has invalid or out-of-range parent vnode %d;" \
+                       " ignore parent count adjustment\n",
+                       ThisVnode, vep->parent);
+               else if (parent_vep->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
@@ -3898,7 +4071,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                         /* noop */
 
                    } else {
-                       salvinfo->vnodeInfo[vLarge].vnodes[pv].count++;
+                       parent_vep->count++;
                    }
                }
            }
@@ -3938,8 +4111,8 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                                        &salvinfo->VolumeChanged);
                    pa.Vnode = LFVnode;
                    pa.Unique = LFUnique;
-                   osi_Assert(Delete(&dh, "..") == 0);
-                   osi_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.
@@ -3962,13 +4135,13 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                             ThisVnode, ThisUnique);
 
                    CopyOnWrite(salvinfo, &rootdir);
-                   code = Create(&rootdir.dirHandle, npath, &pa);
+                   code = afs_dir_Create(&rootdir.dirHandle, npath, &pa);
                    if (!code)
                        break;
 
                    ThisUnique += 50;   /* Try creating a different file */
                }
-               osi_Assert(code == 0);
+               opr_Assert(code == 0);
                Log("Attaching orphaned %s to volume's root dir as %s\n",
                    ((class == vLarge) ? "directory" : "file"), npath);
            }
@@ -3981,7 +4154,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
        code =
            IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
                   oldrootdir.rwVid);
-       osi_Assert(code == 0);
+       opr_Assert(code == 0);
        /* dirVnodeInfo->inodes[?] is not updated with new inode number */
     }
 
@@ -4019,7 +4192,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                    IH_IREAD(salvinfo->vnodeInfo[class].handle,
                             vnodeIndexOffset(vcp, vnodeNumber),
                             (char *)&vnode, sizeof(vnode));
-               osi_Assert(nBytes == sizeof(vnode));
+               opr_Assert(nBytes == sizeof(vnode));
 
                vnode.parent = vnp->parent;
                oldCount = vnode.linkCount;
@@ -4030,7 +4203,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                if (orphaned) {
                    if (!vnp->todelete) {
                        /* Orphans should have already been attached (if requested) */
-                       osi_Assert(orphans != ORPH_ATTACH);
+                       opr_Assert(orphans != ORPH_ATTACH);
                        oblocks += vnp->blockCount;
                        ofiles++;
                    }
@@ -4041,7 +4214,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                        if (VNDISK_GET_INO(&vnode)) {
                            code =
                                IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
-                           osi_Assert(code == 0);
+                           opr_Assert(code == 0);
                        }
                        memset(&vnode, 0, sizeof(vnode));
                    }
@@ -4059,7 +4232,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                        IH_IWRITE(salvinfo->vnodeInfo[class].handle,
                                  vnodeIndexOffset(vcp, vnodeNumber),
                                  (char *)&vnode, sizeof(vnode));
-                   osi_Assert(nBytes == sizeof(vnode));
+                   opr_Assert(nBytes == sizeof(vnode));
                }
                salvinfo->VolumeChanged = 1;
            }
@@ -4090,7 +4263,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
     /* 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
         */
@@ -4104,36 +4277,60 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
            afs_printable_uint32_lu(vid));
     }
 
+    if (!Testing && salvinfo->VolumeChanged) {
 #ifdef FSSYNC_BUILD_CLIENT
-    if (!Testing && salvinfo->VolumeChanged && 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;
+       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 = (salvinfo->VolumeChanged != 0);
+    if (salvinfo->VolumeChanged) {
+       volHeader.needsCallback = 1;
+       volHeader.updateDate = time(NULL);
+    } else {
+       volHeader.needsCallback = 0;
+    }
     volHeader.dontSalvage = DONT_SALVAGE;
     salvinfo->VolumeChanged = 0;
     if (!Testing) {
        nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
-       osi_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);
     }
 
@@ -4152,15 +4349,15 @@ ClearROInUseBit(struct VolumeSummary *summary)
     VolumeDiskData volHeader;
 
     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
-    osi_Assert(nBytes == sizeof(volHeader));
-    osi_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));
-       osi_Assert(nBytes == sizeof(volHeader));
+       opr_Assert(nBytes == sizeof(volHeader));
     }
 }
 
@@ -4174,31 +4371,33 @@ 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" OS_DIRSEP "%s", salvinfo->fileSysPath, isp->volSummary->fileName);
+               char path[VMAXPATHLEN + 10];
+               char filename[VMAXPATHLEN];
+               VolumeExternalName_r(isp->volumeId, filename, sizeof(filename));
+               sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, filename);
 
                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) {
@@ -4211,13 +4410,13 @@ MaybeZapVolume(struct SalvInfo *salvinfo, struct InodeSummary *isp,
            }
        }
     } 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));
     }
 }
 
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#ifdef AFS_DEMAND_ATTACH_FS
 /**
  * Locks a volume on disk for salvaging.
  *
@@ -4252,7 +4451,7 @@ LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId)
              afs_printable_uint32_lu(volumeId));
     }
 
-    code = FSYNC_VerifyCheckout(volumeId, salvinfo->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;
@@ -4296,14 +4495,30 @@ LockVolume(struct SalvInfo *salvinfo, 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. */
-       osi_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);
     }
 
     return 0;
 }
+#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(struct SalvInfo *salvinfo, VolumeId volumeId)
@@ -4329,13 +4544,13 @@ AskOffline(struct SalvInfo *salvinfo, VolumeId volumeId)
            Log("AskOffline:  fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
                FSYNC_VOL_OFF);
            if (AskDAFS()) {
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#ifdef AFS_DEMAND_ATTACH_FS
                Log("AskOffline:  please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
 #else
                Log("AskOffline:  fileserver is DAFS but we are not.\n");
 #endif
            } else {
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#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");
@@ -4360,8 +4575,8 @@ static int isDAFS = -1;
 int
 AskDAFS(void)
 {
-    afs_int32 code, i, ret = 0;
     SYNC_response res;
+    afs_int32 code = 1, i;
 
     /* we don't care if we race. the answer shouldn't change */
     if (isDAFS != -1)
@@ -4369,35 +4584,29 @@ AskDAFS(void)
 
     memset(&res, 0, sizeof(res));
 
-    for (i = 0; i < 3; i++) {
-       code = FSYNC_VolOp(1, NULL,
-                          FSYNC_VOL_QUERY_VOP, FSYNC_SALVAGE, &res);
-
-       if (code == SYNC_OK) {
-           ret = 1;
-           break;
-       } else if (code == SYNC_DENIED) {
-           ret = 1;
-           break;
-       } else if (code == SYNC_BAD_COMMAND) {
-           ret = 0;
-           break;
-       } else if (code == SYNC_FAILED) {
-           if (res.hdr.reason == FSYNC_UNKNOWN_VOLID)
-               ret = 1;
-           else
-               ret = 0;
-           break;
-       } else if (i < 2) {
-           /* try it again */
-           Log("AskDAFS:  request to query fileserver failed; trying again...\n");
+    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();
        }
     }
 
-    isDAFS = ret;
-    return ret;
+    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
@@ -4425,7 +4634,7 @@ AskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
        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, salvinfo->fileSysPartition->name);
+           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);
@@ -4454,18 +4663,18 @@ AskDelete(struct SalvInfo *salvinfo, VolumeId volumeId)
        if (code == SYNC_OK) {
            break;
        } else if (code == SYNC_DENIED) {
-           Log("AskOnline:  file server denied DONE request to volume %u partition %s; trying again...\n", volumeId, salvinfo->fileSysPartition->name);
+           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()) {
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#ifdef AFS_DEMAND_ATTACH_FS
                Log("AskOnline:  please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
 #else
                Log("AskOnline:  fileserver is DAFS but we are not.\n");
 #endif
            } else {
-#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+#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");
@@ -4500,14 +4709,14 @@ CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
 
     IH_INIT(srcH, device, rwvolume, inode1);
     srcFdP = IH_OPEN(srcH);
-    osi_Assert(srcFdP != NULL);
+    opr_Assert(srcFdP != NULL);
     IH_INIT(destH, device, rwvolume, inode2);
     destFdP = IH_OPEN(destH);
     while ((nBytes = FDH_PREAD(srcFdP, buf, sizeof(buf), size)) > 0) {
-       osi_Assert(FDH_PWRITE(destFdP, buf, nBytes, size) == nBytes);
+       opr_Verify(FDH_PWRITE(destFdP, buf, nBytes, size) == nBytes);
        size += nBytes;
     }
-    osi_Assert(nBytes == 0);
+    opr_Assert(nBytes == 0);
     FDH_REALLYCLOSE(srcFdP);
     FDH_REALLYCLOSE(destFdP);
     IH_RELEASE(srcH);
@@ -4525,15 +4734,16 @@ PrintInodeList(struct SalvInfo *salvinfo)
     afs_sfsize_t st_size;
 
     st_size = OS_SIZE(salvinfo->inodeFd);
-    osi_Assert(st_size >= 0);
-    buf = (struct ViceInodeInfo *)malloc(st_size);
-    osi_Assert(buf != NULL);
+    opr_Assert(st_size >= 0);
+    buf = malloc(st_size);
+    opr_Assert(buf != NULL);
     nInodes = st_size / sizeof(struct ViceInodeInfo);
-    osi_Assert(OS_READ(salvinfo->inodeFd, buf, st_size) == st_size);
+    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",
+       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, ip->u.param[0], ip->u.param[1],
+           (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);
@@ -4547,18 +4757,7 @@ PrintInodeSummary(struct SalvInfo *salvinfo)
 
     for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
        isp = &salvinfo->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(struct SalvInfo *salvinfo)
-{
-    int i;
-    struct VolumeSummary *vsp;
-
-    for (i = 0, vsp = salvinfo->volumeSummaryp; i < salvinfo->nVolumes; vsp++, i++) {
-       Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
+       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);
     }
 }
 
@@ -4568,10 +4767,10 @@ Fork(void)
     int f;
 #ifdef AFS_NT40_ENV
     f = 0;
-    osi_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();
-    osi_Assert(f >= 0);
+    opr_Assert(f >= 0);
 #ifdef AFS_DEMAND_ATTACH_FS
     if ((f == 0) && (programType == salvageServer)) {
        /* we are a salvageserver child */
@@ -4587,20 +4786,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 */
 
@@ -4614,13 +4822,21 @@ Exit(int code)
 #endif
 }
 
+void
+Exit(int code)
+{
+    SalvageShowLog();
+    QuietExit(code);
+}
+
+
 int
 Wait(char *prog)
 {
     int status;
     int pid;
     pid = wait(&status);
-    osi_Assert(pid != -1);
+    opr_Assert(pid != -1);
     if (WCOREDUMP(status))
        Log("\"%s\" core dumped!\n", prog);
     if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
@@ -4629,152 +4845,92 @@ 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;
-}
-
-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
-    }
+       nbytes = strftime(buffer, size, "%m/%d/%Y %H:%M", lt);
+    if (nbytes == 0)
+       memset(buffer, 0, size);
+    return buffer;
 }
 
-#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);
-    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];
+    char *filename;
+    FILE *logFile;
 
-#ifndef AFS_NT40_ENV
-    if (useSyslog) {
-       printf("Can't show log since using syslog.\n");
-       fflush(stdout);
-       return;
-    }
-#endif
-
-    if (logFile) {
-       rewind(logFile);
-       fclose(logFile);
+    if (ShowLog == 0 || ClientMode) {
+       return; /* nothing to do */
     }
+    filename = strdup(GetLogFilename());
+    opr_Assert(filename != NULL);
+    CloseLog();
 
-    logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
-
+    logFile = afs_fopen(filename, "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);
     }
+    free(filename);
+}
+
+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);
-    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);
-    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)
 {
     char *p;
-    p = (char *)malloc(strlen(s) + 1);
-    osi_Assert(p != NULL);
-    strcpy(p, s);
+    p = strdup(s);
+    opr_Assert(p != NULL);
     return p;
 }
 
@@ -4851,7 +5007,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;
@@ -4860,11 +5016,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;
 }