vol-salvage: Unlock volumes before exiting
[openafs.git] / src / vol / vol-salvage.c
index b212265..a689a29 100644 (file)
@@ -89,22 +89,11 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
 #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
@@ -112,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/afs_assert.h>
 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
 #if defined(AFS_VFSINCL_ENV)
 #include <sys/vnode.h>
@@ -130,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) && !defined(AFS_ARM_DARWIN_ENV)
+#if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_DARWIN_ENV)
 #include <sys/inode.h>
 #endif
 #endif
@@ -141,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
@@ -162,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
@@ -170,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"
@@ -184,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"
@@ -252,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. */
@@ -299,6 +280,7 @@ static int IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode);
 static int AskVolumeSummary(struct SalvInfo *salvinfo,
                             VolumeId singleVolumeNumber);
 static void MaybeAskOnline(struct SalvInfo *salvinfo, VolumeId volumeId);
+static void AskError(struct SalvInfo *salvinfo, VolumeId volumeId);
 
 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
 static int LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId);
@@ -504,12 +486,11 @@ SalvageFileSysParallel(struct DiskPartition64 *partP)
 
     if (partP) {
        /* We have a partition to salvage. Copy it into thisjob */
-       thisjob = (struct job *)malloc(sizeof(struct job));
+       thisjob = calloc(1, sizeof(struct job));
        if (!thisjob) {
            Log("Can't salvage '%s'. Not enough memory\n", partP->name);
            return;
        }
-       memset(thisjob, 0, sizeof(struct job));
        thisjob->partP = partP;
        thisjob->jobnumb = jobcount;
        jobcount++;
@@ -849,11 +830,6 @@ SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
 
     if (GetInodeSummary(salvinfo, inodeFile, singleVolumeNumber) < 0) {
        OS_CLOSE(inodeFile);
-       if (singleVolumeNumber) {
-           /* the volume group -- let alone the volume -- does not exist,
-            * but we checked it out, so give it back to the fileserver */
-           AskDelete(salvinfo, singleVolumeNumber);
-       }
        return;
     }
     salvinfo->inodeFd = inodeFile;
@@ -882,6 +858,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;
@@ -895,7 +881,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
@@ -904,7 +890,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;
                }
            }
@@ -912,12 +898,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);
     }
 
@@ -976,7 +967,14 @@ void
 DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp)
 {
     char path[64];
-    sprintf(path, "%s" OS_DIRSEP "%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 " : ""));
@@ -989,7 +987,7 @@ 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) {
@@ -1000,7 +998,6 @@ DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp
        }
        vsp->deleted = 1;
     }
-    vsp->fileName = 0;
 }
 
 int
@@ -1163,6 +1160,8 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
     char *part = salvinfo->fileSysPath;
     char *tdir;
     int i;
+    int retcode = 0;
+    int deleted = 0;
     afs_sfsize_t st_size;
 
     /* This file used to come from vfsck; cobble it up ourselves now... */
@@ -1172,7 +1171,8 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
                        singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
        if (err == -2) {
            Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
-           return -1;
+           retcode = -1;
+           goto error;
        }
        Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
     }
@@ -1225,19 +1225,40 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
            else {
                struct VolumeSummary *vsp;
                int i;
+               int foundSVN = 0;
 
                GetVolumeSummary(salvinfo, singleVolumeNumber);
 
                for (i = 0, vsp = salvinfo->volumeSummaryp; i < salvinfo->nVolumes; i++) {
-                   if (vsp->fileName)
+                   if (vsp->unused) {
+                       if (vsp->header.id == singleVolumeNumber) {
+                           foundSVN = 1;
+                       }
                        DeleteExtraVolumeHeaderFile(salvinfo, vsp);
+                   }
+               }
+
+               if (!foundSVN) {
+                   if (Testing) {
+                       MaybeAskOnline(salvinfo, singleVolumeNumber);
+                   } else {
+                       /* make sure we get rid of stray .vol headers, even if
+                        * they're not in our volume summary (might happen if
+                        * e.g. something else created them and they're not in the
+                        * fileserver VGC) */
+                       VDestroyVolumeDiskHeader(salvinfo->fileSysPartition,
+                                                singleVolumeNumber, 0 /*parent*/);
+                       AskDelete(salvinfo, singleVolumeNumber);
+                   }
                }
            }
            Log("%s vice inodes on %s; not salvaged\n",
                singleVolumeNumber ? "No applicable" : "No", dev);
