Abstract /vicepX header traversal
[openafs.git] / src / vol / vol-salvage.c
index 0f4c0f4..bf3c325 100644 (file)
@@ -86,8 +86,6 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #include <afsconfig.h>
 #include <afs/param.h>
 
-RCSID
-    ("$Header$");
 
 #ifndef AFS_NT40_ENV
 #include <sys/param.h>
@@ -107,8 +105,8 @@ RCSID
 #include <io.h>
 #include <WINNT/afsevent.h>
 #endif
-#if    defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
-#define WCOREDUMP(x)   (x & 0200)
+#ifndef WCOREDUMP
+#define WCOREDUMP(x)   ((x) & 0200)
 #endif
 #include <rx/xdr.h>
 #include <afs/afsint.h>
@@ -167,6 +165,7 @@ RCSID
 #include <afs/osi_inode.h>
 #endif
 #include <afs/cmd.h>
+#include <afs/dir.h>
 #include <afs/afsutil.h>
 #include <afs/fileutil.h>
 #include <afs/procmgmt.h>      /* signal(), kill(), wait(), etc. */
@@ -189,6 +188,12 @@ RCSID
 #include "salvage.h"
 #include "volinodes.h"         /* header magic number, etc. stuff */
 #include "vol-salvage.h"
+#include "vol_internal.h"
+
+#ifdef FSSYNC_BUILD_CLIENT
+#include "vg_cache.h"
+#endif
+
 #ifdef AFS_NT40_ENV
 #include <pthread.h>
 #endif
@@ -288,7 +293,7 @@ int inodeFd;                        /* File descriptor for inode file */
 struct VnodeInfo vnodeInfo[nVNODECLASSES];
 
 
-struct VolumeSummary *volumeSummaryp;  /* Holds all the volumes in a part */
+struct VolumeSummary *volumeSummaryp = NULL;   /* Holds all the volumes in a part */
 int nVolumes;                  /* Number of volumes (read-write and read-only)
                                 * in volume summary */
 
@@ -300,6 +305,7 @@ char *tmpdir = NULL;
 /*@printflike@*/ void Log(const char *format, ...);
 /*@printflike@*/ void Abort(const char *format, ...);
 static int IsVnodeOrphaned(VnodeId vnode);
+static int AskVolumeSummary(VolumeId singleVolumeNumber);
 
 /* Uniquifier stored in the Inode */
 static Unique
