vol: Call memset with the correct size
[openafs.git] / src / vol / vol-salvage.c
index 148879a..0b1fa26 100644 (file)
@@ -86,23 +86,14 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <afsconfig.h>
 #include <afs/param.h>
 
+#include <afs/procmgmt.h>
+#include <roken.h>
 
-#ifndef AFS_NT40_ENV
-#include <sys/param.h>
-#include <sys/file.h>
-#ifndef ITIMER_REAL
-#include <sys/time.h>
-#endif /* ITIMER_REAL */
+#ifdef HAVE_SYS_FILE_H
+# include <sys/file.h>
 #endif
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <time.h>
-#include <errno.h>
+
 #ifdef AFS_NT40_ENV
-#include <io.h>
 #include <WINNT/afsevent.h>
 #endif
 #ifndef WCOREDUMP
@@ -110,7 +101,6 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #endif
 #include <rx/xdr.h>
 #include <afs/afsint.h>
-#include <afs/assert.h>
 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
 #if defined(AFS_VFSINCL_ENV)
 #include <sys/vnode.h>
@@ -128,7 +118,7 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #ifdef AFS_OSF_ENV
 #include <ufs/inode.h>
 #else /* AFS_OSF_ENV */
-#if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
+#if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_DARWIN_ENV)
 #include <sys/inode.h>
 #endif
 #endif
@@ -139,17 +129,13 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <sys/lockf.h>
 #else
 #ifdef AFS_HPUX_ENV
-#include <unistd.h>
 #include <checklist.h>
 #else
 #if defined(AFS_SGI_ENV)
-#include <unistd.h>
-#include <fcntl.h>
 #include <mntent.h>
 #else
 #if    defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
 #ifdef   AFS_SUN5_ENV
-#include <unistd.h>
 #include <sys/mnttab.h>
 #include <sys/mntent.h>
 #else
@@ -160,7 +146,6 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #endif /* AFS_HPUX_ENV */
 #endif
 #endif
-#include <fcntl.h>
 #ifndef AFS_NT40_ENV
 #include <afs/osi_inode.h>
 #endif
@@ -168,10 +153,6 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <afs/dir.h>
 #include <afs/afsutil.h>
 #include <afs/fileutil.h>
-#include <afs/procmgmt.h>      /* signal(), kill(), wait(), etc. */
-#ifndef AFS_NT40_ENV
-#include <syslog.h>
-#endif
 
 #include "nfs.h"
 #include "lwp.h"
@@ -182,7 +163,9 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include "volume.h"
 #include "partition.h"
 #include "daemon_com.h"
+#include "daemon_com_inline.h"
 #include "fssync.h"
+#include "fssync_inline.h"
 #include "volume_inline.h"
 #include "salvsync.h"
 #include "viceinode.h"
@@ -202,28 +185,6 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <pthread.h>
 #endif
 
-/*@+fcnmacros +macrofcndecl@*/
-#ifdef O_LARGEFILE
-#ifdef S_SPLINT_S
-extern off64_t afs_lseek(int FD, off64_t O, int F);
-#endif /*S_SPLINT_S */
-#define afs_lseek(FD, O, F)    lseek64(FD, (off64_t) (O), F)
-#define afs_stat       stat64
-#define afs_fstat      fstat64
-#define afs_open       open64
-#define afs_fopen      fopen64
-#else /* !O_LARGEFILE */
-#ifdef S_SPLINT_S
-extern off_t afs_lseek(int FD, off_t O, int F);
-#endif /*S_SPLINT_S */
-#define afs_lseek(FD, O, F)    lseek(FD, (off_t) (O), F)
-#define afs_stat       stat
-#define afs_fstat      fstat
-#define afs_open       open
-#define afs_fopen      fopen
-#endif /* !O_LARGEFILE */
-/*@=fcnmacros =macrofcndecl@*/
-
 #ifdef AFS_OSF_ENV
 extern void *calloc();
 #endif
@@ -272,7 +233,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. */
@@ -294,7 +255,7 @@ struct SalvInfo {
                               *   header dealt with */
 
     int nVolumesInInodeFile; /**< Number of read-write volumes summarized */
-    int inodeFd;             /**< File descriptor for inode file */
+    FD_t inodeFd;             /**< File descriptor for inode file */
 
     struct VolumeSummary *volumeSummaryp; /**< Holds all the volumes in a part */
     int nVolumes;            /**< Number of volumes (read-write and read-only)
@@ -306,6 +267,8 @@ struct SalvInfo {
                                                 *   vnodes in the volume that
                                                 *   we are currently looking
                                                 *   at */
+    int useFSYNC; /**< 0 if the fileserver is unavailable; 1 if we should try
+                   *   to contact the fileserver over FSYNC */
 };
 
 char *tmpdir = NULL;
@@ -316,10 +279,12 @@ char *tmpdir = NULL;
 static int IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode);
 static int AskVolumeSummary(struct SalvInfo *salvinfo,
                             VolumeId singleVolumeNumber);
+static void MaybeAskOnline(struct SalvInfo *salvinfo, VolumeId volumeId);
+static void AskError(struct SalvInfo *salvinfo, VolumeId volumeId);
 
-#ifdef AFS_DEMAND_ATTACH_FS
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
 static int LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId);
-#endif /* AFS_DEMAND_ATTACH_FS */
+#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
 
 /* Uniquifier stored in the Inode */
 static Unique
@@ -402,7 +367,7 @@ IsPartitionMounted(char *part)
     FILE *mntfp;
     struct mntent *mntent;
 