-           return -1;
+           retcode = -1;
+           deleted = 1;
+           goto error;
        }
-       ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
+       ip = malloc(nInodes*sizeof(struct ViceInodeInfo));
        if (ip == NULL) {
            OS_CLOSE(summaryFile);
            Abort
@@ -1261,7 +1282,8 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
            if (OS_WRITE(summaryFile, &summary, sizeof(summary)) != sizeof(summary)) {
                Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
                OS_CLOSE(summaryFile);
-               return -1;
+               retcode = -1;
+               goto error;
            }
            summary.index += (summary.nInodes);
            nInodes -= summary.nInodes;
@@ -1273,7 +1295,8 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
        if (OS_SYNC(summaryFile) == -1) {
            Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
            OS_CLOSE(summaryFile);
-           return -1;
+           retcode = -1;
+           goto error;
        }
        if (canfork && !debug) {
            ShowLog = 0;
@@ -1290,7 +1313,7 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
     osi_Assert(st_size >= 0);
     if (st_size != 0) {
        int ret;
-       salvinfo->inodeSummary = (struct InodeSummary *)malloc(st_size);
+       salvinfo->inodeSummary = malloc(st_size);
        osi_Assert(salvinfo->inodeSummary != NULL);
        /* For GNU we need to do lseek to get the file pointer moved. */
        osi_Assert(OS_SEEK(summaryFile, 0, SEEK_SET) == 0);
@@ -1303,7 +1326,13 @@ GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolume
     }
     Log("%d nVolumesInInodeFile %lu \n",salvinfo->nVolumesInInodeFile,(unsigned long)st_size);
     OS_CLOSE(summaryFile);
-    return 0;
+
+ error:
+    if (retcode && singleVolumeNumber && !deleted) {
+       AskError(salvinfo, singleVolumeNumber);
+    }
+
+    return retcode;
 }
 
 /* Comparison routine for volume sort.
@@ -1451,7 +1480,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++;
            }
@@ -1537,6 +1566,8 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
 
     params = (struct SalvageScanParams *)rock;
 
+    memset(&summary, 0, sizeof(summary));
+
     singleVolumeNumber = params->singleVolumeNumber;
     salvinfo = params->salvinfo;
 
@@ -1620,7 +1651,7 @@ RecordHeader(struct DiskPartition64 *dp, const char *name,
            return 1;
        }
 
-       summary.fileName = ToString(base);
+       summary.unused = 1;
        params->nVolumes++;
 
        if (params->nVolumes > params->totalVolumes) {
@@ -1792,7 +1823,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
@@ -1837,7 +1868,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;
@@ -1902,7 +1933,7 @@ DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nV
     for (i = 0, totalInodes = 0; i < nVols; i++)
        totalInodes += isp[i].nInodes;
     size = totalInodes * sizeof(struct ViceInodeInfo);
-    inodes = (struct ViceInodeInfo *)malloc(size);
+    inodes = malloc(size);
     allInodes = inodes - isp->index;   /* this would the base of all the inodes
                                         * for the partition, if all the inodes
                                         * had been read into memory */
@@ -1921,6 +1952,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);
@@ -2141,10 +2184,8 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
 
     memset(goodspecial, 0, sizeof(goodspecial));
 