@@ -336,13 +342,13 @@ childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
 void
 ObtainSalvageLock(void)
 {
-    int salvageLock;
+    FD_t salvageLock;
 
 #ifdef AFS_NT40_ENV
     salvageLock =
-       (int)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
+       (FD_t)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
                        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-    if (salvageLock == (int)INVALID_HANDLE_VALUE) {
+    if (salvageLock == INVALID_FD) {
        fprintf(stderr,
                "salvager:  There appears to be another salvager running!  Aborted.\n");
        Exit(1);
@@ -707,10 +713,12 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 {
     char *name, *tdir;
     char inodeListPath[256];
+    FILE *inodeFile;
     static char tmpDevName[100];
     static char wpath[100];
     struct VolumeSummary *vsp, *esp;
     int i, j;
+    int code;
 
     fileSysPartition = partP;
     fileSysDevice = fileSysPartition->device;
@@ -779,9 +787,10 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
     snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
             getpid());
 #endif
-    if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
-       unlink(inodeListPath);
-       return;
+
+    inodeFile = fopen(inodeListPath, "w+b");
+    if (!inodeFile) {
+       Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
     }
 #ifdef AFS_NT40_ENV
     /* Using nt_unlink here since we're really using the delete on close
@@ -789,15 +798,22 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
      * 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.
      */
-    inodeFd =
-       _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
-    nt_unlink(inodeListPath);  /* NT's crt unlink won't if file is open. */
+    code = nt_unlink(inodeListPath);
 #else
-    inodeFd = afs_open(inodeListPath, O_RDONLY);
-    unlink(inodeListPath);
+    code = unlink(inodeListPath);
 #endif
+    if (code < 0) {
+       Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
+    }
+
+    if (GetInodeSummary(inodeFile, singleVolumeNumber) < 0) {
+       fclose(inodeFile);
+       return;
+    }
+    inodeFd = fileno(inodeFile);
     if (inodeFd == -1)
        Abort("Temporary file %s is missing...\n", inodeListPath);
+    afs_lseek(inodeFd, 0L, SEEK_SET);
     if (ListInodeOption) {
        PrintInodeList();
        return;
@@ -867,19 +883,37 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
                fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
     }
 
-    close(inodeFd);            /* SalvageVolumeGroup was the last which needed it. */
+    fclose(inodeFile);         /* SalvageVolumeGroup was the last which needed it. */
 }
 
 void
 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
 {
+    char path[64];
+    sprintf(path, "%s/%s", fileSysPath, vsp->fileName);
+
     if (!Showmode)
-       Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", vsp->fileName, (Testing ? "would have been " : ""));
-    if (!Testing)
-       unlink(vsp->fileName);
+       Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
+    if (!Testing) {
+       afs_int32 code;
+       code = VDestroyVolumeDiskHeader(fileSysPartition, vsp->header.id, vsp->header.parent);
+       if (code) {
+           Log("Error %ld destroying volume disk header for volume %lu\n",
+               afs_printable_int32_ld(code),
+               afs_printable_uint32_lu(vsp->header.id));
+       }
+
+       /* make sure we actually delete the fileName 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);
+       }
+    }
     vsp->fileName = 0;
 }
 
+int
 CompareInodes(const void *_p1, const void *_p2)
 {
     register const struct ViceInodeInfo *p1 = _p1;
@@ -980,9 +1014,9 @@ void
 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
                  register struct InodeSummary *summary)
 {
-    int volume = ip->u.vnode.volumeId;
-    int rwvolume = volume;
-    register n, nSpecial;
+    VolumeId volume = ip->u.vnode.volumeId;
+    VolumeId rwvolume = volume;
+    register int n, nSpecial;
     register Unique maxunique;
     n = nSpecial = 0;
     maxunique = 0;
@@ -1007,7 +1041,7 @@ CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
 }
 
 int
-OnlyOneVolume(struct ViceInodeInfo *inodeinfo, int singleVolumeNumber, void *rock)
+OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
 {
     if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
        return (inodeinfo->u.special.parentId == singleVolumeNumber);
@@ -1021,10 +1055,11 @@ OnlyOneVolume(struct ViceInodeInfo *inodeinfo, int singleVolumeNumber, void *roc
  * be unlinked by the caller.
  */
 int
-GetInodeSummary(char *path, VolumeId singleVolumeNumber)
+GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
 {
     struct afs_stat status;
     int forceSal, err;
+    int code;
     struct ViceInodeInfo *ip;
     struct InodeSummary summary;
     char summaryFileName[50];
@@ -1041,23 +1076,22 @@ GetInodeSummary(char *path, VolumeId singleVolumeNumber)
 
     /* This file used to come from vfsck; cobble it up ourselves now... */
     if ((err =
-        ListViceInodes(dev, fileSysPath, path,
+        ListViceInodes(dev, fileSysPath, inodeFile,
                        singleVolumeNumber ? OnlyOneVolume : 0,
                        singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
        if (err == -2) {
-           Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, path, dev);
+           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;
        }
-       unlink(path);
        Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
     }
     if (forceSal && !ForceSalvage) {
        Log("***Forced salvage of all volumes on this partition***\n");
        ForceSalvage = 1;
     }
-    inodeFd = afs_open(path, O_RDWR);
+    fseek(inodeFile, 0L, SEEK_SET);
+    inodeFd = fileno(inodeFile);
     if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
-       unlink(path);
        Abort("No inode description file for \"%s\"; not salvaged\n", dev);
     }
     tdir = (tmpdir ? tmpdir : part);
@@ -1070,18 +1104,29 @@ GetInodeSummary(char *path, VolumeId singleVolumeNumber)
 #endif
     summaryFile = afs_fopen(summaryFileName, "a+");
     if (summaryFile == NULL) {
-       close(inodeFd);
-       unlink(path);
        Abort("Unable to create inode summary file\n");
     }
+
+#ifdef AFS_NT40_ENV
+    /* Using nt_unlink here since we're really using the delete on close
+     * 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.
+     */
+    code = nt_unlink(summaryFileName);
+#else
+    code = unlink(summaryFileName);
+#endif
+    if (code < 0) {
+       Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
+    }
+
     if (!canfork || debug || Fork() == 0) {
        int nInodes;
        unsigned long st_size=(unsigned long) status.st_size;
        nInodes = st_size / sizeof(struct ViceInodeInfo);
        if (nInodes == 0) {
            fclose(summaryFile);
-           close(inodeFd);
-           unlink(summaryFileName);
            if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
                RemoveTheForce(fileSysPath);
            else {
@@ -1102,27 +1147,18 @@ GetInodeSummary(char *path, VolumeId singleVolumeNumber)
        ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
        if (ip == NULL) {
            fclose(summaryFile);
-           close(inodeFd);
-           unlink(path);
-           unlink(summaryFileName);
            Abort
                ("Unable to allocate enough space to read inode table; %s not salvaged\n",
                 dev);
        }
        if (read(inodeFd, ip, st_size) != st_size) {
            fclose(summaryFile);
-           close(inodeFd);
-           unlink(path);
-           unlink(summaryFileName);
            Abort("Unable to read inode table; %s not salvaged\n", dev);
        }
        qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
        if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
            || write(inodeFd, ip, st_size) != st_size) {
            fclose(summaryFile);
-           close(inodeFd);
-           unlink(path);
-           unlink(summaryFileName);
            Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
        }
        summary.index = 0;
@@ -1131,7 +1167,6 @@ GetInodeSummary(char *path, VolumeId singleVolumeNumber)
            if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
                Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
                fclose(summaryFile);
-               close(inodeFd);
                return -1;
            }
            summary.index += (summary.nInodes);
@@ -1142,7 +1177,6 @@ GetInodeSummary(char *path, VolumeId singleVolumeNumber)
        if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
            Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
            fclose(summaryFile);
-           close(inodeFd);
            return -1;
        }
        if (canfork && !debug) {
@@ -1152,9 +1186,6 @@ GetInodeSummary(char *path, VolumeId singleVolumeNumber)
     } else {
        if (Wait("Inode summary") == -1) {
            fclose(summaryFile);
-           close(inodeFd);
-           unlink(path);
-           unlink(summaryFileName);
            Exit(1);            /* salvage of this partition aborted */
        }
     }
@@ -1172,8 +1203,6 @@ GetInodeSummary(char *path, VolumeId singleVolumeNumber)
     nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
     Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
     fclose(summaryFile);
-    close(inodeFd);
-    unlink(summaryFileName);
     return 0;
 }
 
@@ -1194,124 +1223,392 @@ CompareVolumes(const void *_p1, const void *_p2)
     return p1->header.id < p2->header.id ? -1 : 1;     /* Both read-only */
 }
 
+/**
+ * Gleans volumeSummary information by asking the fileserver
+ *
+ * @param[in] singleVolumeNumber  the volume we're salvaging. 0 if we're
+ *                                salvaging a whole partition
+ *
+ * @return whether we obtained the volume summary information or not
+ *  @retval 0 success; we obtained the volume summary information
+ *  @retval nonzero we did not get the volume summary information; either the
+ *            fileserver responded with an error, or we are not supposed to
+ *            ask the fileserver for the information (e.g. we are salvaging
+ *            the entire partition or we are not the salvageserver)
+ *
+ * @note for non-DAFS, always returns 1
+ */
+static int
+AskVolumeSummary(VolumeId singleVolumeNumber)
+{
+    afs_int32 code = 1;
+#ifdef FSSYNC_BUILD_CLIENT
+    if (programType == salvageServer) {
+       if (singleVolumeNumber) {
+           FSSYNC_VGQry_response_t q_res;
+           SYNC_response res;
+           struct VolumeSummary *vsp;
+           int i;
+           struct VolumeDiskHeader diskHdr;
+
+           memset(&res, 0, sizeof(res));
+
+           code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
+
+           /*
+            * We must wait for the partition to finish scanning before
+            * can continue, since we will not know if we got the entire
+            * VG membership unless the partition is fully scanned.
+            * We could, in theory, just scan the partition ourselves if
+            * the VG cache is not ready, but we would be doing the exact
+            * same scan the fileserver is doing; it will almost always
+            * be faster to wait for the fileserver. The only exceptions
+            * are if the partition does not take very long to scan, and
+            * in that case it's fast either way, so who cares?
+            */
+           if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
+               Log("waiting for fileserver to finish scanning partition %s...\n",
+                   fileSysPartition->name);
+
+               for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
+                   /* linearly ramp up from 1 to 10 seconds; nothing fancy,
+                    * just so small partitions don't need to wait over 10
+                    * seconds every time, and large partitions are generally
+                    * polled only once every ten seconds. */
+                   sleep((i > 10) ? (i = 10) : i);
+
+                   code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
+               }
+           }
+
+           if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
+               /* This can happen if there's no header for the volume
+                * we're salvaging, or no headers exist for the VG (if
+                * we're salvaging an RW). Act as if we got a response
+                * with no VG members. The headers may be created during
+                * salvaging, if there are inodes in this VG. */
+               code = 0;
+               memset(&q_res, 0, sizeof(q_res));
+               q_res.rw = singleVolumeNumber;
+           }
+
+           if (code) {
+               Log("fileserver refused VGCQuery request for volume %lu on "
+                   "partition %s, code %ld reason %ld\n",
+                   afs_printable_uint32_lu(singleVolumeNumber),
+                   fileSysPartition->name,
+                   afs_printable_int32_ld(code),
+                   afs_printable_int32_ld(res.hdr.reason));
+               goto done;
+           }
+
+           if (q_res.rw != singleVolumeNumber) {
+               Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
+                   afs_printable_uint32_lu(singleVolumeNumber),
+                   afs_printable_uint32_lu(q_res.rw));
+#ifdef SALVSYNC_BUILD_CLIENT
+               if (SALVSYNC_LinkVolume(q_res.rw,
+                                      singleVolumeNumber,
+                                      fileSysPartition->name,
+                                      NULL) != SYNC_OK) {
+                   Log("schedule request failed\n");
+               }
+#endif /* SALVSYNC_BUILD_CLIENT */
+               Exit(SALSRV_EXIT_VOLGROUP_LINK);
+           }
+
+           volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
+           assert(volumeSummaryp != NULL);
+
+           nVolumes = 0;
+           vsp = volumeSummaryp;
+
+           for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
+               char name[VMAXPATHLEN];
+
+               if (!q_res.children[i]) {
+                   continue;
+               }
+
+               if (q_res.children[i] != singleVolumeNumber) {
+                   AskOffline(q_res.children[i], fileSysPartition->name);
+               }
+               code = VReadVolumeDiskHeader(q_res.children[i], fileSysPartition, &diskHdr);
+               if (code) {
+                   Log("Cannot read header for %lu; trying to salvage group anyway\n",
+                       afs_printable_uint32_lu(q_res.children[i]));
+                   code = 0;
+                   continue;
+               }
+
+               DiskToVolumeHeader(&vsp->header, &diskHdr);
+               VolumeExternalName_r(q_res.children[i], name, sizeof(name));
+               vsp->fileName = ToString(name);
+               nVolumes++;
+               vsp++;
+           }
+
+           qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
+                 CompareVolumes);
+       }
+      done:
+       if (code) {
+           Log("Cannot get volume summary from fileserver; falling back to scanning "
+               "entire partition\n");
+       }
+    }
+#endif /* FSSYNC_BUILD_CLIENT */
+    return code;
+}
+
+/**
+ * count how many volume headers are found by VWalkVolumeHeaders.
+ *
+ * @param[in] dp   the disk partition (unused)
+ * @param[in] name full path to the .vol header (unused)
+ * @param[in] hdr  the header data (unused)
+ * @param[in] last whether this is the last try or not (unused)
+ * @param[in] rock actually an afs_int32*; the running count of how many
+ *                 volumes we have found
+ *
+ * @retval 0 always
+ */
+static int
+CountHeader(struct DiskPartition64 *dp, const char *name,
+            struct VolumeDiskHeader *hdr, int last, void *rock)
+{
+    afs_int32 *nvols = (afs_int32 *)rock;
+    (*nvols)++;
+    return 0;
+}
+
+/**
+ * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
+ * data.
+ */
+struct SalvageScanParams {
+    VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
+                                  * vol id of the VG we're salvaging */
+    struct VolumeSummary *vsp;   /**< ptr to the current volume summary object
+                                  * we're filling in */
+    afs_int32 nVolumes;          /**< # of vols we've encountered */
+    afs_int32 totalVolumes;      /**< max # of vols we should encounter (the
+                                  * # of vols we've alloc'd memory for) */
+};
+
+/**
+ * records volume summary info found from VWalkVolumeHeaders.
+ *
+ * Found volumes are also taken offline if they are in the specific volume
+ * group we are looking for.
+ *
+ * @param[in] dp   the disk partition
+ * @param[in] name full path to the .vol header
+ * @param[in] hdr  the header data
+ * @param[in] last 1 if this is the last try to read the header, 0 otherwise
+ * @param[in] rock actually a struct SalvageScanParams*, containing the
+ *                 information needed to record the volume summary data
+ *
+ * @return operation status
+ *  @retval 0 success
+ *  @retval 1 volume header is mis-named and should be deleted
+ */
+static int
+RecordHeader(struct DiskPartition64 *dp, const char *name,
+             struct VolumeDiskHeader *hdr, int last, void *rock)
+{
+    char nameShouldBe[64];
+    struct SalvageScanParams *params;
+    struct VolumeSummary summary;
+    VolumeId singleVolumeNumber;
+
+    params = (struct SalvageScanParams *)rock;
+
+    singleVolumeNumber = params->singleVolumeNumber;
+
+    DiskToVolumeHeader(&summary.header, hdr);
+
+    if (singleVolumeNumber && summary.header.id == singleVolumeNumber
+        && summary.header.parent != singleVolumeNumber) {
+
+       if (programType == salvageServer) {
+#ifdef SALVSYNC_BUILD_CLIENT
+           Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
+               summary.header.id, summary.header.parent);
+           if (SALVSYNC_LinkVolume(summary.header.parent,
+                                   summary.header.id,
+                                   dp->name,
+                                   NULL) != SYNC_OK) {
+               Log("schedule request failed\n");
+           }
+#endif
+           Exit(SALSRV_EXIT_VOLGROUP_LINK);
+
+       } else {
+           Log("%u is a read-only volume; not salvaged\n",
+               singleVolumeNumber);
+           Exit(1);
+       }
+    }
+
+    if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
+       || summary.header.parent == singleVolumeNumber) {
+
+       /* check if the header file is incorrectly named */
+       int badname = 0;
+       const char *base = strrchr(name, '/');
+       if (base) {
+           base++;
+       } else {
+           base = name;
+       }
+
+       (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
+                          VFORMAT, afs_printable_uint32_lu(summary.header.id));
+
+
+       if (strcmp(nameShouldBe, base)) {
+           /* .vol file has wrong name; retry/delete */
+           badname = 1;
+       }
+
+       if (!badname || last) {
+           /* only offline the volume if the header is good, or if this is
+            * the last try looking at it; avoid AskOffline'ing the same vol
+            * multiple times */
+
+           if (singleVolumeNumber 
+               && summary.header.id != singleVolumeNumber) {
+               /* don't offline singleVolumeNumber; we already did that
+                * earlier */
+
+               AskOffline(summary.header.id, fileSysPartition->name);
+           }
+       }
+       if (badname) {
+           if (last && !Showmode) {
+               Log("Volume header file %s is incorrectly named (should be %s "
+                   "not %s); %sdeleted (it will be recreated later, if "
+                   "necessary)\n", name, nameShouldBe, base,
+                   (Testing ? "it would have been " : ""));
+           }
+           return 1;
+       }
+
+       summary.fileName = ToString(base);
+       params->nVolumes++;
+
+       if (params->nVolumes > params->totalVolumes) {
+           /* We found more volumes than we found on the first partition walk;
+            * apparently something created a volume while we were
+            * partition-salvaging, or we found more than 20 vols when salvaging a
+            * particular volume. Abort if we detect this, since other programs
+            * supposed to not touch the partition while it is partition-salvaging,
+            * and we shouldn't find more than 20 vols in a VG.
+            */
+           Abort("Found %ld vol headers, but should have found at most %ld! "
+                 "Make sure the volserver/fileserver are not running at the "
+                 "same time as a partition salvage\n",
+                 afs_printable_int32_ld(params->nVolumes),
+                 afs_printable_int32_ld(params->totalVolumes));
+       }
+
+       memcpy(params->vsp, &summary, sizeof(summary));
+       params->vsp++;
+    }
+
+    return 0;
+}
+
+/**
+ * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
+ *
+ * If the header could not be read in at all, the header is always unlinked.
+ * If instead RecordHeader said the header was bad (that is, the header file
+ * is mis-named), we only unlink if we are doing a partition salvage, as
+ * opposed to salvaging a specific volume group.
+ *
+ * @param[in] dp   the disk partition
+ * @param[in] name full path to the .vol header
+ * @param[in] hdr  header data, or NULL if the header could not be read
+ * @param[in] rock actually a struct SalvageScanParams*, with some information
+ *                 about the scan
+ */
+static void
+UnlinkHeader(struct DiskPartition64 *dp, const char *name,
+             struct VolumeDiskHeader *hdr, void *rock)
+{
+    struct SalvageScanParams *params;
+    int dounlink = 0;
+
+    params = (struct SalvageScanParams *)rock;
+
+    if (!hdr) {
+       /* no header; header is too bogus to read in at all */
+       if (!Showmode) {
+           Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
+       }
+       if (!Testing) {
+           dounlink = 1;
+       }
+
+    } else if (!params->singleVolumeNumber) {
+       /* We were able to read in a header, but RecordHeader said something
+        * was wrong with it. We only unlink those if we are doing a partition
+        * salvage. */
+       if (!Testing) {
+           dounlink = 1;
+       }
+    }
+
+    if (dounlink && unlink(name)) {
+       Log("Error %d while trying to unlink %s\n", errno, name);
+    }
+}
+
 void
 GetVolumeSummary(VolumeId singleVolumeNumber)
 {
-    DIR *dirp = NULL;
     afs_int32 nvols = 0;
-    struct VolumeSummary *vsp, vs;
-    struct VolumeDiskHeader diskHeader;
-    struct dirent *dp;
+    struct SalvageScanParams params;
+    int code;
+
+    if (AskVolumeSummary(singleVolumeNumber) == 0) {
+       /* we successfully got the vol information from the fileserver; no
+        * need to scan the partition */
+       return;
+    }
 
-    /* Get headers from volume directory */
-    dirp = opendir(fileSysPath);
-    if (dirp  == NULL)
-       Abort("Can't read directory %s; not salvaged\n", fileSysPath);
     if (!singleVolumeNumber) {
-       while ((dp = readdir(dirp))) {
-           char *p = dp->d_name;
-           p = strrchr(dp->d_name, '.');
-           if (p != NULL && strcmp(p, VHDREXT) == 0) {
-               int fd;
-               char name[64];
-               sprintf(name, "%s/%s", fileSysPath, dp->d_name);
-               if ((fd = afs_open(name, O_RDONLY)) != -1
-                   && read(fd, (char *)&diskHeader, sizeof(diskHeader))
-                   == sizeof(diskHeader)
-                   && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
-                   DiskToVolumeHeader(&vs.header, &diskHeader);
-                   nvols++;
-               }
-               close(fd);
-           }
+       /* Count how many volumes we have in /vicepX */
+       code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, CountHeader,
+                                 NULL, &nvols);
+       if (code < 0) {
+           Abort("Can't read directory %s; not salvaged\n", fileSysPath);
        }
-#ifdef AFS_NT40_ENV
-       closedir(dirp);
-       dirp = opendir(".");    /* No rewinddir for NT */
-#else
-       rewinddir(dirp);
-#endif
        if (!nvols)
            nvols = 1;
-       volumeSummaryp =
-           (struct VolumeSummary *)malloc(nvols *
-                                          sizeof(struct VolumeSummary));
-    } else
-       volumeSummaryp =
-           (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
+    } else {
+       nvols = VOL_VG_MAX_VOLS;
+    }
+
+    volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
     assert(volumeSummaryp != NULL);
 
-    nVolumes = 0;
-    vsp = volumeSummaryp;
-    while ((dp = readdir(dirp))) {
-       char *p = dp->d_name;
-       p = strrchr(dp->d_name, '.');
-       if (p != NULL && strcmp(p, VHDREXT) == 0) {
-           int error = 0;
-           int fd;
-           char name[64];
-           sprintf(name, "%s/%s", fileSysPath, dp->d_name);
-           if ((fd = afs_open(name, O_RDONLY)) == -1
-               || read(fd, &diskHeader, sizeof(diskHeader))
-               != sizeof(diskHeader)
-               || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
-               error = 1;
-           }
-           close(fd);
-           if (error) {
-               if (!singleVolumeNumber) {
-                   if (!Showmode)
-                       Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
-                   if (!Testing)
-                       unlink(dp->d_name);
-               }
-           } else {
-               char nameShouldBe[64];
-               DiskToVolumeHeader(&vsp->header, &diskHeader);
-               if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
-                   && vsp->header.parent != singleVolumeNumber) {
-                   if (programType == salvageServer) {
-#ifdef SALVSYNC_BUILD_CLIENT
-                       Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
-                           vsp->header.id, vsp->header.parent);
-                       if (SALVSYNC_LinkVolume(vsp->header.parent,
-                                               vsp->header.id,
-                                               fileSysPartition->name,
-                                               NULL) != SYNC_OK) {
-                           Log("schedule request failed\n");
-                       }
-#endif
-                       Exit(SALSRV_EXIT_VOLGROUP_LINK);
-                   } else {
-                       Log("%u is a read-only volume; not salvaged\n",
-                           singleVolumeNumber);
-                       Exit(1);
-                   }
-               }
-               if (!singleVolumeNumber
-                   || (vsp->header.id == singleVolumeNumber
-                       || vsp->header.parent == singleVolumeNumber)) {
-                   (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
-                                      VFORMAT, vsp->header.id);
-                   if (singleVolumeNumber 
-                       && vsp->header.id != singleVolumeNumber)
-                       AskOffline(vsp->header.id, fileSysPartition->name);
-                   if (strcmp(nameShouldBe, dp->d_name)) {
-                       if (!Showmode)
-                           Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", dp->d_name, (Testing ? "it would have been " : ""));
-                       if (!Testing)
-                           unlink(dp->d_name);
-                   } else {
-                       vsp->fileName = ToString(dp->d_name);
-                       nVolumes++;
-                       vsp++;
-                   }
-               }
-           }
-           close(fd);
-       }
+    params.singleVolumeNumber = singleVolumeNumber;
+    params.vsp = volumeSummaryp;
+    params.nVolumes = 0;
+    params.totalVolumes = nvols;
+
+    /* walk the partition directory of volume headers and record the info
+     * about them; unlinking invalid headers */
+    code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, RecordHeader,
+                              UnlinkHeader, &params);
+    if (code < 0) {
+       Abort("Failed to get volume header summary\n");
     }