-    assert(mntfp = setmntent(MOUNTED, "r"));
+    osi_Assert(mntfp = setmntent(MOUNTED, "r"));
     while (mntent = getmntent(mntfp)) {
        if (!strcmp(part, mntent->mnt_dir))
            break;
@@ -415,7 +380,7 @@ IsPartitionMounted(char *part)
 /* Check if the given inode is the root of the filesystem. */
 #ifndef AFS_SGI_XFS_IOPS_ENV
 int
-IsRootInode(struct afs_stat *status)
+IsRootInode(struct afs_stat_st *status)
 {
     /*
      * The root inode is not a fixed value in XFS partitions. So we need to
@@ -465,35 +430,31 @@ int
 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
 {
 #define RES_LEN 256
-    char res[RES_LEN];
-    int d1, d2;
+    char res1[RES_LEN];
+    char res2[RES_LEN];
+
     static int dowarn = 1;
 
-    if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
+    if (!QueryDosDevice(p1->devName, res1, RES_LEN - 1))
        return 1;
-    if (strncmp(res, HDSTR, HDLEN)) {
+    if (strncmp(res1, HDSTR, HDLEN)) {
        if (dowarn) {
            dowarn = 0;
            Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
-               res, HDSTR, p1->devName);
+               res1, HDSTR, p1->devName);
        }
-       return 1;
     }
-    d1 = atoi(&res[HDLEN]);
-
-    if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
+    if (!QueryDosDevice(p2->devName, res2, RES_LEN - 1))
        return 1;
-    if (strncmp(res, HDSTR, HDLEN)) {
+    if (strncmp(res2, HDSTR, HDLEN)) {
        if (dowarn) {
            dowarn = 0;
            Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
-               res, HDSTR, p2->devName);
+               res2, HDSTR, p2->devName);
        }
-       return 1;
     }
-    d2 = atoi(&res[HDLEN]);
 
-    return d1 == d2;
+    return (0 == _strnicmp(res1, res2, RES_LEN - 1));
 }
 #else
 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
@@ -581,12 +542,12 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
             * job to finish. When it's done, clean up after it.
             */
            pid = wait(&wstatus);
-           assert(pid != -1);
+           osi_Assert(pid != -1);
            for (j = 0; j < numjobs; j++) {     /* Find which job it is */
                if (pid == jobs[j]->pid)
                    break;
            }
-           assert(j < numjobs);
+           osi_Assert(j < numjobs);
            if (WCOREDUMP(wstatus)) {   /* Say if the job core dumped */
                Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
            }
@@ -646,7 +607,7 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
                ShowLog = 0;
                for (fd = 0; fd < 16; fd++)
                    close(fd);
-               open("/", 0);
+               open(OS_DIRSEP, 0);
                dup2(0, 1);
                dup2(0, 2);
 #ifndef AFS_NT40_ENV
@@ -655,10 +616,9 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
                } else
 #endif
                {
-                   (void)afs_snprintf(logFileName, sizeof logFileName,
-                                      "%s.%d",
-                                      AFSDIR_SERVER_SLVGLOG_FILEPATH,
-                                      jobs[startjob]->jobnumb);
+                   snprintf(logFileName, sizeof logFileName, "%s.%d",
+                            AFSDIR_SERVER_SLVGLOG_FILEPATH,
+                            jobs[startjob]->jobnumb);
                    logFile = afs_fopen(logFileName, "w");
                }
                if (!logFile)
@@ -677,8 +637,8 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
 #endif
        if (!partP) {
            for (i = 0; i < jobcount; i++) {
-               (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
-                                  AFSDIR_SERVER_SLVGLOG_FILEPATH, 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);
@@ -711,13 +671,13 @@ get_DevName(char *pbuffer, char *wpath)
 {
     char pbuf[128], *ptr;
     strcpy(pbuf, pbuffer);
-    ptr = (char *)strrchr(pbuf, '/');
+    ptr = (char *)strrchr(pbuf, OS_DIRSEPC);
     if (ptr) {
        *ptr = '\0';
        strcpy(wpath, pbuf);
     } else
        return NULL;
-    ptr = (char *)strrchr(pbuffer, '/');
+    ptr = (char *)strrchr(pbuffer, OS_DIRSEPC);
     if (ptr) {
        strcpy(pbuffer, ptr + 1);
        return pbuffer;
@@ -730,7 +690,7 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 {
     char *name, *tdir;
     char inodeListPath[256];
-    FILE *inodeFile = NULL;
+    FD_t inodeFile = INVALID_FD;
     static char tmpDevName[100];
     static char wpath[100];
     struct VolumeSummary *vsp, *esp;
@@ -744,21 +704,21 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
     memset(salvinfo, 0, sizeof(*salvinfo));
 
     tries++;
-    if (inodeFile) {
-       fclose(inodeFile);
-       inodeFile = NULL;
+    if (inodeFile != INVALID_FD) {
+       OS_CLOSE(inodeFile);
+       inodeFile = INVALID_FD;
     }
     if (tries > VOL_MAX_CHECKOUT_RETRIES) {
        Abort("Raced too many times with fileserver restarts while trying to "
              "checkout/lock volumes; Aborted\n");
     }
-#ifdef AFS_DEMAND_ATTACH_FS
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
     if (tries > 1) {
        /* unlock all previous volume locks, since we're about to lock them
         * again */
        VLockFileReinit(&partP->volLockFile);
     }
-#endif /* AFS_DEMAND_ATTACH_FS */
+#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
 
     salvinfo->fileSysPartition = partP;
     salvinfo->fileSysDevice = salvinfo->fileSysPartition->device;
@@ -766,7 +726,7 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 
 #ifdef AFS_NT40_ENV
     /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
-    (void)sprintf(salvinfo->fileSysPath, "%s\\", salvinfo->fileSysPathName);
+    (void)sprintf(salvinfo->fileSysPath, "%s" OS_DIRSEP, salvinfo->fileSysPathName);
     name = partP->devName;
 #else
     strlcpy(salvinfo->fileSysPath, salvinfo->fileSysPathName, sizeof(salvinfo->fileSysPath));
@@ -777,11 +737,11 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 #endif
 
     if (singleVolumeNumber) {
-#ifndef AFS_DEMAND_ATTACH_FS
+#if !(defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL))
        /* 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 */
+#endif /* !(AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL) */
 
        ForceSalvage = 1;
 
@@ -790,14 +750,16 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
            Abort("Couldn't connect to file server\n");
        }
 
+       salvinfo->useFSYNC = 1;
        AskOffline(salvinfo, singleVolumeNumber);
-#ifdef AFS_DEMAND_ATTACH_FS
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
        if (LockVolume(salvinfo, singleVolumeNumber)) {
            goto retry;
        }
-#endif /* AFS_DEMAND_ATTACH_FS */
+#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
 
     } else {
+       salvinfo->useFSYNC = 0;
        VLockPartition(partP->name);
        if (ForceSalvage) {
            ForceSalvage = 1;
@@ -820,16 +782,16 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
        DIR *dirp;
        struct dirent *dp;
 
-       assert((dirp = opendir(salvinfo->fileSysPath)) != NULL);
+       osi_Assert((dirp = opendir(salvinfo->fileSysPath)) != NULL);
        while ((dp = readdir(dirp))) {
            if (!strncmp(dp->d_name, "salvage.inodes.", 15)
                || !strncmp(dp->d_name, "salvage.temp.", 13)) {
                char npath[1024];
                Log("Removing old salvager temp files %s\n", dp->d_name);
                strcpy(npath, salvinfo->fileSysPath);
-               strcat(npath, "/");
+               strcat(npath, OS_DIRSEP);
                strcat(npath, dp->d_name);
-               unlink(npath);
+               OS_UNLINK(npath);
            }
        }
        closedir(dirp);
@@ -839,12 +801,12 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
     (void)_putenv("TMP=");     /* If "TMP" is set, then that overrides tdir. */
     (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
 #else
-    snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
+    snprintf(inodeListPath, 255, "%s" OS_DIRSEP "salvage.inodes.%s.%d", tdir, name,
             getpid());
 #endif
 
-    inodeFile = fopen(inodeListPath, "w+b");
-    if (!inodeFile) {
+    inodeFile = OS_OPEN(inodeListPath, O_RDWR|O_TRUNC|O_CREAT, 0666);
+    if (inodeFile == INVALID_FD) {
        Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
     }
 #ifdef AFS_NT40_ENV
@@ -852,6 +814,12 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
      * semantics of unlink. In most places in the salvager, we really do
      * mean to unlink the file at that point. Those places have been
      * modified to actually do that so that the NT crt can be used there.
+     *
+     * jaltman - On NT delete on close cannot be applied to a file while the
+     * process has an open file handle that does not have DELETE file
+     * access and FILE_SHARE_DELETE.  fopen() calls CreateFile() without
+     * delete privileges.  As a result the nt_unlink() call will always
+     * fail.
      */
     code = nt_unlink(inodeListPath);
 #else
@@ -862,15 +830,23 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
     }
 
     if (GetInodeSummary(salvinfo, inodeFile, singleVolumeNumber) < 0) {
-       fclose(inodeFile);
+       OS_CLOSE(inodeFile);
        return;
     }
-    salvinfo->inodeFd = fileno(inodeFile);
-    if (salvinfo->inodeFd == -1)
+    salvinfo->inodeFd = inodeFile;
+    if (salvinfo->inodeFd == INVALID_FD)
        Abort("Temporary file %s is missing...\n", inodeListPath);
-    afs_lseek(salvinfo->inodeFd, 0L, SEEK_SET);
+    OS_SEEK(salvinfo->inodeFd, 0L, SEEK_SET);
     if (ListInodeOption) {
        PrintInodeList(salvinfo);
+       if (singleVolumeNumber) {
+           /* We've checked out the volume from the fileserver, and we need
+            * to give it back. We don't know if the volume exists or not,
+            * so we don't know whether to AskOnline or not. Try to determine
+            * if the volume exists by trying to read the volume header, and
+            * AskOnline if it is readable. */
+           MaybeAskOnline(salvinfo, singleVolumeNumber);
+       }
        return;
     }
     /* enumerate volumes in the partition.
@@ -883,6 +859,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;
@@ -896,7 +882,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
@@ -905,7 +891,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;
                }
            }
@@ -913,12 +899,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);
     }
 
@@ -926,18 +917,43 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
        RemoveTheForce(salvinfo->fileSysPath);
 
     if (!Testing && singleVolumeNumber) {
-#ifdef AFS_DEMAND_ATTACH_FS
+       int foundSVN = 0;
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
        /* unlock vol headers so the fs can attach them when we AskOnline */
        VLockFileReinit(&salvinfo->fileSysPartition->volLockFile);
-#endif /* AFS_DEMAND_ATTACH_FS */
-
-       AskOnline(salvinfo, singleVolumeNumber);
+#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
 
        /* Step through the volumeSummary list and set all volumes on-line.
-        * The volumes were taken off-line in GetVolumeSummary.
+        * Most volumes were taken off-line in GetVolumeSummary.
+        * If a volume was deleted, don't tell the fileserver anything, since
+        * we already told the fileserver the volume was deleted back when we
+        * we destroyed the volume header.
+        * Also, make sure we bring the singleVolumeNumber back online first.
         */
+
+       for (j = 0; j < salvinfo->nVolumes; j++) {
+           if (salvinfo->volumeSummaryp[j].header.id == singleVolumeNumber) {
+               foundSVN = 1;
+               if (!salvinfo->volumeSummaryp[j].deleted) {
+                   AskOnline(salvinfo, singleVolumeNumber);
+               }
+           }
+       }
+
+       if (!foundSVN) {
+           /* If singleVolumeNumber is not in our volumeSummary, it means that
+            * at least one other volume in the VG is on the partition, but the
+            * RW volume is not. We've already AskOffline'd it by now, though,
+            * so make sure we don't still have the volume checked out. */
+           AskDelete(salvinfo, singleVolumeNumber);
+       }
+
        for (j = 0; j < salvinfo->nVolumes; j++) {
-           AskOnline(salvinfo, salvinfo->volumeSummaryp[j].header.id);
+           if (salvinfo->volumeSummaryp[j].header.id != singleVolumeNumber) {
+               if (!salvinfo->volumeSummaryp[j].deleted) {
+                   AskOnline(salvinfo, salvinfo->volumeSummaryp[j].header.id);
+               }
+           }
        }
     } else {
        if (!Showmode)
@@ -945,14 +961,21 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
                salvinfo->fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
     }
 
-    fclose(inodeFile);         /* SalvageVolumeGroup was the last which needed it. */
+    OS_CLOSE(inodeFile);               /* SalvageVolumeGroup was the last which needed it. */
 }
 
 void
 DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp)
 {
     char path[64];
-    sprintf(path, "%s/%s", salvinfo->fileSysPath, vsp->fileName);
+    char filename[VMAXPATHLEN];
+
+    if (vsp->deleted) {
+       return;
+    }
+
+    VolumeExternalName_r(vsp->header.id, filename, sizeof(filename));
+    sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, filename);
 
     if (!Showmode)
        Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
@@ -965,14 +988,17 @@ DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp
                afs_printable_uint32_lu(vsp->header.id));
        }
 
-       /* make sure we actually delete the fileName file; ENOENT
+       /* make sure we actually delete the header file; ENOENT
         * is fine, since VDestroyVolumeDiskHeader probably already
         * unlinked it */
        if (unlink(path) && errno != ENOENT) {
            Log("Unable to unlink %s (errno = %d)\n", path, errno);
        }
+       if (salvinfo->useFSYNC) {
+           AskDelete(salvinfo, vsp->header.id);
+       }
+       vsp->deleted = 1;
     }
-    vsp->fileName = 0;
 }
 
 int
@@ -1117,15 +1143,14 @@ OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, vo
  * be unlinked by the caller.
  */
 int
-GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolumeNumber)
+GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolumeNumber)
 {
-    struct afs_stat status;
     int forceSal, err;
     int code;
     struct ViceInodeInfo *ip, *ip_save;
     struct InodeSummary summary;
     char summaryFileName[50];
-    FILE *summaryFile;
+    FD_t summaryFile = INVALID_FD;
 #ifdef AFS_NT40_ENV
     char *dev = salvinfo->fileSysPath;
     char *wpath = salvinfo->fileSysPath;
@@ -1136,6 +1161,9 @@ GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolum
     char *part = salvinfo->fileSysPath;
     char *tdir;
     int i;
+    int retcode = 0;
+    int deleted = 0;
+    afs_sfsize_t st_size;
 
     /* This file used to come from vfsck; cobble it up ourselves now... */
     if ((err =
@@ -1144,7 +1172,8 @@ GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolum
                        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);
     }
@@ -1152,21 +1181,22 @@ GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolum
        Log("***Forced salvage of all volumes on this partition***\n");
        ForceSalvage = 1;
     }