-    skip = malloc(isp->nSpecialInodes * sizeof(*skip));
-    if (skip) {
-       memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
-    } else {
+    skip = calloc(isp->nSpecialInodes, sizeof(*skip));
+    if (skip == NULL) {
        Log("cannot allocate memory for inode skip array when salvaging "
            "volume %lu; not performing duplicate special inode recovery\n",
            afs_printable_uint32_lu(isp->volumeId));
@@ -2285,7 +2326,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;
@@ -2312,7 +2353,6 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
                isp->volumeId, (Testing ? "it would have been " : ""),
                path);
        isp->volSummary = calloc(1, sizeof(struct VolumeSummary));
-       isp->volSummary->fileName = ToString(headerName);
 
        writefunc = VCreateVolumeDiskHeader;
     } else {
@@ -2325,14 +2365,7 @@ SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
        if (memcmp
            (&isp->volSummary->header, &tempHeader,
             sizeof(struct VolumeHeader))) {
-           /* We often remove the name before calling us, so we make a fake one up */
-           if (isp->volSummary->fileName) {
-               strcpy(headerName, isp->volSummary->fileName);
-           } else {
-               snprintf(headerName, sizeof headerName, VFORMAT,
-                        afs_printable_uint32_lu(isp->volumeId));
-               isp->volSummary->fileName = ToString(headerName);
-           }
+           VolumeExternalName_r(isp->volumeId, headerName, sizeof(headerName));
            snprintf(path, sizeof path, "%s" OS_DIRSEP "%s",
                     salvinfo->fileSysPath, headerName);
 
@@ -2385,7 +2418,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) {
@@ -2438,6 +2471,14 @@ SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
         * it below */
        memset(&header, 0, sizeof(header));
     }
+#ifdef AFS_NAMEI_ENV
+    if (namei_FixSpecialOGM(fdP, check)) {
+       Log("Error with namei header OGM data (%s)\n", sp->description);
+       FDH_REALLYCLOSE(fdP);
+       IH_RELEASE(specH);
+       return -1;
+    }
+#endif
     if (sp->inodeType == VI_VOLINFO
        && header.volumeInfo.destroyMe == DESTROY_ME) {
        if (deleteMe)
@@ -2472,7 +2513,7 @@ 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;
            nBytes =
                FDH_PWRITE(fdP, (char *)&header.volumeInfo,
@@ -2944,7 +2985,7 @@ CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
     }
     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,
@@ -2998,7 +3039,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        }
        if (!Testing) {
            CopyOnWrite(salvinfo, dir);
-           osi_Assert(Delete(&dir->dirHandle, name) == 0);
+           osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
        }
        return 0;
     }
@@ -3030,7 +3071,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        if (!unique) {
            if (!Testing) {
                CopyOnWrite(salvinfo, dir);
-               osi_Assert(Delete(&dir->dirHandle, name) == 0);
+               osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
            }
            return 0;
        }
@@ -3060,9 +3101,9 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
            fid.Vnode = vnodeNumber;
            fid.Unique = vnodeEssence->unique;
            CopyOnWrite(salvinfo, dir);
-           osi_Assert(Delete(&dir->dirHandle, name) == 0);
+           osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
            if (!todelete)
-               osi_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 */
@@ -3070,20 +3111,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);
-               osi_Assert(Delete(&dir->dirHandle, ".") == 0);
+               osi_Assert(afs_dir_Delete(&dir->dirHandle, ".") == 0);
                fid.Vnode = dir->vnodeNumber;
                fid.Unique = dir->unique;