-    closedir(dirp);
+    nVolumes = params.nVolumes;
+
     qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
          CompareVolumes);
 }
@@ -1673,43 +1970,148 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
                        register struct ViceInodeInfo *inodes, int RW,
                        int check, int *deleteMe)
 {
-    int headerFd = 0;
     int i;
     register struct ViceInodeInfo *ip;
     int allinodesobsolete = 1;
     struct VolumeDiskHeader diskHeader;
+    afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
+    int *skip;
+
+    /* keeps track of special inodes that are probably 'good'; they are
+     * referenced in the vol header, and are included in the given inodes
+     * array */
+    struct {
+       int valid;
+       Inode inode;
+    } goodspecial[MAXINODETYPE];
 
     if (deleteMe)
        *deleteMe = 0;
+
+    memset(goodspecial, 0, sizeof(goodspecial));
+
+    skip = malloc(isp->nSpecialInodes * sizeof(*skip));
+    if (skip) {
+       memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
+    } else {
+       Log("cannot allocate memory for inode skip array when salvaging "
+           "volume %lu; not performing duplicate special inode recovery\n",
+           afs_printable_uint32_lu(isp->volumeId));
+       /* still try to perform the salvage; the skip array only does anything
+        * if we detect duplicate special inodes */
+    }
+
+    /*
+     * First, look at the special inodes and see if any are referenced by
+     * the existing volume header. If we find duplicate special inodes, we
+     * can use this information to use the referenced inode (it's more
+     * likely to be the 'good' one), and throw away the duplicates.
+     */
+    if (isp->volSummary && skip) {
+       /* use tempHeader, so we can use the stuff[] array to easily index
+        * into the isp->volSummary special inodes */
+       memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
+
+       for (i = 0; i < isp->nSpecialInodes; i++) {
+           ip = &inodes[isp->index + i];
+           if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
+               /* will get taken care of in a later loop */
+               continue;
+           }
+           if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
+               goodspecial[ip->u.special.type-1].valid = 1;
+               goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
+           }
+       }
+    }
+
     memset(&tempHeader, 0, sizeof(tempHeader));
     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
     tempHeader.stamp.version = VOLUMEHEADERVERSION;
     tempHeader.id = isp->volumeId;
     tempHeader.parent = isp->RWvolumeId;