-    fseek(inodeFile, 0L, SEEK_SET);
-    salvinfo->inodeFd = fileno(inodeFile);
-    if (salvinfo->inodeFd == -1 || afs_fstat(salvinfo->inodeFd, &status) == -1) {
+    OS_SEEK(inodeFile, 0L, SEEK_SET);
+    salvinfo->inodeFd = inodeFile;
+    if (salvinfo->inodeFd == INVALID_FD ||
+        (st_size = OS_SIZE(salvinfo->inodeFd)) == -1) {
        Abort("No inode description file for \"%s\"; not salvaged\n", dev);
     }
     tdir = (tmpdir ? tmpdir : part);
 #ifdef AFS_NT40_ENV
     (void)_putenv("TMP=");     /* If "TMP" is set, then that overrides tdir. */
-    (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
+    (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp."));
 #else
-    (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
-                      "%s/salvage.temp.%d", tdir, getpid());
+    snprintf(summaryFileName, sizeof summaryFileName,
+            "%s" OS_DIRSEP "salvage.temp.%d", tdir, getpid());
 #endif
-    summaryFile = afs_fopen(summaryFileName, "a+");
-    if (summaryFile == NULL) {
+    summaryFile = OS_OPEN(summaryFileName, O_RDWR|O_APPEND|O_CREAT, 0666);
+    if (summaryFile == INVALID_FD) {
        Abort("Unable to create inode summary file\n");
     }
 
@@ -1175,6 +1205,9 @@ GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolum
      * semantics of unlink. In most places in the salvager, we really do
      * mean to unlink the file at that point. Those places have been
      * modified to actually do that so that the NT crt can be used there.
+     *
+     * jaltman - As commented elsewhere, this cannot work because fopen()
+     * does not open files with DELETE and FILE_SHARE_DELETE.
      */
     code = nt_unlink(summaryFileName);
 #else
@@ -1185,53 +1218,73 @@ GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolum
     }
 
     if (!canfork || debug || Fork() == 0) {
-       int nInodes;
-       unsigned long st_size=(unsigned long) status.st_size;
-       nInodes = st_size / sizeof(struct ViceInodeInfo);
+       int nInodes = st_size / sizeof(struct ViceInodeInfo);
        if (nInodes == 0) {
-           fclose(summaryFile);
+           OS_CLOSE(summaryFile);
            if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
                RemoveTheForce(salvinfo->fileSysPath);
            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));
        if (ip == NULL) {
-           fclose(summaryFile);
+           OS_CLOSE(summaryFile);
            Abort
                ("Unable to allocate enough space to read inode table; %s not salvaged\n",
                 dev);
        }
-       if (read(salvinfo->inodeFd, ip, st_size) != st_size) {
-           fclose(summaryFile);
+       if (OS_READ(salvinfo->inodeFd, ip, st_size) != st_size) {
+           OS_CLOSE(summaryFile);
            Abort("Unable to read inode table; %s not salvaged\n", dev);
        }
        qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
-       if (afs_lseek(salvinfo->inodeFd, 0, SEEK_SET) == -1
-           || write(salvinfo->inodeFd, ip, st_size) != st_size) {
-           fclose(summaryFile);
+       if (OS_SEEK(salvinfo->inodeFd, 0, SEEK_SET) == -1
+           || OS_WRITE(salvinfo->inodeFd, ip, st_size) != st_size) {
+           OS_CLOSE(summaryFile);
            Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
        }
        summary.index = 0;
        ip_save = ip;
        while (nInodes) {
            CountVolumeInodes(ip, nInodes, &summary);
-           if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
+           if (OS_WRITE(summaryFile, &summary, sizeof(summary)) != sizeof(summary)) {
                Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
-               fclose(summaryFile);
-               return -1;
+               OS_CLOSE(summaryFile);
+               retcode = -1;
+               goto error;
            }
            summary.index += (summary.nInodes);
            nInodes -= summary.nInodes;
@@ -1240,10 +1293,11 @@ GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolum
        free(ip_save);
        ip = ip_save = NULL;
        /* Following fflush is not fclose, because if it was debug mode would not work */
-       if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
+       if (OS_SYNC(summaryFile) == -1) {
            Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
-           fclose(summaryFile);
-           return -1;
+           OS_CLOSE(summaryFile);
+           retcode = -1;
+           goto error;
        }
        if (canfork && !debug) {
            ShowLog = 0;
@@ -1251,28 +1305,35 @@ GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolum
        }
     } else {
        if (Wait("Inode summary") == -1) {
-           fclose(summaryFile);
+           OS_CLOSE(summaryFile);
            Exit(1);            /* salvage of this partition aborted */
        }
     }
-    assert(afs_fstat(fileno(summaryFile), &status) != -1);
-    if (status.st_size != 0) {
+
+    st_size = OS_SIZE(summaryFile);
+    osi_Assert(st_size >= 0);
+    if (st_size != 0) {
        int ret;
-       unsigned long st_status=(unsigned long)status.st_size;
-       salvinfo->inodeSummary = (struct InodeSummary *)malloc(st_status);
-       assert(salvinfo->inodeSummary != NULL);
+       salvinfo->inodeSummary = (struct InodeSummary *)malloc(st_size);
+       osi_Assert(salvinfo->inodeSummary != NULL);
        /* For GNU we need to do lseek to get the file pointer moved. */
-       assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
-       ret = read(fileno(summaryFile), salvinfo->inodeSummary, st_status);
-       assert(ret == st_status);
+       osi_Assert(OS_SEEK(summaryFile, 0, SEEK_SET) == 0);
+       ret = OS_READ(summaryFile, salvinfo->inodeSummary, st_size);
+       osi_Assert(ret == st_size);
     }
-    salvinfo->nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
+    salvinfo->nVolumesInInodeFile = st_size / sizeof(struct InodeSummary);
     for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
        salvinfo->inodeSummary[i].volSummary = NULL;
     }
-    Log("%d nVolumesInInodeFile %lu \n",salvinfo->nVolumesInInodeFile,(unsigned long)(status.st_size));
-    fclose(summaryFile);
-    return 0;
+    Log("%d nVolumesInInodeFile %lu \n",salvinfo->nVolumesInInodeFile,(unsigned long)st_size);
+    OS_CLOSE(summaryFile);
+
+ error:
+    if (retcode && singleVolumeNumber && !deleted) {
+       AskError(salvinfo, singleVolumeNumber);
+    }
+
+    return retcode;
 }
 
 /* Comparison routine for volume sort.
@@ -1388,8 +1449,8 @@ AskVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
                Exit(SALSRV_EXIT_VOLGROUP_LINK);
            }
 
-           salvinfo->volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
-           assert(salvinfo->volumeSummaryp != NULL);
+           salvinfo->volumeSummaryp = calloc(VOL_VG_MAX_VOLS, sizeof(struct VolumeSummary));
+           osi_Assert(salvinfo->volumeSummaryp != NULL);
 
            salvinfo->nVolumes = 0;
            vsp = salvinfo->volumeSummaryp;
@@ -1420,7 +1481,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++;
            }
@@ -1506,6 +1567,8 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
 
     params = (struct SalvageScanParams *)rock;
 
+    memset(&summary, 0, sizeof(summary));
+
     singleVolumeNumber = params->singleVolumeNumber;
     salvinfo = params->salvinfo;
 
@@ -1539,15 +1602,15 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
 
        /* check if the header file is incorrectly named */
        int badname = 0;
-       const char *base = strrchr(name, '/');
+       const char *base = strrchr(name, OS_DIRSEPC);
        if (base) {
            base++;
        } else {
            base = name;
        }
 
-       (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
-                          VFORMAT, afs_printable_uint32_lu(summary.header.id));
+       snprintf(nameShouldBe, sizeof nameShouldBe,
+                VFORMAT, afs_printable_uint32_lu(summary.header.id));
 
 
        if (strcmp(nameShouldBe, base)) {
@@ -1567,7 +1630,7 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
 
                AskOffline(salvinfo, summary.header.id);
 
-#ifdef AFS_DEMAND_ATTACH_FS
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
                if (!badname) {
                    /* don't lock the volume if the header is bad, since we're
                     * about to delete it anyway. */
@@ -1576,7 +1639,7 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
                        return -1;
                    }
                }
-#endif /* AFS_DEMAND_ATTACH_FS */
+#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
            }
        }
        if (badname) {
@@ -1589,7 +1652,7 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
            return 1;
        }
 
-       summary.fileName = ToString(base);
+       summary.unused = 1;
        params->nVolumes++;
 
        if (params->nVolumes > params->totalVolumes) {
@@ -1704,8 +1767,8 @@ GetVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
        nvols = VOL_VG_MAX_VOLS;
     }
 
-    salvinfo->volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
-    assert(salvinfo->volumeSummaryp != NULL);
+    salvinfo->volumeSummaryp = calloc(nvols, sizeof(struct VolumeSummary));
+    osi_Assert(salvinfo->volumeSummaryp != NULL);
 
     params.singleVolumeNumber = singleVolumeNumber;
     params.vsp = salvinfo->volumeSummaryp;
@@ -1761,7 +1824,7 @@ 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
@@ -1780,7 +1843,7 @@ CreateLinkTable(struct SalvInfo *salvinfo, struct InodeSummary *isp, Inode ino)
     version.magic = LINKTABLEMAGIC;
     version.version = LINKTABLEVERSION;
 
-    if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
+    if (FDH_PWRITE(fdP, (char *)&version, sizeof(version), 0)
        != sizeof(version))
        Abort("Can't truncate link table for volume %u (error = %d)\n",
              isp->RWvolumeId, errno);
@@ -1806,7 +1869,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;
@@ -1875,10 +1938,10 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
     allInodes = inodes - isp->index;   /* this would the base of all the inodes
                                         * for the partition, if all the inodes
                                         * had been read into memory */
-    assert(afs_lseek
+    osi_Assert(OS_SEEK
           (salvinfo->inodeFd, isp->index * sizeof(struct ViceInodeInfo),
            SEEK_SET) != -1);
-    assert(read(salvinfo->inodeFd, inodes, size) == size);
+    osi_Assert(OS_READ(salvinfo->inodeFd, inodes, size) == size);
 
     /* Don't try to salvage a read write volume if there isn't one on this
      * partition */
@@ -1890,6 +1953,18 @@ 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 %u.\n", 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);
@@ -1905,11 +1980,7 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
                for (i = 0; i < nVols; i++) {
                        ip = allInodes + isp[i].index;
                         for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
-#ifdef AFS_NT40_ENV
-                                nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
-#else
                                 namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
-#endif
                    }
                }
            }
@@ -1970,6 +2041,7 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
 
     /* Fix actual inode counts */
     if (!Showmode) {
+       afs_ino_str_t stmp;
        Log("totalInodes %d\n",totalInodes);
        for (ip = inodes; totalInodes; ip++, totalInodes--) {
            static int TraceBadLinkCounts = 0;
@@ -1982,14 +2054,14 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
 #endif
            if (ip->linkCount != 0 && TraceBadLinkCounts) {
                TraceBadLinkCounts--;   /* Limit reports, per volume */
-               Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %llu, p=(%u,%u,%u,%u)\n", ip->linkCount, PrintInode(NULL, ip->inodeNumber), (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
+               Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %llu, p=(%u,%u,%u,%u)\n", ip->linkCount, PrintInode(stmp, ip->inodeNumber), (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
            }
            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",
-                           PrintInode(NULL, ip->inodeNumber), errno);
+                           PrintInode(stmp, ip->inodeNumber), errno);
                        break;
                    }
                }