-               osi_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) {
@@ -3103,8 +3143,8 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
                Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
            if (!Testing) {
                CopyOnWrite(salvinfo, dir);
-               osi_Assert(Delete(&dir->dirHandle, "..") == 0);
-               osi_Assert(Create(&dir->dirHandle, "..", &pa) == 0);
+               osi_Assert(afs_dir_Delete(&dir->dirHandle, "..") == 0);
+               osi_Assert(afs_dir_Create(&dir->dirHandle, "..", &pa) == 0);
            }
 
            vnodeNumber = pa.Vnode;     /* Get the new Essence */
@@ -3118,7 +3158,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
        }
        if (!Testing) {
            CopyOnWrite(salvinfo, dir);
-           osi_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 */
@@ -3175,10 +3215,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
            Log("FOUND root file: %s" OS_DIRSEP "%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
        if (vnodeIdToClass(vnodeNumber) == vLarge
            && vnodeEssence->name == NULL) {
-           char *n;
-           if ((n = (char *)malloc(strlen(name) + 1)))
-               strcpy(n, name);
-           vnodeEssence->name = n;
+           vnodeEssence->name = strdup(name);
        }
 
        /* The directory entry points to the vnode. Check to see if the
@@ -3212,7 +3249,7 @@ JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
                }
                if (!Testing) {
                    CopyOnWrite(salvinfo, dir);
-                   osi_Assert(Delete(&dir->dirHandle, name) == 0);
+                   osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
                }
                return 0;
            }
@@ -3288,7 +3325,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));
@@ -3411,7 +3448,8 @@ SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
        judge_params.salvinfo = salvinfo;
        judge_params.dir = &dir;
 
-       osi_Assert(EnumerateDir(&dirHandle, JudgeEntry, &judge_params) == 0);
+       osi_Assert(afs_dir_EnumerateDir(&dirHandle, JudgeEntry,
+                                       &judge_params) == 0);
     }
 
     /* Delete the old directory if it was copied in order to salvage.
@@ -3649,7 +3687,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) {
@@ -3698,17 +3736,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);
@@ -3958,8 +3996,8 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                                        &salvinfo->VolumeChanged);
                    pa.Vnode = LFVnode;
                    pa.Unique = LFUnique;
-                   osi_Assert(Delete(&dh, "..") == 0);
-                   osi_Assert(Create(&dh, "..", &pa) == 0);
+                   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.
@@ -3982,7 +4020,7 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
                             ThisVnode, ThisUnique);
 
                    CopyOnWrite(salvinfo, &rootdir);
-                   code = Create(&rootdir.dirHandle, npath, &pa);
+                   code = afs_dir_Create(&rootdir.dirHandle, npath, &pa);
                    if (!code)
                        break;
 
@@ -4124,22 +4162,41 @@ SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t *
            afs_printable_uint32_lu(vid));
     }
 
+    if (!Testing && salvinfo->VolumeChanged) {
 #ifdef FSSYNC_BUILD_CLIENT
-    if (!Testing && salvinfo->VolumeChanged && salvinfo->useFSYNC) {
-       afs_int32 fsync_code;
-
-       fsync_code = FSYNC_VolOp(vid, NULL, FSYNC_VOL_BREAKCBKS, FSYNC_SALVAGE, NULL);
-       if (fsync_code) {
-           Log("Error trying to tell the fileserver to break callbacks for "
-               "changed volume %lu; error code %ld\n",
-               afs_printable_uint32_lu(vid),
-               afs_printable_int32_ld(fsync_code));
-       } else {
-           salvinfo->VolumeChanged = 0;
+       if (salvinfo->useFSYNC) {
+           afs_int32 fsync_code;
+
+           fsync_code = FSYNC_VolOp(vid, NULL, FSYNC_VOL_BREAKCBKS, FSYNC_SALVAGE, NULL);
+           if (fsync_code) {
+               Log("Error trying to tell the fileserver to break callbacks for "
+                   "changed volume %lu; error code %ld\n",
+                   afs_printable_uint32_lu(vid),
+                   afs_printable_int32_ld(fsync_code));
+           } else {
+               salvinfo->VolumeChanged = 0;
+           }
        }
-    }
 #endif /* FSSYNC_BUILD_CLIENT */
 
+#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 */
@@ -4194,7 +4251,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);
@@ -4209,7 +4266,9 @@ MaybeZapVolume(struct SalvInfo *salvinfo, struct InodeSummary *isp,
            if (!Testing) {
                afs_int32 code;
                char path[64];
-               sprintf(path, "%s" OS_DIRSEP "%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) {
@@ -4218,7 +4277,7 @@ 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) {
@@ -4272,7 +4331,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;
@@ -4325,6 +4384,21 @@ LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId)
 }
 #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)
 {
@@ -4380,8 +4454,8 @@ static int isDAFS = -1;
 int
 AskDAFS(void)
 {
-    afs_int32 code, i, ret = 0;
     SYNC_response res;
+    afs_int32 code = 1, i;
 
     /* we don't care if we race. the answer shouldn't change */
     if (isDAFS != -1)
@@ -4389,35 +4463,29 @@ AskDAFS(void)
 
     memset(&res, 0, sizeof(res));
 
-    for (i = 0; i < 3; i++) {
-       code = FSYNC_VolOp(1, NULL,
-                          FSYNC_VOL_QUERY_VOP, FSYNC_SALVAGE, &res);
-
-       if (code == SYNC_OK) {
-           ret = 1;
-           break;
-       } else if (code == SYNC_DENIED) {
-           ret = 1;
-           break;
-       } else if (code == SYNC_BAD_COMMAND) {
-           ret = 0;
-           break;
-       } else if (code == SYNC_FAILED) {
-           if (res.hdr.reason == FSYNC_UNKNOWN_VOLID)
-               ret = 1;
-           else
-               ret = 0;
-           break;
-       } else if (i < 2) {
-           /* try it again */
-           Log("AskDAFS:  request to query fileserver failed; trying again...\n");
+    for (i = 0; code && i < 3; i++) {
+       code = FSYNC_VolOp(0, NULL, FSYNC_VOL_LISTVOLUMES, FSYNC_SALVAGE, &res);
+       if (code) {
+           Log("AskDAFS: FSYNC_VOL_LISTVOLUMES failed with code %ld reason "
+               "%ld (%s); trying again...\n", (long)code, (long)res.hdr.reason,
+               FSYNC_reason2string(res.hdr.reason));
            FSYNC_clientFinis();
            FSYNC_clientInit();
        }
     }
 
-    isDAFS = ret;
-    return ret;
+    if (code) {
+       Log("AskDAFS: could not determine DAFS-ness, assuming not DAFS\n");
+       res.hdr.flags = 0;
+    }
+
+    if ((res.hdr.flags & SYNC_FLAG_DAFS_EXTENSIONS)) {
+       isDAFS = 1;
+    } else {
+       isDAFS = 0;
+    }
+
+    return isDAFS;
 }
 
 static void
@@ -4546,7 +4614,7 @@ PrintInodeList(struct SalvInfo *salvinfo)
 
     st_size = OS_SIZE(salvinfo->inodeFd);
     osi_Assert(st_size >= 0);
-    buf = (struct ViceInodeInfo *)malloc(st_size);
+    buf = malloc(st_size);
     osi_Assert(buf != NULL);
     nInodes = st_size / sizeof(struct ViceInodeInfo);
     osi_Assert(OS_READ(salvinfo->inodeFd, buf, st_size) == st_size);
@@ -4571,17 +4639,6 @@ 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)
 {
@@ -4615,12 +4672,24 @@ Exit(int code)
 
 #ifdef AFS_DEMAND_ATTACH_FS
     if (programType == salvageServer) {
-#ifdef SALVSYNC_BUILD_CLIENT
+       /* release all volume locks before closing down our SYNC channels.
+        * the fileserver may try to online volumes we have checked out when
+        * we close down FSSYNC, so we should make sure we don't have those
+        * volumes locked when it does */
+       struct DiskPartition64 *dp;
+       int i;
+       for (i = 0; i <= VOLMAXPARTS; i++) {
+           dp = VGetPartitionById(i, 0);
+           if (dp) {
+               VLockFileReinit(&dp->volLockFile);
+           }
+       }
+# ifdef SALVSYNC_BUILD_CLIENT
        VDisconnectSALV();
-#endif
-#ifdef FSSYNC_BUILD_CLIENT
+# endif
+# ifdef FSSYNC_BUILD_CLIENT
        VDisconnectFS();
-#endif
+# endif
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
@@ -4756,7 +4825,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);
        }
@@ -4792,9 +4861,8 @@ char *
 ToString(const char *s)
 {
     char *p;
-    p = (char *)malloc(strlen(s) + 1);
+    p = strdup(s);
     osi_Assert(p != NULL);
-    strcpy(p, s);
     return p;
 }