+
     /* Check for duplicates (inodes are sorted by type field) */
     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
        ip = &inodes[isp->index + i];
        if (ip->u.special.type == (ip + 1)->u.special.type) {
-           if (!Showmode)
-               Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
-           return -1;
+           afs_ino_str_t stmp1, stmp2;
+
+           if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
+               /* Will be caught in the loop below */
+               continue;
+           }
+           if (!Showmode) {
+               Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
+                   ip->u.special.type, isp->volumeId,
+                   PrintInode(stmp1, ip->inodeNumber),
+                   PrintInode(stmp2, (ip+1)->inodeNumber));
+           }
+           if (skip && goodspecial[ip->u.special.type-1].valid) {
+               Inode gi = goodspecial[ip->u.special.type-1].inode;
+
+               if (!Showmode) {
+                   Log("using special inode referenced by vol header (%s)\n",
+                       PrintInode(stmp1, gi));
+               }
+
+               /* the volume header references some special inode of
+                * this type in the inodes array; are we it? */
+               if (ip->inodeNumber != gi) {
+                   skip[i] = 1;
+               } else if ((ip+1)->inodeNumber != gi) {
+                   /* in case this is the last iteration; we need to
+                    * make sure we check ip+1, too */
+                   skip[i+1] = 1;
+               }
+           } else {
+               if (!Showmode)
+                   Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
+               if (skip) {
+                   free(skip);
+               }
+               return -1;
+           }
        }
     }
     for (i = 0; i < isp->nSpecialInodes; i++) {
        ip = &inodes[isp->index + i];
        if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
            if (check) {
-               Log("Rubbish header inode\n");
+               Log("Rubbish header inode %s of type %d\n",
+                   PrintInode(NULL, ip->inodeNumber),
+                   ip->u.special.type);
+               if (skip) {
+                   free(skip);
+               }
                return -1;
            }
-           Log("Rubbish header inode; deleted\n");
+           Log("Rubbish header inode %s of type %d; deleted\n",
+               PrintInode(NULL, ip->inodeNumber),
+               ip->u.special.type);
        } else if (!stuff[ip->u.special.type - 1].obsolete) {
-           *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
+           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);
+                   continue;
+               } else {
+                   Log("Ignoring orphan special inode %s of type %d\n",
+                       PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
+                   /* fall through to the ip->linkCount--; line below */
+               }
+           } else {
+               *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
+               allinodesobsolete = 0;
+           }
            if (!check && ip->u.special.type != VI_LINKTABLE)
                ip->linkCount--;        /* Keep the inode around */