@@ -2000,7 +2072,7 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
                if (!Testing) {
                    if (IH_INC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
                        Log("iinc failed. inode %s errno %d\n",
-                           PrintInode(NULL, ip->inodeNumber), errno);
+                           PrintInode(stmp, ip->inodeNumber), errno);
                        break;
                    }
                }
@@ -2200,11 +2272,12 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
        }
     }
     for (i = 0; i < isp->nSpecialInodes; i++) {
+       afs_ino_str_t stmp;
        ip = &inodes[isp->index + i];
        if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
            if (check) {
                Log("Rubbish header inode %s of type %d\n",
-                   PrintInode(NULL, ip->inodeNumber),
+                   PrintInode(stmp, ip->inodeNumber),
                    ip->u.special.type);
                if (skip) {
                    free(skip);
@@ -2212,17 +2285,17 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
                return -1;
            }
            Log("Rubbish header inode %s of type %d; deleted\n",
-               PrintInode(NULL, ip->inodeNumber),
+               PrintInode(stmp, ip->inodeNumber),
                ip->u.special.type);
        } else if (!stuff[ip->u.special.type - 1].obsolete) {
            if (skip && skip[i]) {
                if (orphans == ORPH_REMOVE) {
                    Log("Removing orphan special inode %s of type %d\n",
-                       PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
+                       PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
                    continue;
                } else {
                    Log("Ignoring orphan special inode %s of type %d\n",
-                       PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
+                       PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
                    /* fall through to the ip->linkCount--; line below */
                }
            } else {
@@ -2256,7 +2329,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;
@@ -2270,8 +2343,10 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
     if (isp->volSummary == NULL) {
        char path[64];
        char headerName[64];
-       (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
-       (void)afs_snprintf(path, sizeof path, "%s/%s", salvinfo->fileSysPath, headerName);
+       snprintf(headerName, sizeof headerName, VFORMAT,
+                afs_printable_uint32_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);
            return -1;
@@ -2280,9 +2355,7 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
            Log("No header file for volume %u; %screating %s\n",
                isp->volumeId, (Testing ? "it would have been " : ""),
                path);
-       isp->volSummary = (struct VolumeSummary *)
-           malloc(sizeof(struct VolumeSummary));
-       isp->volSummary->fileName = ToString(headerName);
+       isp->volSummary = calloc(1, sizeof(struct VolumeSummary));
 
        writefunc = VCreateVolumeDiskHeader;
     } else {
@@ -2295,14 +2368,9 @@ 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 {
-               (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
-               isp->volSummary->fileName = ToString(headerName);
-           }
-           (void)afs_snprintf(path, sizeof path, "%s/%s", salvinfo->fileSysPath, headerName);
+           VolumeExternalName_r(isp->volumeId, headerName, sizeof(headerName));
+           snprintf(path, sizeof path, "%s" OS_DIRSEP "%s",
+                    salvinfo->fileSysPath, headerName);
 
            Log("Header file %s is damaged or no longer valid%s\n", path,
                (check ? "" : "; repairing"));
@@ -2353,7 +2421,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) {
@@ -2391,7 +2459,7 @@ SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
              sp->description, errno);
 
     if (!recreate
-       && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
+       && (FDH_PREAD(fdP, (char *)&header, sp->size, 0) != sp->size
            || header.fileHeader.magic != sp->stamp.magic)) {
        if (check) {
            Log("Part of the header (%s) is corrupted\n", sp->description);
@@ -2440,16 +2508,11 @@ SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
            header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
            header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
            header.volumeInfo.needsCallback = 0;
-           gettimeofday(&tp, 0);
+           gettimeofday(&tp, NULL);
            header.volumeInfo.creationDate = tp.tv_sec;
-           if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
-               Abort
-                   ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
-                    sp->description, errno);
-           }
            nBytes =
-               FDH_WRITE(fdP, (char *)&header.volumeInfo,
-                         sizeof(header.volumeInfo));
+               FDH_PWRITE(fdP, (char *)&header.volumeInfo,
+                          sizeof(header.volumeInfo), 0);
            if (nBytes != sizeof(header.volumeInfo)) {
                if (nBytes < 0)
                    Abort
@@ -2459,12 +2522,7 @@ SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
                      sp->description);
            }
        } else {
-           if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
-               Abort
-                   ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
-                    sp->description, errno);
-           }
-           nBytes = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
+           nBytes = FDH_PWRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp), 0);
            if (nBytes != sizeof(sp->stamp)) {
                if (nBytes < 0)
                    Abort
@@ -2530,7 +2588,6 @@ SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
             struct ViceInodeInfo *ip, int nInodes,
              struct VolumeSummary *volSummary, int check)
 {
-    VolumeId volumeNumber;
     char buf[SIZEOF_LARGEDISKVNODE];
     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
     int err = 0;
@@ -2544,19 +2601,18 @@ SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
     IHandle_t *handle;
     FdHandle_t *fdP;
 
-    volumeNumber = volSummary->header.id;
     IH_INIT(handle, salvinfo->fileSysDevice, volSummary->header.parent, ino);
     fdP = IH_OPEN(handle);
-    assert(fdP != NULL);
+    osi_Assert(fdP != NULL);
     file = FDH_FDOPEN(fdP, "r+");
-    assert(file != NULL);
+    osi_Assert(file != NULL);
     vcp = &VnodeClassInfo[class];
     size = OS_SIZE(fdP->fd_fd);
-    assert(size != -1);
+    osi_Assert(size != -1);
     nVnodes = (size / vcp->diskSize) - 1;
     if (nVnodes > 0) {
-       assert((nVnodes + 1) * vcp->diskSize == size);
-       assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
+       osi_Assert((nVnodes + 1) * vcp->diskSize == size);
+       osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
     } else {
        nVnodes = 0;
     }
@@ -2566,12 +2622,6 @@ SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
        if (vnode->type != vNull) {
            int vnodeChanged = 0;
            int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
-           /* Log programs that belong to root (potentially suid root);
-            * don't bother for read-only or backup volumes */
-#ifdef notdef                  /* This is done elsewhere */
-           if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
-               Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n", salvinfo->VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author, vnode->owner, vnode->modeBits);
-#endif
            if (VNDISK_GET_INO(vnode) == 0) {
                if (RW) {
                    /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
@@ -2581,6 +2631,14 @@ SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
            } else {
                if (vcp->magic != vnode->vnodeMagic) {
                    /* bad magic #, probably partially created vnode */
+                   if (check) {
+                      Log("Partially allocated vnode %d: bad magic (is %lx should be %lx)\n",
+                          vnodeNumber, afs_printable_uint32_lu(vnode->vnodeMagic),
+                          afs_printable_uint32_lu(vcp->magic));
+                      memset(vnode, 0, vcp->diskSize);
+                      err = -1;
+                      goto zooks;
+                   }
                    Log("Partially allocated vnode %d deleted.\n",
                        vnodeNumber);
                    memset(vnode, 0, vcp->diskSize);
@@ -2728,13 +2786,14 @@ SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
                    ip++;
                    nInodes--;
                } else {        /* no matching inode */
+                   afs_ino_str_t stmp;
                    if (VNDISK_GET_INO(vnode) != 0
                        || vnode->type == vDirectory) {
                        /* No matching inode--get rid of the vnode */
                        if (check) {
                            if (VNDISK_GET_INO(vnode)) {
                                if (!Showmode) {
-                                   Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
+                                   Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)));
                                }
                            } else {
                                if (!Showmode)
@@ -2746,7 +2805,7 @@ SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
                        if (VNDISK_GET_INO(vnode)) {
                            if (!Showmode) {
                                time_t serverModifyTime = vnode->serverModifyTime;
-                               Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)), ctime(&serverModifyTime));
+                               Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)), ctime(&serverModifyTime));
                            }
                        } else {
                            if (!Showmode) {
@@ -2769,9 +2828,9 @@ SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
                }
            }                   /* VNDISK_GET_INO(vnode) != 0 */
          vnodeDone:
-           assert(!(vnodeChanged && check));
+           osi_Assert(!(vnodeChanged && check));
            if (vnodeChanged && !Testing) {
-               assert(IH_IWRITE
+               osi_Assert(IH_IWRITE
                       (handle, vnodeIndexOffset(vcp, vnodeNumber),
                        (char *)vnode, vcp->diskSize)
                       == vcp->diskSize);
@@ -2818,7 +2877,7 @@ CopyOnWrite(struct SalvInfo *salvinfo, struct DirSummary *dir)
        IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
                 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                 sizeof(vnode));
-    assert(code == sizeof(vnode));
+    osi_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
@@ -2828,15 +2887,15 @@ 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);
-    assert(VALID_INO(newinode));
-    assert(CopyInode(salvinfo->fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
+    osi_Assert(VALID_INO(newinode));
+    osi_Assert(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));
-    assert(code == sizeof(vnode));
+    osi_Assert(code == sizeof(vnode));
 
     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
                        salvinfo->fileSysDevice, newinode,
@@ -2874,7 +2933,7 @@ CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
        IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
                 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
                 sizeof(vnode));
-    assert(lcode == sizeof(vnode));
+    osi_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
@@ -2884,7 +2943,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);
-    assert(VALID_INO(newinode));
+    osi_Assert(VALID_INO(newinode));
     SetSalvageDirHandle(&newdir, dir->rwVid, salvinfo->fileSysDevice, newinode,
                         &salvinfo->VolumeChanged);
 
@@ -2910,24 +2969,24 @@ CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
        if (code) {
            Log("also failed to decrement link count on new inode");
        }
-       assert(1 == 2);
+       osi_Assert(1 == 2);
     }
     Log("Checking the results of the directory salvage...\n");
     if (!DirOK(&newdir)) {
        Log("Directory salvage failed!!!; restoring old version of the directory.\n");
        code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
-       assert(code == 0);
-       assert(1 == 2);
+       osi_Assert(code == 0);
+       osi_Assert(1 == 2);
     }
     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));
-    assert(lcode == sizeof(vnode));
+    osi_Assert(lcode == sizeof(vnode));
 #if 0
 #ifdef AFS_NT40_ENV
     nt_sync(salvinfo->fileSysDevice);
@@ -2944,7 +3003,7 @@ CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
     FDH_REALLYCLOSE(fdP);
 
     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
-    assert(code == 0);
+    osi_Assert(code == 0);
     dir->dirHandle = newdir;
 }
 
@@ -2971,11 +3030,11 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
     vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
     if (vnodeEssence == NULL) {
        if (!Showmode) {
-           Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
+           Log("dir vnode %u: invalid entry deleted: %s" OS_DIRSEP "%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
        }
        if (!Testing) {
            CopyOnWrite(salvinfo, dir);
-           assert(Delete(&dir->dirHandle, name) == 0);
+           osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
        }
        return 0;
     }
@@ -2986,10 +3045,10 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
      * the machine.
      */
     if (vnodeEssence->InodeNumber == 0) {
-       Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
+       Log("dir vnode %d: invalid entry: %s" OS_DIRSEP "%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
        if (!Testing) {
            CopyOnWrite(salvinfo, dir);
-           assert(Delete(&dir->dirHandle, name) == 0);
+           osi_Assert(Delete(&dir->dirHandle, name) == 0);
        }
        return 0;
     }
@@ -2999,7 +3058,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
     if (!(vnodeNumber & 1) && !Showmode
        && !(vnodeEssence->count || vnodeEssence->unique
             || vnodeEssence->modeBits)) {
-       Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
+       Log("dir vnode %u: invalid entry: %s" OS_DIRSEP "%s (vnode %u, unique %u)%s\n",
            dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
            vnodeNumber, unique,
            ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
@@ -3007,7 +3066,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        if (!unique) {
            if (!Testing) {
                CopyOnWrite(salvinfo, dir);
-               assert(Delete(&dir->dirHandle, name) == 0);
+               osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
            }
            return 0;
        }
@@ -3030,16 +3089,16 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
 
        if (!Showmode) {
-           Log("dir vnode %u: %s/%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
+           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);
-           assert(Delete(&dir->dirHandle, name) == 0);
+           osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
            if (!todelete)
-               assert(Create(&dir->dirHandle, name, &fid) == 0);
+               osi_Assert(afs_dir_Create(&dir->dirHandle, name, &fid) == 0);
        }
        if (todelete)
            return 0;           /* no need to continue */
@@ -3047,20 +3106,19 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
 
     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);
-               assert(Delete(&dir->dirHandle, ".") == 0);
+               osi_Assert(afs_dir_Delete(&dir->dirHandle, ".") == 0);
                fid.Vnode = dir->vnodeNumber;
                fid.Unique = dir->unique;
-               assert(Create(&dir->dirHandle, ".", &fid) == 0);
+               osi_Assert(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) {
@@ -3069,7 +3127,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
            struct VnodeEssence *dotdot;
            pa.Vnode = dir->parent;
            dotdot = CheckVnodeNumber(salvinfo, pa.Vnode);
-           assert(dotdot != NULL);     /* XXX Should not be assert */
+           osi_Assert(dotdot != NULL); /* XXX Should not be assert */
            pa.Unique = dotdot->unique;
        } else {
            pa.Vnode = dir->vnodeNumber;
@@ -3080,8 +3138,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);
-               assert(Delete(&dir->dirHandle, "..") == 0);
-               assert(Create(&dir->dirHandle, "..", &pa) == 0);
+               osi_Assert(afs_dir_Delete(&dir->dirHandle, "..") == 0);
+               osi_Assert(afs_dir_Create(&dir->dirHandle, "..", &pa) == 0);
            }
 
            vnodeNumber = pa.Vnode;     /* Get the new Essence */
@@ -3095,17 +3153,17 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        }
        if (!Testing) {
            CopyOnWrite(salvinfo, dir);
-           assert(Delete(&dir->dirHandle, name) == 0);
+           osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
        }
        vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
        vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
        return 0;
     } else {
        if (ShowSuid && (vnodeEssence->modeBits & 06000))
-           Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
+           Log("FOUND suid/sgid file: %s" OS_DIRSEP "%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
        if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
            && !(vnodeEssence->modeBits & 0111)) {
-           ssize_t nBytes;
+           afs_sfsize_t nBytes;
            afs_sfsize_t size;
            char buf[1025];
            IHandle_t *ihP;
@@ -3129,16 +3187,16 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
 
            if (size > 1024)
                size = 1024;
-           nBytes = FDH_READ(fdP, buf, size);
+           nBytes = FDH_PREAD(fdP, buf, size, 0);
            if (nBytes == size) {
                buf[size] = '\0';
                if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
-                   Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
+                   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,
                        Testing ? "would convert" : "converted");
                    vnodeEssence->modeBits |= 0111;
                    vnodeEssence->changed = 1;
-               } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
+               } 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 {
@@ -3149,7 +3207,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
            IH_RELEASE(ihP);
        }
        if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