-           allinodesobsolete = 0;
        }
     }
+    if (skip) {
+       free(skip);
+    }
+    skip = NULL;
 
     if (allinodesobsolete) {
        if (deleteMe)
@@ -1743,7 +2145,7 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
     if (isp->volSummary == NULL) {
        char path[64];
        char headerName[64];
-       (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, isp->volumeId);
+       (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
        (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
        if (check) {
            Log("No header file for volume %u\n", isp->volumeId);
@@ -1753,11 +2155,11 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
            Log("No header file for volume %u; %screating %s\n",
                isp->volumeId, (Testing ? "it would have been " : ""),
                path);
-       headerFd = afs_open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
-       assert(headerFd != -1);
        isp->volSummary = (struct VolumeSummary *)
            malloc(sizeof(struct VolumeSummary));
        isp->volSummary->fileName = ToString(headerName);
+
+       writefunc = VCreateVolumeDiskHeader;
     } else {
        char path[64];
        char headerName[64];
@@ -1772,7 +2174,7 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
            if (isp->volSummary->fileName) {
                strcpy(headerName, isp->volSummary->fileName);
            } else {
-               (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, isp->volumeId);
+               (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
                isp->volSummary->fileName = ToString(headerName);
            }
            (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
@@ -1782,11 +2184,10 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
            if (check)
                return -1;
 
-           headerFd = afs_open(path, O_RDWR | O_TRUNC, 0644);
-           assert(headerFd != -1);
+           writefunc = VWriteVolumeDiskHeader;
        }
     }
-    if (headerFd) {
+    if (writefunc) {
        memcpy(&isp->volSummary->header, &tempHeader,
               sizeof(struct VolumeHeader));
        if (Testing) {
@@ -1794,15 +2195,16 @@ SalvageVolumeHeaderFile(register struct InodeSummary *isp,
                Log("It would have written a new header file for volume %u\n",
                    isp->volumeId);
        } else {
+           afs_int32 code;
            VolumeHeaderToDisk(&diskHeader, &tempHeader);
-           if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
-               != sizeof(struct VolumeDiskHeader)) {
-               Log("Couldn't rewrite volume header file!\n");
-               close(headerFd);
+           code = (*writefunc)(&diskHeader, fileSysPartition);
+           if (code) {
+               Log("Error %ld writing volume header file for volume %lu\n",
+                   afs_printable_int32_ld(code),
+                   afs_printable_uint32_lu(diskHeader.id));
                return -1;
            }
        }
-       close(headerFd);
     }
     IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
            isp->volSummary->header.volumeInfo);
@@ -2413,9 +2815,10 @@ CopyAndSalvage(register struct DirSummary *dir)
 }
 
 int
-JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
-          Unique unique)
+JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
+          afs_int32 unique)
 {
+    struct DirSummary *dir = (struct DirSummary *)dirVal;
     struct VnodeEssence *vnodeEssence;
     afs_int32 dirOrphaned, todelete;
 
@@ -2486,7 +2889,7 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
            Log("dir vnode %u: %s/%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
        }
        if (!Testing) {
-           ViceFid fid;
+           AFSFid fid;
            fid.Vnode = vnodeNumber;
            fid.Unique = vnodeEssence->unique;
            CopyOnWrite(dir);
@@ -2500,7 +2903,7 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
 
     if (strcmp(name, ".") == 0) {
        if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
-           ViceFid fid;
+           AFSFid fid;
            if (!Showmode)
                Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
            if (!Testing) {
@@ -2517,7 +2920,7 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
        }
        dir->haveDot = 1;
     } else if (strcmp(name, "..") == 0) {
-       ViceFid pa;
+       AFSFid pa;
        if (dir->parent) {
            struct VnodeEssence *dotdot;
            pa.Vnode = dir->parent;
@@ -2713,8 +3116,16 @@ DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
            vep->owner = vnode->owner;
            vep->group = vnode->group;
            if (vnode->type == vDirectory) {
-               assert(class == vLarge);
-               vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
+               if (class != vLarge) {
+                   VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
+                   vip->nAllocatedVnodes--;
+                   memset(vnode, 0, sizeof(vnode));
+                   IH_IWRITE(vnodeInfo[vSmall].handle,
+                             vnodeIndexOffset(vcp, vnodeNumber),
+                             (char *)&vnode, sizeof(vnode));
+                   VolumeChanged = 1;
+               } else
+                   vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
            }
        }
     }
@@ -2868,7 +3279,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
     afs_int32 v, pv;
     IHandle_t *h;
     afs_sfsize_t nBytes;
-    ViceFid pa;
+    AFSFid pa;
     VnodeId LFVnode, ThisVnode;
     Unique LFUnique, ThisUnique;
     char npath[128];
@@ -2952,7 +3363,7 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
                 * won't be visible there.
                 */
                if (class == vLarge) {
-                   ViceFid pa;
+                   AFSFid pa;
                    DirHandle dh;
 
                    /* Remove and recreate the ".." entry in this orphaned directory */
@@ -3186,8 +3597,25 @@ MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
                if (!Showmode)
                    Log("it will be deleted instead.  It should be recloned.\n");
            }
-           if (!Testing)
-               unlink(isp->volSummary->fileName);
+           if (!Testing) {
+               afs_int32 code;
+               char path[64];
+               sprintf(path, "%s/%s", fileSysPath, isp->volSummary->fileName);
+
+               code = VDestroyVolumeDiskHeader(fileSysPartition, isp->volumeId, isp->RWvolumeId);
+               if (code) {
+                   Log("Error %ld destroying volume disk header for volume %lu\n",
+                       afs_printable_int32_ld(code),
+                       afs_printable_uint32_lu(isp->volumeId));
+               }
+
+               /* make sure we actually delete the fileName 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);
+               }
+           }
        }
     } else if (!check) {
        Log("%s salvage was unsuccessful: read-write volume %u\n", message,
@@ -3201,9 +3629,12 @@ void
 AskOffline(VolumeId volumeId, char * partition)
 {
     afs_int32 code, i;
+    SYNC_response res;
+
+    memset(&res, 0, sizeof(res));
 
     for (i = 0; i < 3; i++) {
-       code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
+       code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
 
        if (code == SYNC_OK) {
            break;
@@ -3234,6 +3665,49 @@ AskOffline(VolumeId volumeId, char * partition)
        Log("AskOffline:  request for fileserver to take volume offline failed; salvage aborting.\n");
        Abort("Salvage aborted\n");
     }
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    /* set inUse = programType in the volume header. We do this in case
+     * the fileserver restarts/crashes while we are salvaging.
+     * Otherwise, the fileserver could attach the volume again on
+     * startup while we are salvaging, which would be very bad, or
+     * schedule another salvage while we are salvaging, which would be
+     * annoying. */
+    if (!Testing) {
+       IHandle_t *h;
+       struct VolumeHeader header;
+       struct VolumeDiskHeader diskHeader;
+       struct VolumeDiskData volHeader;
+
+       code = VReadVolumeDiskHeader(volumeId, fileSysPartition, &diskHeader);
+       if (code) {
+           return;
+       }
+
+       DiskToVolumeHeader(&header, &diskHeader);
+
+       IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
+       if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
+           volHeader.stamp.magic != VOLUMEINFOMAGIC) {
+
+           IH_RELEASE(h);
+           return;
+       }
+
+       volHeader.inUse = programType;
+
+       /* If we can't re-write the header, bail out and error. We don't
+        * assert when reading the header, since it's possible the
+        * header isn't really there (when there's no data associated
+        * with the volume; we just delete the vol header file in that
+        * 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));
+
+       IH_RELEASE(h);
+    }
+#endif /* AFS_DEMAND_ATTACH_FS */
 }
 
 void
@@ -3299,7 +3773,7 @@ PrintInodeList(void)
     register struct ViceInodeInfo *ip;
     struct ViceInodeInfo *buf;
     struct afs_stat status;
-    register nInodes;
+    register int nInodes;
 
     assert(afs_fstat(inodeFd, &status) == 0);
     buf = (struct ViceInodeInfo *)malloc(status.st_size);
@@ -3364,8 +3838,7 @@ Fork(void)
 }
 
 void
-Exit(code)
-     int code;
+Exit(int code)
 {
     if (ShowLog)
        showlog();
@@ -3481,8 +3954,10 @@ showlog(void)
     }
 #endif
 
-    rewind(logFile);
-    fclose(logFile);
+    if (logFile) {
+       rewind(logFile);
+       fclose(logFile);
+    }
 
     logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
 
@@ -3545,7 +4020,7 @@ Abort(const char *format, ...)
 }
 
 char *
-ToString(char *s)
+ToString(const char *s)
 {
     register char *p;
     p = (char *)malloc(strlen(s) + 1);