-           Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
+           Log("FOUND root file: %s" OS_DIRSEP "%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
        if (vnodeIdToClass(vnodeNumber) == vLarge
            && vnodeEssence->name == NULL) {
            char *n;
@@ -3172,7 +3230,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
                 * another non-orphaned dir).
                 */
                if (!Showmode) {
-                   Log("dir vnode %u: %s/%s (vnode %u, unique %u) -- parent vnode %schanged from %u to %u\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""), vnodeEssence->parent, dir->vnodeNumber);
+                   Log("dir vnode %u: %s" OS_DIRSEP "%s (vnode %u, unique %u) -- parent vnode %schanged from %u to %u\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""), vnodeEssence->parent, dir->vnodeNumber);
                }
                vnodeEssence->parent = dir->vnodeNumber;
                vnodeEssence->changed = 1;
@@ -3180,16 +3238,16 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
                /* Vnode was claimed by another directory */
                if (!Showmode) {
                    if (dirOrphaned) {
-                       Log("dir vnode %u: %s/%s parent vnode is %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
+                       Log("dir vnode %u: %s" OS_DIRSEP "%s parent vnode is %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
                    } else if (vnodeNumber == 1) {
-                       Log("dir vnode %d: %s/%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""));
+                       Log("dir vnode %d: %s" OS_DIRSEP "%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""));
                    } else {
-                       Log("dir vnode %u: %s/%s already claimed by directory vnode %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
+                       Log("dir vnode %u: %s" OS_DIRSEP "%s already claimed by directory vnode %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
                    }
                }
                if (!Testing) {
                    CopyOnWrite(salvinfo, dir);
-                   assert(Delete(&dir->dirHandle, name) == 0);
+                   osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
                }
                return 0;
            }
@@ -3217,19 +3275,19 @@ DistilVnodeEssence(struct SalvInfo *salvinfo, VolumeId rwVId,
 
     IH_INIT(vip->handle, salvinfo->fileSysDevice, rwVId, ino);
     fdP = IH_OPEN(vip->handle);
-    assert(fdP != NULL);
+    osi_Assert(fdP != NULL);
     file = FDH_FDOPEN(fdP, "r+");
-    assert(file != NULL);
+    osi_Assert(file != NULL);
     size = OS_SIZE(fdP->fd_fd);
-    assert(size != -1);
+    osi_Assert(size != -1);
     vip->nVnodes = (size / vcp->diskSize) - 1;
     if (vip->nVnodes > 0) {
-       assert((vip->nVnodes + 1) * vcp->diskSize == size);
-       assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
-       assert((vip->vnodes = (struct VnodeEssence *)
+       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);
        if (class == vLarge) {
-           assert((vip->inodes = (Inode *)
+           osi_Assert((vip->inodes = (Inode *)
                    calloc(vip->nVnodes, sizeof(Inode))) != NULL);
        } else {
            vip->inodes = NULL;
@@ -3265,7 +3323,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));
@@ -3291,7 +3349,7 @@ GetDirName(struct SalvInfo *salvinfo, VnodeId vnode, struct VnodeEssence *vp,
     }
     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(salvinfo, vp->parent))
        && GetDirName(salvinfo, vp->parent, parentvp, path)) {
-       strcat(path, "/");
+       strcat(path, OS_DIRSEP);
        strcat(path, vp->name);
        return path;
     }
@@ -3388,7 +3446,8 @@ SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
        judge_params.salvinfo = salvinfo;
        judge_params.dir = &dir;
 
-       assert(EnumerateDir(&dirHandle, JudgeEntry, &judge_params) == 0);
+       osi_Assert(afs_dir_EnumerateDir(&dirHandle, JudgeEntry,
+                                       &judge_params) == 0);
     }
 
     /* Delete the old directory if it was copied in order to salvage.
@@ -3399,7 +3458,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);
-       assert(code == 0);
+       osi_Assert(code == 0);
        dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
     }
 
@@ -3626,7 +3685,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) {
@@ -3641,8 +3700,8 @@ CreateRootDir(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
        salvinfo->vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
        salvinfo->vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
 
-       assert(salvinfo->vnodeInfo[vLarge].vnodes);
-       assert(salvinfo->vnodeInfo[vLarge].inodes);
+       osi_Assert(salvinfo->vnodeInfo[vLarge].vnodes);
+       osi_Assert(salvinfo->vnodeInfo[vLarge].inodes);
     }
 
     vep = &salvinfo->vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
@@ -3675,17 +3734,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);
@@ -3824,9 +3883,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));
-    assert(nBytes == sizeof(volHeader));
-    assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
-    assert(volHeader.destroyMe != DESTROY_ME);
+    osi_Assert(nBytes == sizeof(volHeader));
+    osi_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
+    osi_Assert(volHeader.destroyMe != DESTROY_ME);
     /* (should not have gotten this far with DESTROY_ME flag still set!) */
 
     DistilVnodeEssence(salvinfo, vid, vLarge,
@@ -3935,8 +3994,8 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                                        &salvinfo->VolumeChanged);
                    pa.Vnode = LFVnode;
                    pa.Unique = LFUnique;
-                   assert(Delete(&dh, "..") == 0);
-                   assert(Create(&dh, "..", &pa) == 0);
+                   osi_Assert(afs_dir_Delete(&dh, "..") == 0);
+                   osi_Assert(afs_dir_Create(&dh, "..", &pa) == 0);
 
                    /* The original parent's link count was decremented above.
                     * Here we increment the new parent's link count.
@@ -3953,20 +4012,19 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                    pa.Vnode = ThisVnode;
                    pa.Unique = ThisUnique;
 
-                   (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
-                                      ((class ==
-                                        vLarge) ? "__ORPHANDIR__" :
-                                       "__ORPHANFILE__"), ThisVnode,
-                                      ThisUnique);
+                   snprintf(npath, sizeof npath, "%s.%u.%u",
+                            ((class == vLarge) ? "__ORPHANDIR__"
+                                               : "__ORPHANFILE__"),
+                            ThisVnode, ThisUnique);
 
                    CopyOnWrite(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 */
                }
-               assert(code == 0);
+               osi_Assert(code == 0);
                Log("Attaching orphaned %s to volume's root dir as %s\n",
                    ((class == vLarge) ? "directory" : "file"), npath);
            }
@@ -3979,7 +4037,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
        code =
            IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
                   oldrootdir.rwVid);
-       assert(code == 0);
+       osi_Assert(code == 0);
        /* dirVnodeInfo->inodes[?] is not updated with new inode number */
     }
 
@@ -3993,7 +4051,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
      * will get removed here also (if requested).
      */
     for (class = 0; class < nVNODECLASSES; class++) {
-       int nVnodes = salvinfo->vnodeInfo[class].nVnodes;
+       afs_sfsize_t nVnodes = salvinfo->vnodeInfo[class].nVnodes;
        struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
        struct VnodeEssence *vnodes = salvinfo->vnodeInfo[class].vnodes;
        FilesInVolume += salvinfo->vnodeInfo[class].nAllocatedVnodes;
@@ -4017,7 +4075,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                    IH_IREAD(salvinfo->vnodeInfo[class].handle,
                             vnodeIndexOffset(vcp, vnodeNumber),
                             (char *)&vnode, sizeof(vnode));
-               assert(nBytes == sizeof(vnode));
+               osi_Assert(nBytes == sizeof(vnode));
 
                vnode.parent = vnp->parent;
                oldCount = vnode.linkCount;
@@ -4028,7 +4086,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                if (orphaned) {
                    if (!vnp->todelete) {
                        /* Orphans should have already been attached (if requested) */
-                       assert(orphans != ORPH_ATTACH);
+                       osi_Assert(orphans != ORPH_ATTACH);
                        oblocks += vnp->blockCount;
                        ofiles++;
                    }
@@ -4039,7 +4097,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                        if (VNDISK_GET_INO(&vnode)) {
                            code =
                                IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
-                           assert(code == 0);
+                           osi_Assert(code == 0);
                        }
                        memset(&vnode, 0, sizeof(vnode));
                    }
@@ -4057,7 +4115,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                        IH_IWRITE(salvinfo->vnodeInfo[class].handle,
                                  vnodeIndexOffset(vcp, vnodeNumber),
                                  (char *)&vnode, sizeof(vnode));
-                   assert(nBytes == sizeof(vnode));
+                   osi_Assert(nBytes == sizeof(vnode));
                }
                salvinfo->VolumeChanged = 1;
            }
@@ -4102,22 +4160,41 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
            afs_printable_uint32_lu(vid));
     }
 
-#ifdef FSSYNC_BUILD_CLIENT
     if (!Testing && salvinfo->VolumeChanged) {
-       afs_int32 fsync_code;
-
-       fsync_code = FSYNC_VolOp(vid, NULL, FSYNC_VOL_BREAKCBKS, FSYNC_SALVAGE, NULL);
-       if (fsync_code) {
-           Log("Error trying to tell the fileserver to break callbacks for "
-               "changed volume %lu; error code %ld\n",
-               afs_printable_uint32_lu(vid),
-               afs_printable_int32_ld(fsync_code));
-       } else {
-           salvinfo->VolumeChanged = 0;
+#ifdef FSSYNC_BUILD_CLIENT
+       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 */
 
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+       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 */
@@ -4127,7 +4204,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
     salvinfo->VolumeChanged = 0;
     if (!Testing) {
        nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
-       assert(nBytes == sizeof(volHeader));
+       osi_Assert(nBytes == sizeof(volHeader));
     }
     if (!Showmode) {
        Log("%sSalvaged %s (%u): %d files, %d blocks\n",
@@ -4150,15 +4227,15 @@ ClearROInUseBit(struct VolumeSummary *summary)
     VolumeDiskData volHeader;
 
     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
-    assert(nBytes == sizeof(volHeader));
-    assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
+    osi_Assert(nBytes == sizeof(volHeader));
+    osi_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
     volHeader.inUse = 0;
     volHeader.needsSalvaged = 0;
     volHeader.inService = 1;
     volHeader.dontSalvage = DONT_SALVAGE;
     if (!Testing) {
        nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
-       assert(nBytes == sizeof(volHeader));
+       osi_Assert(nBytes == sizeof(volHeader));
     }
 }
 
@@ -4172,7 +4249,7 @@ 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);
@@ -4187,7 +4264,9 @@ MaybeZapVolume(struct SalvInfo *salvinfo, struct InodeSummary *isp,
            if (!Testing) {
                afs_int32 code;
                char path[64];
-               sprintf(path, "%s/%s", salvinfo->fileSysPath, isp->volSummary->fileName);
+               char filename[VMAXPATHLEN];
+               VolumeExternalName_r(isp->volumeId, filename, sizeof(filename));
+               sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, filename);
 
                code = VDestroyVolumeDiskHeader(salvinfo->fileSysPartition, isp->volumeId, isp->RWvolumeId);
                if (code) {
@@ -4196,12 +4275,16 @@ MaybeZapVolume(struct SalvInfo *salvinfo, struct InodeSummary *isp,
                        afs_printable_uint32_lu(isp->volumeId));
                }
 
-               /* make sure we actually delete the fileName file; ENOENT
+               /* make sure we actually delete the header file; ENOENT
                 * is fine, since VDestroyVolumeDiskHeader probably already
                 * unlinked it */
                if (unlink(path) && errno != ENOENT) {
                    Log("Unable to unlink %s (errno = %d)\n", path, errno);
                }
+               if (salvinfo->useFSYNC) {
+                   AskDelete(salvinfo, isp->volumeId);
+               }
+               isp->volSummary->deleted = 1;
            }
        }
     } else if (!check) {
@@ -4211,7 +4294,7 @@ MaybeZapVolume(struct SalvInfo *salvinfo, struct InodeSummary *isp,
     }
 }
 
-#ifdef AFS_DEMAND_ATTACH_FS
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
 /**
  * Locks a volume on disk for salvaging.
  *
@@ -4246,7 +4329,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;
@@ -4290,14 +4373,29 @@ 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. */
-       assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
+       osi_Assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
 
        IH_RELEASE(h);
     }
 
     return 0;
 }
-#endif /* AFS_DEMAND_ATTACH_FS */
+#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
+
+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)
@@ -4314,20 +4412,27 @@ AskOffline(struct SalvInfo *salvinfo, VolumeId volumeId)
        if (code == SYNC_OK) {
            break;
        } else if (code == SYNC_DENIED) {
-#ifdef DEMAND_ATTACH_ENABLE
-           Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
-#else
-           Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
-#endif
+           if (AskDAFS())
+               Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
+           else
+               Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
            Abort("Salvage aborted\n");
        } else if (code == SYNC_BAD_COMMAND) {
            Log("AskOffline:  fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
                FSYNC_VOL_OFF);
-#ifdef DEMAND_ATTACH_ENABLE
-           Log("AskOffline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
+           if (AskDAFS()) {
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+               Log("AskOffline:  please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
 #else
-           Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
+               Log("AskOffline:  fileserver is DAFS but we are not.\n");
 #endif
+           } else {
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+               Log("AskOffline:  fileserver is not DAFS but we are.\n");
+#else
+               Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
+#endif
+           }
            Abort("Salvage aborted\n");
        } else if (i < 2) {
            /* try it again */
@@ -4342,6 +4447,58 @@ AskOffline(struct SalvInfo *salvinfo, VolumeId volumeId)
     }
 }
 
+/* don't want to pass around state; remember it here */
+static int isDAFS = -1;
+int
+AskDAFS(void)
+{
+    SYNC_response res;
+    afs_int32 code = 1, i;
+
+    /* we don't care if we race. the answer shouldn't change */
+    if (isDAFS != -1)
+       return isDAFS;
+
+    memset(&res, 0, sizeof(res));
+
+    for (i = 0; code && i < 3; i++) {
+       code = FSYNC_VolOp(0, NULL, FSYNC_VOL_LISTVOLUMES, FSYNC_SALVAGE, &res);
+       if (code) {
+           Log("AskDAFS: FSYNC_VOL_LISTVOLUMES failed with code %ld reason "
+               "%ld (%s); trying again...\n", (long)code, (long)res.hdr.reason,
+               FSYNC_reason2string(res.hdr.reason));
+           FSYNC_clientFinis();
+           FSYNC_clientInit();
+       }
+    }
+
+    if (code) {
+       Log("AskDAFS: could not determine DAFS-ness, assuming not DAFS\n");
+       res.hdr.flags = 0;
+    }
+
+    if ((res.hdr.flags & SYNC_FLAG_DAFS_EXTENSIONS)) {
+       isDAFS = 1;
+    } else {
+       isDAFS = 0;
+    }
+
+    return isDAFS;
+}
+
+static void
+MaybeAskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
+{
+    struct VolumeDiskHeader diskHdr;
+    int code;
+    code = VReadVolumeDiskHeader(volumeId, salvinfo->fileSysPartition, &diskHdr);
+    if (code) {
+       /* volume probably does not exist; no need to bring back online */
+       return;
+    }
+    AskOnline(salvinfo, volumeId);
+}
+
 void
 AskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
 {
@@ -4358,15 +4515,57 @@ AskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
        } else if (code == SYNC_BAD_COMMAND) {
            Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
                FSYNC_VOL_ON);
-#ifdef DEMAND_ATTACH_ENABLE
-           Log("AskOnline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
+           Log("AskOnline:  please make sure file server binaries are same version.\n");
+           break;
+       } else if (i < 2) {
+           /* try it again */
+           Log("AskOnline:  request for fileserver to put volume online failed; trying again...\n");
+           FSYNC_clientFinis();
+           FSYNC_clientInit();
+       }
+    }
+}
+
+void
+AskDelete(struct SalvInfo *salvinfo, VolumeId volumeId)
+{
+    afs_int32 code, i;
+    SYNC_response res;
+
+    for (i = 0; i < 3; i++) {
+       memset(&res, 0, sizeof(res));
+       code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
+                          FSYNC_VOL_DONE, FSYNC_SALVAGE, &res);
+
+       if (code == SYNC_OK) {
+           break;
+       } else if (code == SYNC_DENIED) {
+           Log("AskOnline:  file server denied DONE request to volume %u partition %s; trying again...\n", 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)
+               Log("AskOnline:  please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
 #else
-           Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
+               Log("AskOnline:  fileserver is DAFS but we are not.\n");
 #endif
+           } else {
+#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
+               Log("AskOnline:  fileserver is not DAFS but we are.\n");
+#else
+               Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
+#endif
+           }
+           break;
+       } else if (code == SYNC_FAILED &&
+                    (res.hdr.reason == FSYNC_UNKNOWN_VOLID ||
+                     res.hdr.reason == FSYNC_WRONG_PART)) {
+           /* volume is already effectively 'deleted' */
            break;
        } else if (i < 2) {
            /* try it again */
-           Log("AskOnline:  request for fileserver to take volume offline failed; trying again...\n");
+           Log("AskOnline:  request for fileserver to delete volume failed; trying again...\n");
            FSYNC_clientFinis();
            FSYNC_clientInit();
        }
@@ -4383,15 +4582,18 @@ CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
     IHandle_t *srcH, *destH;
     FdHandle_t *srcFdP, *destFdP;
     ssize_t nBytes = 0;
+    afs_foff_t size = 0;
 
     IH_INIT(srcH, device, rwvolume, inode1);
     srcFdP = IH_OPEN(srcH);
-    assert(srcFdP != NULL);
+    osi_Assert(srcFdP != NULL);
     IH_INIT(destH, device, rwvolume, inode2);
     destFdP = IH_OPEN(destH);
-    while ((nBytes = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
-       assert(FDH_WRITE(destFdP, buf, nBytes) == nBytes);
-    assert(nBytes == 0);
+    while ((nBytes = FDH_PREAD(srcFdP, buf, sizeof(buf), size)) > 0) {
+       osi_Assert(FDH_PWRITE(destFdP, buf, nBytes, size) == nBytes);
+       size += nBytes;
+    }
+    osi_Assert(nBytes == 0);
     FDH_REALLYCLOSE(srcFdP);
     FDH_REALLYCLOSE(destFdP);
     IH_RELEASE(srcH);
@@ -4404,17 +4606,19 @@ PrintInodeList(struct SalvInfo *salvinfo)
 {
     struct ViceInodeInfo *ip;
     struct ViceInodeInfo *buf;
-    struct afs_stat status;
     int nInodes;
-
-    assert(afs_fstat(salvinfo->inodeFd, &status) == 0);
-    buf = (struct ViceInodeInfo *)malloc(status.st_size);
-    assert(buf != NULL);
-    nInodes = status.st_size / sizeof(struct ViceInodeInfo);
-    assert(read(salvinfo->inodeFd, buf, status.st_size) == status.st_size);
+    afs_ino_str_t stmp;
+    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);
+    nInodes = st_size / sizeof(struct ViceInodeInfo);
+    osi_Assert(OS_READ(salvinfo->inodeFd, buf, st_size) == st_size);
     for (ip = buf; nInodes--; ip++) {
        Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
-           PrintInode(NULL, ip->inodeNumber), ip->linkCount,
+           PrintInode(stmp, ip->inodeNumber), ip->linkCount,
            (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
            ip->u.param[2], ip->u.param[3]);
     }
@@ -4433,27 +4637,16 @@ PrintInodeSummary(struct SalvInfo *salvinfo)
     }
 }
 
-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);
-    }
-}
-
 int
 Fork(void)
 {
     int f;
 #ifdef AFS_NT40_ENV
     f = 0;
-    assert(0);                 /* Fork is never executed in the NT code path */
+    osi_Assert(0);                     /* Fork is never executed in the NT code path */
 #else
     f = fork();
-    assert(f >= 0);
+    osi_Assert(f >= 0);
 #ifdef AFS_DEMAND_ATTACH_FS
     if ((f == 0) && (programType == salvageServer)) {
        /* we are a salvageserver child */
@@ -4502,7 +4695,7 @@ Wait(char *prog)
     int status;
     int pid;
     pid = wait(&status);
-    assert(pid != -1);
+    osi_Assert(pid != -1);
     if (WCOREDUMP(status))
        Log("\"%s\" core dumped!\n", prog);
     if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
@@ -4561,11 +4754,10 @@ TimeStampLogFile(char * log_path)
 
     now = time(0);
     lt = localtime(&now);
-    (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
-                      "%s.%04d-%02d-%02d.%02d:%02d:%02d",
-                      log_path, lt->tm_year + 1900,
-                      lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
-                      lt->tm_sec);
+    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 */
@@ -4611,7 +4803,7 @@ Log(const char *format, ...)
     va_list args;
 
     va_start(args, format);
-    (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
+    vsnprintf(tmp, sizeof tmp, format, args);
     va_end(args);
 #ifndef AFS_NT40_ENV
     if (useSyslog) {
@@ -4619,7 +4811,7 @@ Log(const char *format, ...)
     } else
 #endif
        if (logFile) {
-           gettimeofday(&now, 0);
+           gettimeofday(&now, NULL);
            fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
            fflush(logFile);
        }
@@ -4632,7 +4824,7 @@ Abort(const char *format, ...)
     char tmp[1024];
 
     va_start(args, format);
-    (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
+    vsnprintf(tmp, sizeof tmp, format, args);
     va_end(args);
 #ifndef AFS_NT40_ENV
     if (useSyslog) {
@@ -4656,7 +4848,7 @@ ToString(const char *s)
 {
     char *p;
     p = (char *)malloc(strlen(s) + 1);
-    assert(p != NULL);
+    osi_Assert(p != NULL);
     strcpy(p, s);
     return p;
 }
@@ -4666,7 +4858,7 @@ void
 RemoveTheForce(char *path)
 {
     char target[1024];
-    struct afs_stat force; /* so we can use afs_stat to find it */
+    struct afs_stat_st force; /* so we can use afs_stat to find it */
     strcpy(target,path);
     strcat(target,"/FORCESALVAGE");
     if (!Testing && ForceSalvage) {
@@ -4681,7 +4873,7 @@ RemoveTheForce(char *path)
 int
 UseTheForceLuke(char *path)
 {
-    struct afs_stat force;
+    struct afs_stat_st force;
     char target[1024];
     strcpy(target,path);
     strcat(target,"/FORCESALVAGE");