DAFS: VRS_r with VOL_SALVAGE_NO_OFFLINE in attach2
[openafs.git] / src / vol / volume.c
index 370dc70..c85c835 100644 (file)
 #include <afsconfig.h>
 #include <afs/param.h>
 
+#include <roken.h>
+#include <afs/opr.h>
 
-#include <rx/xdr.h>
-#include <afs/afsint.h>
 #include <ctype.h>
-#include <signal.h>
+#include <stddef.h>
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#include <afs/opr.h>
+#include <afs/afsint.h>
+
 #ifndef AFS_NT40_ENV
-#include <sys/param.h>
 #if !defined(AFS_SGI_ENV)
 #ifdef AFS_OSF_ENV
 #include <ufs/fs.h>
 #endif
 #endif
 #else /* AFS_VFSINCL_ENV */
-#if !defined(AFS_AIX_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
+#if !defined(AFS_AIX_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_DARWIN_ENV)
 #include <sys/fs.h>
 #endif
 #endif /* AFS_VFSINCL_ENV */
 #endif /* AFS_OSF_ENV */
 #endif /* AFS_SGI_ENV */
-#endif /* AFS_NT40_ENV */
-#include <errno.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#ifdef AFS_NT40_ENV
-#include <fcntl.h>
-#else
-#include <sys/file.h>
-#endif
-#include <dirent.h>
+#endif /* !AFS_NT40_ENV */
+
 #ifdef AFS_AIX_ENV
 #include <sys/vfs.h>
-#include <fcntl.h>
 #else
 #ifdef AFS_HPUX_ENV
-#include <fcntl.h>
 #include <mntent.h>
 #else
 #if    defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
@@ -79,9 +76,7 @@
 #else
 #ifndef AFS_NT40_ENV
 #if defined(AFS_SGI_ENV)
-#include <fcntl.h>
 #include <mntent.h>
-
 #else
 #ifndef AFS_LINUX20_ENV
 #include <fstab.h>             /* Need to find in libc 5, present in libc 6 */
 #endif
 #endif /* AFS_HPUX_ENV */
 #endif
-#ifndef AFS_NT40_ENV
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/wait.h>
-#include <setjmp.h>
-#ifndef ITIMER_REAL
-#include <sys/time.h>
-#endif /* ITIMER_REAL */
-#endif /* AFS_NT40_ENV */
-#if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
-#include <string.h>
-#else
-#include <strings.h>
-#endif
 
 #include "nfs.h"
 #include <afs/errors.h>
 #include <afs/afssyscalls.h>
 #include "ihandle.h"
 #include <afs/afsutil.h>
-#ifdef AFS_NT40_ENV
-#include <io.h>
-#endif
 #include "daemon_com.h"
 #include "fssync.h"
 #include "salvsync.h"
 #include "partition.h"
 #include "volume_inline.h"
 #include "common.h"
-#include "afs/afs_assert.h"
 #include "vutils.h"
-#ifndef AFS_NT40_ENV
 #include <afs/dir.h>
-#include <unistd.h>
-#endif
-
-#if !defined(offsetof)
-#include <stddef.h>
-#endif
-
-#ifdef O_LARGEFILE
-#define afs_stat       stat64
-#define afs_fstat      fstat64
-#define afs_open       open64
-#else /* !O_LARGEFILE */
-#define afs_stat       stat
-#define afs_fstat      fstat
-#define afs_open       open
-#endif /* !O_LARGEFILE */
 
 #ifdef AFS_PTHREAD_ENV
 pthread_mutex_t vol_glock_mutex;
@@ -168,6 +128,11 @@ pthread_mutex_t vol_salvsync_mutex;
 static volatile sig_atomic_t vol_disallow_salvsync = 0;
 #endif /* AFS_DEMAND_ATTACH_FS */
 
+/**
+ * has VShutdown_r been called / is VShutdown_r running?
+ */
+static int vol_shutting_down = 0;
+
 #ifdef AFS_OSF_ENV
 extern void *calloc(), *realloc();
 #endif
@@ -215,9 +180,6 @@ pthread_t vol_glock_holder = 0;
 #endif
 
 
-#define VOLUME_BITMAP_GROWSIZE 16      /* bytes, => 128vnodes */
-                                       /* Must be a multiple of 4 (1 word) !! */
-
 /* this parameter needs to be tunable at runtime.
  * 128 was really inadequate for largish servers -- at 16384 volumes this
  * puts average chain length at 128, thus an average 65 deref's to find a volptr.
@@ -412,6 +374,13 @@ static void VVByPListEndExclusive_r(struct DiskPartition64 * dp);
 static void VVByPListWait_r(struct DiskPartition64 * dp);
 
 /* online salvager */
+typedef enum {
+    VCHECK_SALVAGE_OK = 0,         /**< no pending salvage */
+    VCHECK_SALVAGE_SCHEDULED = 1,  /**< salvage has been scheduled */
+    VCHECK_SALVAGE_ASYNC = 2,      /**< salvage being scheduled */
+    VCHECK_SALVAGE_DENIED = 3,     /**< salvage not scheduled; denied */
+    VCHECK_SALVAGE_FAIL = 4        /**< salvage not scheduled; failed */
+} vsalvage_check;
 static int VCheckSalvage(Volume * vp);
 #if defined(SALVSYNC_BUILD_CLIENT) || defined(FSSYNC_BUILD_CLIENT)
 static int VScheduleSalvage_r(Volume * vp);
@@ -512,6 +481,10 @@ VOptDefaults(ProgramType pt, VolumePackageOptions *opts)
     opts->canUseSALVSYNC = 0;
 
     opts->interrupt_rxcall = NULL;
+    opts->offline_timeout = -1;
+    opts->offline_shutdown_timeout = -1;
+    opts->usage_threshold = 128;
+    opts->usage_rate_limit = 5;
 
 #ifdef FAST_RESTART
     opts->unsafe_attach = 1;
@@ -557,6 +530,21 @@ VSetVInit_r(int value)
     CV_BROADCAST(&vol_vinit_cond);
 }
 
+static_inline void
+VLogOfflineTimeout(const char *type, afs_int32 timeout)
+{
+    if (timeout < 0) {
+       return;
+    }
+    if (timeout == 0) {
+       Log("VInitVolumePackage: Interrupting clients accessing %s "
+           "immediately\n", type);
+    } else {
+       Log("VInitVolumePackage: Interrupting clients accessing %s "
+           "after %ld second%s\n", type, (long)timeout, timeout==1?"":"s");
+    }
+}
+
 int
 VInitVolumePackage2(ProgramType pt, VolumePackageOptions * opts)
 {
@@ -565,6 +553,18 @@ VInitVolumePackage2(ProgramType pt, VolumePackageOptions * opts)
     programType = pt;
     vol_opts = *opts;
 
+#ifndef AFS_PTHREAD_ENV
+    if (opts->offline_timeout != -1 || opts->offline_shutdown_timeout != -1) {
+       Log("VInitVolumePackage: offline_timeout and/or "
+           "offline_shutdown_timeout was specified, but the volume package "
+           "does not support these for LWP builds\n");
+       return -1;
+    }
+#endif
+    VLogOfflineTimeout("volumes going offline", opts->offline_timeout);
+    VLogOfflineTimeout("volumes going offline during shutdown",
+                       opts->offline_shutdown_timeout);
+
     memset(&VStats, 0, sizeof(VStats));
     VStats.hdr_cache_size = 200;
 
@@ -576,7 +576,7 @@ VInitVolumePackage2(ProgramType pt, VolumePackageOptions * opts)
     } else {
        VLRU_SetOptions(VLRU_SET_ENABLED, 0);
     }
-    osi_Assert(pthread_key_create(&VThread_key, NULL) == 0);
+    opr_Verify(pthread_key_create(&VThread_key, NULL) == 0);
 #endif
 
     MUTEX_INIT(&vol_glock_mutex, "vol glock", MUTEX_DEFAULT, 0);
@@ -615,7 +615,7 @@ VInitVolumePackage2(ProgramType pt, VolumePackageOptions * opts)
 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_CLIENT)
     if (VCanUseSALVSYNC()) {
        /* establish a connection to the salvager at this point */
-       osi_Assert(VConnectSALV() != 0);
+       opr_Verify(VConnectSALV() != 0);
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
@@ -669,13 +669,15 @@ VInitVolumePackage2(ProgramType pt, VolumePackageOptions * opts)
 int
 VInitAttachVolumes(ProgramType pt)
 {
-    osi_Assert(VInit==1);
+    opr_Assert(VInit==1);
     if (pt == fileServer) {
        struct DiskPartition64 *diskP;
        /* Attach all the volumes in this partition */
        for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
            int nAttached = 0, nUnattached = 0;
-           osi_Assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
+           opr_Verify(VAttachVolumesByPartition(diskP,
+                                                &nAttached, &nUnattached)
+                           == 0);
        }
     }
     VOL_LOCK;
@@ -700,7 +702,7 @@ VInitAttachVolumes(ProgramType pt)
 int
 VInitAttachVolumes(ProgramType pt)
 {
-    osi_Assert(VInit==1);
+    opr_Assert(VInit==1);
     if (pt == fileServer) {
        struct DiskPartition64 *diskP;
        struct vinitvolumepackage_thread_t params;
@@ -715,18 +717,20 @@ VInitAttachVolumes(ProgramType pt)
 
        /* create partition work queue */
        for (parts=0, diskP = DiskPartitionList; diskP; diskP = diskP->next, parts++) {
-           dpq = (diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
-           osi_Assert(dpq != NULL);
+           dpq = malloc(sizeof(struct diskpartition_queue_t));
+           opr_Assert(dpq != NULL);
            dpq->diskP = diskP;
            queue_Append(&params,dpq);
        }
 
-       threads = MIN(parts, vol_attach_threads);
+       threads = min(parts, vol_attach_threads);
 
        if (threads > 1) {
            /* spawn off a bunch of initialization threads */
-           osi_Assert(pthread_attr_init(&attrs) == 0);
-           osi_Assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
+           opr_Verify(pthread_attr_init(&attrs) == 0);
+           opr_Verify(pthread_attr_setdetachstate(&attrs,
+                                                  PTHREAD_CREATE_DETACHED)
+                           == 0);
 
            Log("VInitVolumePackage: beginning parallel fileserver startup\n");
            Log("VInitVolumePackage: using %d threads to attach volumes on %d partitions\n",
@@ -736,9 +740,9 @@ VInitAttachVolumes(ProgramType pt)
            for (i=0; i < threads; i++) {
                 AFS_SIGSET_DECL;
                 AFS_SIGSET_CLEAR();
-               osi_Assert(pthread_create
-                      (&tid, &attrs, &VInitVolumePackageThread,
-                       &params) == 0);
+               opr_Verify(pthread_create(&tid, &attrs,
+                                         &VInitVolumePackageThread,
+                                         &params) == 0);
                 AFS_SIGSET_RESTORE();
            }
 
@@ -747,7 +751,7 @@ VInitAttachVolumes(ProgramType pt)
            }
            VOL_UNLOCK;
 
-           osi_Assert(pthread_attr_destroy(&attrs) == 0);
+           opr_Verify(pthread_attr_destroy(&attrs) == 0);
        } else {
            /* if we're only going to run one init thread, don't bother creating
             * another LWP */
@@ -793,7 +797,8 @@ VInitVolumePackageThread(void * args) {
        diskP = dpq->diskP;
        free(dpq);
 
-       osi_Assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
+       opr_Verify(VAttachVolumesByPartition(diskP, &nAttached,
+                                            &nUnattached) == 0);
 
        VOL_LOCK;
     }
@@ -820,7 +825,7 @@ done:
 int
 VInitAttachVolumes(ProgramType pt)
 {
-    osi_Assert(VInit==1);
+    opr_Assert(VInit==1);
     if (pt == fileServer) {
 
        struct DiskPartition64 *diskP;
@@ -837,22 +842,23 @@ VInitAttachVolumes(ProgramType pt)
        MUTEX_INIT(&(pq.mutex), "partq", MUTEX_DEFAULT, 0);
        for (parts = 0, diskP = DiskPartitionList; diskP; diskP = diskP->next, parts++) {
            struct diskpartition_queue_t *dp;
-           dp = (struct diskpartition_queue_t*)malloc(sizeof(struct diskpartition_queue_t));
-           osi_Assert(dp != NULL);
+           dp = malloc(sizeof(struct diskpartition_queue_t));
+           opr_Assert(dp != NULL);
            dp->diskP = diskP;
            queue_Append(&pq, dp);
        }
 
         /* number of worker threads; at least one, not to exceed the number of partitions */
-       threads = MIN(parts, vol_attach_threads);
+       threads = min(parts, vol_attach_threads);
 
         /* create volume work queue */
         queue_Init(&vq);
        CV_INIT(&(vq.cv), "volq", CV_DEFAULT, 0);
        MUTEX_INIT(&(vq.mutex), "volq", MUTEX_DEFAULT, 0);
 
-        osi_Assert(pthread_attr_init(&attrs) == 0);
-        osi_Assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
+        opr_Verify(pthread_attr_init(&attrs) == 0);
+        opr_Verify(pthread_attr_setdetachstate(&attrs,
+                                              PTHREAD_CREATE_DETACHED) == 0);
 
         Log("VInitVolumePackage: beginning parallel fileserver startup\n");
         Log("VInitVolumePackage: using %d threads to pre-attach volumes on %d partitions\n",
@@ -863,21 +869,23 @@ VInitAttachVolumes(ProgramType pt)
            struct vinitvolumepackage_thread_param *params;
             AFS_SIGSET_DECL;
 
-            params = (struct vinitvolumepackage_thread_param *)malloc(sizeof(struct vinitvolumepackage_thread_param));
-            osi_Assert(params);
+            params = malloc(sizeof(struct vinitvolumepackage_thread_param));
+            opr_Assert(params);
             params->pq = &pq;
             params->vq = &vq;
             params->nthreads = threads;
             params->thread = i+1;
 
             AFS_SIGSET_CLEAR();
-           osi_Assert(pthread_create (&tid, &attrs, &VInitVolumePackageThread, (void*)params) == 0);
+           opr_Verify(pthread_create(&tid, &attrs,
+                                     &VInitVolumePackageThread,
+                                     (void*)params) == 0);
             AFS_SIGSET_RESTORE();
        }
 
         VInitPreAttachVolumes(threads, &vq);
 
-        osi_Assert(pthread_attr_destroy(&attrs) == 0);
+        opr_Verify(pthread_attr_destroy(&attrs) == 0);
        CV_DESTROY(&pq.cv);
        MUTEX_DESTROY(&pq.mutex);
        CV_DESTROY(&vq.cv);
@@ -907,15 +915,15 @@ VInitVolumePackageThread(void *args)
     struct volume_init_queue *vq;
     struct volume_init_batch *vb;
 
-    osi_Assert(args);
+    opr_Assert(args);
     params = (struct vinitvolumepackage_thread_param *)args;
     pq = params->pq;
     vq = params->vq;
-    osi_Assert(pq);
-    osi_Assert(vq);
+    opr_Assert(pq);
+    opr_Assert(vq);
 
-    vb = (struct volume_init_batch*)malloc(sizeof(struct volume_init_batch));
-    osi_Assert(vb);
+    vb = malloc(sizeof(struct volume_init_batch));
+    opr_Assert(vb);
     vb->thread = params->thread;
     vb->last = 0;
     vb->size = 0;
@@ -932,9 +940,8 @@ VInitVolumePackageThread(void *args)
             continue;
         }
         while ((vid = VInitNextVolumeId(dirp))) {
-            Volume *vp = (Volume*)malloc(sizeof(Volume));
-            osi_Assert(vp);
-            memset(vp, 0, sizeof(Volume));
+            Volume *vp = calloc(1, sizeof(Volume));
+            opr_Assert(vp);
             vp->device = partition->device;
             vp->partition = partition;
             vp->hashid = vid;
@@ -949,8 +956,8 @@ VInitVolumePackageThread(void *args)
                CV_BROADCAST(&vq->cv);
                MUTEX_EXIT(&vq->mutex);
 
-                vb = (struct volume_init_batch*)malloc(sizeof(struct volume_init_batch));
-                osi_Assert(vb);
+                vb = malloc(sizeof(struct volume_init_batch));
+                opr_Assert(vb);
                 vb->thread = params->thread;
                 vb->size = 0;
                 vb->last = 0;
@@ -994,8 +1001,8 @@ VInitNextPartition(struct partition_queue *pq)
     queue_Remove(dp);
     MUTEX_EXIT(&pq->mutex);
 
-    osi_Assert(dp);
-    osi_Assert(dp->diskP);
+    opr_Assert(dp);
+    opr_Assert(dp->diskP);
 
     partition = dp->diskP;
     free(dp);
@@ -1220,14 +1227,17 @@ VShutdown_r(void)
     Log("VShutdown:  shutting down on-line volumes on %d partition%s...\n",
        params.n_parts, params.n_parts > 1 ? "s" : "");
 
+    vol_shutting_down = 1;
+
     if (vol_attach_threads > 1) {
        /* prepare for parallel shutdown */
        params.n_threads = vol_attach_threads;
        MUTEX_INIT(&params.lock, "params", MUTEX_DEFAULT, 0);
        CV_INIT(&params.cv, "params", CV_DEFAULT, 0);
        CV_INIT(&params.master_cv, "params master", CV_DEFAULT, 0);
-       osi_Assert(pthread_attr_init(&attrs) == 0);
-       osi_Assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
+       opr_Verify(pthread_attr_init(&attrs) == 0);
+       opr_Verify(pthread_attr_setdetachstate(&attrs,
+                                              PTHREAD_CREATE_DETACHED) == 0);
        queue_Init(&params);
 
        /* setup the basic partition information structures for
@@ -1252,8 +1262,8 @@ VShutdown_r(void)
 
 
            /* build up the pass 0 shutdown work queue */
-           dpq = (struct diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
-           osi_Assert(dpq != NULL);
+           dpq = malloc(sizeof(struct diskpartition_queue_t));
+           opr_Assert(dpq != NULL);
            dpq->diskP = diskP;
            queue_Prepend(&params, dpq);
 
@@ -1267,9 +1277,8 @@ VShutdown_r(void)
        /* do pass 0 shutdown */
        MUTEX_ENTER(&params.lock);
        for (i=0; i < params.n_threads; i++) {
-           osi_Assert(pthread_create
-                  (&tid, &attrs, &VShutdownThread,
-                   &params) == 0);
+           opr_Verify(pthread_create(&tid, &attrs, &VShutdownThread,
+                                     &params) == 0);
        }
 
        /* wait for all the pass 0 shutdowns to complete */
@@ -1292,7 +1301,7 @@ VShutdown_r(void)
            VOL_CV_WAIT(&params.cv);
        }
 
-       osi_Assert(pthread_attr_destroy(&attrs) == 0);
+       opr_Verify(pthread_attr_destroy(&attrs) == 0);
        CV_DESTROY(&params.cv);
        CV_DESTROY(&params.master_cv);
        MUTEX_DESTROY(&params.lock);
@@ -1342,6 +1351,7 @@ VShutdown_r(void)
     }
 
     Log("VShutdown:  shutting down on-line volumes...\n");
+    vol_shutting_down = 1;
     for (i = 0; i < VolumeHashTable.Size; i++) {
        /* try to hold first volume in the hash table */
        for (queue_Scan(&VolumeHashTable.Table[i],vp,np,Volume)) {
@@ -1364,7 +1374,7 @@ VShutdown_r(void)
 void
 VShutdown(void)
 {
-    osi_Assert(VInit>0);
+    opr_Assert(VInit>0);
     VOL_LOCK;
     VShutdown_r();
     VOL_UNLOCK;
@@ -1604,7 +1614,7 @@ VShutdownThread(void * args)
     VOL_LOCK;
 
     pass = params->pass;
-    osi_Assert(pass > 0);
+    opr_Assert(pass > 0);
 
     /* now escalate through the more complicated shutdowns */
     while (pass <= 3) {
@@ -1838,7 +1848,7 @@ VShutdownVolume_r(Volume * vp)
     /* wait for other blocking ops to finish */
     VWaitExclusiveState_r(vp);
 
-    osi_Assert(VIsValidState(V_attachState(vp)));
+    opr_Assert(VIsValidState(V_attachState(vp)));
 
     switch(V_attachState(vp)) {
     case VOL_STATE_SALVAGING:
@@ -1880,6 +1890,22 @@ VShutdownVolume_r(Volume * vp)
 /* Header I/O routines                             */
 /***************************************************/
 
+static const char *
+HeaderName(bit32 magic)
+{
+    switch (magic) {
+    case VOLUMEINFOMAGIC:
+       return "volume info";
+    case SMALLINDEXMAGIC:
+       return "small index";
+    case LARGEINDEXMAGIC:
+       return "large index";
+    case LINKTABLEMAGIC:
+       return "link table";
+    }
+    return "unknown";
+}
+
 /* open a descriptor for the inode (h),
  * read in an on-disk structure into buffer (to) of size (size),
  * verify versionstamp in structure has magic (magic) and
@@ -1891,29 +1917,63 @@ ReadHeader(Error * ec, IHandle_t * h, char *to, int size, bit32 magic,
 {
     struct versionStamp *vsn;
     FdHandle_t *fdP;
+    afs_sfsize_t nbytes;
+    afs_ino_str_t stmp;
 
     *ec = 0;
     if (h == NULL) {
+       Log("ReadHeader: Null inode handle argument for %s header file.\n",
+           HeaderName(magic));
        *ec = VSALVAGE;
        return;
     }
 
     fdP = IH_OPEN(h);
     if (fdP == NULL) {
+       Log("ReadHeader: Failed to open %s header file "
+           "(volume=%u, inode=%s); errno=%d\n", HeaderName(magic), h->ih_vid,
+           PrintInode(stmp, h->ih_ino), errno);
        *ec = VSALVAGE;
        return;
     }
 
     vsn = (struct versionStamp *)to;
-    if (FDH_PREAD(fdP, to, size, 0) != size || vsn->magic != magic) {
+    nbytes = FDH_PREAD(fdP, to, size, 0);
+    if (nbytes < 0) {
+       Log("ReadHeader: Failed to read %s header file "
+           "(volume=%u, inode=%s); errno=%d\n", HeaderName(magic), h->ih_vid,
+           PrintInode(stmp, h->ih_ino), errno);
+       *ec = VSALVAGE;
+       FDH_REALLYCLOSE(fdP);
+       return;
+    }
+    if (nbytes != size) {
+       Log("ReadHeader: Incorrect number of bytes read from %s header file "
+           "(volume=%u, inode=%s); expected=%d, read=%d\n",
+           HeaderName(magic), h->ih_vid, PrintInode(stmp, h->ih_ino), size,
+           (int)nbytes);
+       *ec = VSALVAGE;
+       FDH_REALLYCLOSE(fdP);
+       return;
+    }
+    if (vsn->magic != magic) {
+       Log("ReadHeader: Incorrect magic for %s header file "
+           "(volume=%u, inode=%s); expected=0x%x, read=0x%x\n",
+           HeaderName(magic), h->ih_vid, PrintInode(stmp, h->ih_ino), magic,
+           vsn->magic);
        *ec = VSALVAGE;
        FDH_REALLYCLOSE(fdP);
        return;
     }
+
     FDH_CLOSE(fdP);
 
     /* Check is conditional, in case caller wants to inspect version himself */
     if (version && vsn->version != version) {
+       Log("ReadHeader: Incorrect version for %s header file "
+           "(volume=%u, inode=%s); expected=%x, read=%x\n",
+           HeaderName(magic), h->ih_vid, PrintInode(stmp, h->ih_ino),
+           version, vsn->version);
        *ec = VSALVAGE;
     }
 }
@@ -2085,7 +2145,7 @@ VPreAttachVolumeById_r(Error * ec,
 
     *ec = 0;
 
-    osi_Assert(programType == fileServer);
+    opr_Assert(programType == fileServer);
 
     if (!(partp = VGetPartition_r(partition, 0))) {
        *ec = VNOVOL;
@@ -2150,7 +2210,8 @@ VPreAttachVolumeByVp_r(Error * ec,
         *   - volume is in an error state
         *   - volume is pre-attached
         */
-       Log("VPreattachVolumeByVp_r: volume %u not in quiescent state\n", vid);
+       Log("VPreattachVolumeByVp_r: volume %u not in quiescent state (state %u flags 0x%x)\n",
+           vid, V_attachState(vp), V_attachFlags(vp));
        goto done;
     } else if (vp) {
        /* we're re-attaching a volume; clear out some old state */
@@ -2168,9 +2229,8 @@ VPreAttachVolumeByVp_r(Error * ec,
        VOL_UNLOCK;
 
        /* allocate the volume structure */
-       vp = nvp = (Volume *) malloc(sizeof(Volume));
-       osi_Assert(vp != NULL);
-       memset(vp, 0, sizeof(Volume));
+       vp = nvp = calloc(1, sizeof(Volume));
+       opr_Assert(vp != NULL);
        queue_Init(&vp->vnode_list);
        queue_Init(&vp->rx_call_list);
        CV_INIT(&V_attachCV(vp), "vp attach", CV_DEFAULT, 0);
@@ -2261,7 +2321,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
     }
 
     if (VRequiresPartLock()) {
-       osi_Assert(VInit == 3);
+       opr_Assert(VInit == 3);
        VLockPartition_r(partition);
     } else if (programType == fileServer) {
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -2336,7 +2396,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
            }
        }
 
-       osi_Assert(vp != NULL);
+       opr_Assert(vp != NULL);
 
        /* handle pre-attach races
         *
@@ -2392,12 +2452,12 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
 
     VOL_UNLOCK;
 
-    strcat(path, "/");
+    strcat(path, OS_DIRSEP);
     strcat(path, name);
 
     if (!vp) {
       vp = (Volume *) calloc(1, sizeof(Volume));
-      osi_Assert(vp != NULL);
+      opr_Assert(vp != NULL);
       vp->hashid = volumeId;
       vp->device = partp->device;
       vp->partition = partp;
@@ -2534,7 +2594,7 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
     *ec = 0;
 
     /* volume utility should never call AttachByVp */
-    osi_Assert(programType == fileServer);
+    opr_Assert(programType == fileServer);
 
     volumeId = vp->hashid;
     partp = vp->partition;
@@ -2578,7 +2638,7 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
        }
     }
 
-    osi_Assert(vp != NULL);
+    opr_Assert(vp != NULL);
     VChangeState_r(vp, VOL_STATE_ATTACHING);
 
     /* restore monotonically increasing stats */
@@ -2591,7 +2651,7 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
 
     VOL_UNLOCK;
 
-    strcat(path, "/");
+    strcat(path, OS_DIRSEP);
     strcat(path, name);
 
     /* do volume attach
@@ -2676,8 +2736,9 @@ VLockVolumeNB(Volume *vp, int locktype)
 {
     int code;
 
-    osi_Assert(programType != fileServer || VIsExclusiveState(V_attachState(vp)));
-    osi_Assert(!(V_attachFlags(vp) & VOL_LOCKED));
+    opr_Assert(programType != fileServer
+              || VIsExclusiveState(V_attachState(vp)));
+    opr_Assert(!(V_attachFlags(vp) & VOL_LOCKED));
 
     code = VLockVolumeByIdNB(vp->hashid, vp->partition, locktype);
     if (code == 0) {
@@ -2699,8 +2760,9 @@ VLockVolumeNB(Volume *vp, int locktype)
 static void
 VUnlockVolume(Volume *vp)
 {
-    osi_Assert(programType != fileServer || VIsExclusiveState(V_attachState(vp)));
-    osi_Assert((V_attachFlags(vp) & VOL_LOCKED));
+    opr_Assert(programType != fileServer
+              || VIsExclusiveState(V_attachState(vp)));
+    opr_Assert((V_attachFlags(vp) & VOL_LOCKED));
 
     VUnlockVolumeById(vp->hashid, vp->partition);
 
@@ -2784,7 +2846,7 @@ attach_volume_header(Error *ec, Volume *vp, struct DiskPartition64 *partp,
         SYNC_response res;
         memset(&res, 0, sizeof(res));
 
-       if (FSYNC_VolOp(volid, VPartitionPath(partp), FSYNC_VOL_NEEDVOLUME, mode, &res)
+       if (FSYNC_VolOp(volid, partp->name, FSYNC_VOL_NEEDVOLUME, mode, &res)
            != SYNC_OK) {
 
             if (res.hdr.reason == FSYNC_SALVAGE) {
@@ -2920,7 +2982,7 @@ attach_volume_header(Error *ec, Volume *vp, struct DiskPartition64 *partp,
 #if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
     if (!peek && *ec == 0 && retry == 0 && VMustCheckoutVolume(mode)) {
 
-       code = FSYNC_VerifyCheckout(volid, VPartitionPath(partp), FSYNC_VOL_NEEDVOLUME, mode);
+       code = FSYNC_VerifyCheckout(volid, partp->name, FSYNC_VOL_NEEDVOLUME, mode);
 
        if (code == SYNC_DENIED) {
            /* must retry checkout; fileserver no longer thinks we have
@@ -2953,6 +3015,9 @@ attach_volume_header(Error *ec, Volume *vp, struct DiskPartition64 *partp,
     }
 
     if (*ec) {
+       VOL_LOCK;
+       FreeVolumeHeader(vp);
+       VOL_UNLOCK;
        return;
     }
     if (retry) {
@@ -3013,12 +3078,14 @@ attach_check_vop(Error *ec, VolumeId volid, struct DiskPartition64 *partp,
        switch (vp->pending_vol_op->vol_op_state) {
        case FSSYNC_VolOpPending:
            /* this should never happen */
-           osi_Assert(vp->pending_vol_op->vol_op_state != FSSYNC_VolOpPending);
+           opr_Assert(vp->pending_vol_op->vol_op_state
+                           != FSSYNC_VolOpPending);
            break;
 
        case FSSYNC_VolOpRunningUnknown:
            /* this should never happen; we resolved 'unknown' above */
-           osi_Assert(vp->pending_vol_op->vol_op_state != FSSYNC_VolOpRunningUnknown);
+           opr_Assert(vp->pending_vol_op->vol_op_state
+                           != FSSYNC_VolOpRunningUnknown);
            break;
 
        case FSSYNC_VolOpRunningOffline:
@@ -3032,7 +3099,11 @@ attach_check_vop(Error *ec, VolumeId volid, struct DiskPartition64 *partp,
 
            /* check to see if we should set the specialStatus flag */
            if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
-               vp->specialStatus = VBUSY;
+               /* don't overwrite specialStatus if it was already set to
+                * something else (e.g. VMOVED) */
+               if (!vp->specialStatus) {
+                   vp->specialStatus = VBUSY;
+               }
            }
            break;
 
@@ -3117,7 +3188,11 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
     if (!*ec) {
        read_header = 1;
 
-       vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
+       /* ensure that we don't override specialStatus if it was set to
+        * something else (e.g. VMOVED) */
+       if (isbusy && !vp->specialStatus) {
+           vp->specialStatus = VBUSY;
+       }
        vp->shuttingDown = 0;
        vp->goingOffline = 0;
        vp->nUsers = 1;
@@ -3143,7 +3218,6 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
     if (!*ec) {
        struct IndexFileHeader iHead;
 
-#if OPENAFS_VOL_STATS
        /*
         * We just read in the diskstuff part of the header.  If the detailed
         * volume stats area has not yet been initialized, we should bzero the
@@ -3153,7 +3227,6 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
            memset((V_stat_area(vp)), 0, VOL_STATS_BYTES);
            V_stat_initialized(vp) = 1;
        }
-#endif /* OPENAFS_VOL_STATS */
 
        (void)ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
                         (char *)&iHead, sizeof(iHead),
@@ -3195,14 +3268,17 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
        if (!VCanScheduleSalvage()) {
            Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
        }
-       VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER |
-                                                 VOL_SALVAGE_NO_OFFLINE);
+       VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_NO_OFFLINE);
        vp->nUsers = 0;
 
        goto locked_error;
     } else if (*ec) {
        /* volume operation in progress */
-       goto unlocked_error;
+       VOL_LOCK;
+       /* we have already transitioned the vp away from ATTACHING state, so we
+        * can go right to the end of attach2, and we do not need to transition
+        * to ERROR. */
+       goto error_notbroken;
     }
 #else /* AFS_DEMAND_ATTACH_FS */
     if (*ec) {
@@ -3219,8 +3295,7 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
        if (!VCanScheduleSalvage()) {
            Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
        }
-       VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER |
-                                                  VOL_SALVAGE_NO_OFFLINE);
+       VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_NO_OFFLINE);
        vp->nUsers = 0;
 
 #else /* AFS_DEMAND_ATTACH_FS */
@@ -3242,8 +3317,7 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
        if (!VCanScheduleSalvage()) {
            Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
        }
-       VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER |
-                                                  VOL_SALVAGE_NO_OFFLINE);
+       VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_NO_OFFLINE);
        vp->nUsers = 0;
 
 #else /* AFS_DEMAND_ATTACH_FS */
@@ -3265,8 +3339,7 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
 
 #if defined(AFS_DEMAND_ATTACH_FS)
        /* schedule a salvage so the volume goes away on disk */
-       VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER |
-                                                 VOL_SALVAGE_NO_OFFLINE);
+       VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_NO_OFFLINE);
        VChangeState_r(vp, VOL_STATE_ERROR);
        vp->nUsers = 0;
        forcefree = 1;
@@ -3284,8 +3357,7 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
            VGetBitmap_r(ec, vp, i);
            if (*ec) {
 #ifdef AFS_DEMAND_ATTACH_FS
-               VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER |
-                                                         VOL_SALVAGE_NO_OFFLINE);
+               VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_NO_OFFLINE);
                vp->nUsers = 0;
 #endif /* AFS_DEMAND_ATTACH_FS */
                Log("VAttachVolume: error getting bitmap for volume (%s)\n",
@@ -3333,8 +3405,7 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
                "%lu; needs salvage\n", (int)*ec,
                afs_printable_uint32_lu(V_id(vp)));
 #ifdef AFS_DEMAND_ATTACH_FS
-           VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER |
-                                                     VOL_SALVAGE_NO_OFFLINE);
+           VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_NO_OFFLINE);
            vp->nUsers = 0;
 #else /* !AFS_DEMAND_ATTACH_FS */
            *ec = VSALVAGE;
@@ -3373,7 +3444,7 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
 #ifdef AFS_DEMAND_ATTACH_FS
                error_state = VOL_STATE_ERROR;
                /* see if we can recover */
-               VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
+               VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_NO_OFFLINE);
 #endif
            }
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -3411,6 +3482,10 @@ unlocked_error:
 locked_error:
 #ifdef AFS_DEMAND_ATTACH_FS
     if (!VIsErrorState(V_attachState(vp))) {
+       if (VIsErrorState(error_state)) {
+           Log("attach2: forcing vol %u to error state (state %u flags 0x%x ec %d)\n",
+               vp->hashid, V_attachState(vp), V_attachFlags(vp), *ec);
+       }
        VChangeState_r(vp, error_state);
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
@@ -3420,7 +3495,15 @@ locked_error:
     }
 
 #ifdef AFS_DEMAND_ATTACH_FS
-    VCheckSalvage(vp);
+ error_notbroken:
+    if (VCheckSalvage(vp) == VCHECK_SALVAGE_FAIL) {
+       /* The salvage could not be scheduled with the salvage server
+        * due to a hard error. Reset the error code to prevent retry loops by
+        * callers. */
+       if (*ec == VSALVAGING) {
+           *ec = VSALVAGE;
+       }
+    }
     if (forcefree) {
        FreeVolume(vp);
     } else {
@@ -3457,7 +3540,7 @@ VAttachVolume_r(Error * ec, VolumeId volumeId, int mode)
        Error error;
        vp = VGetVolume_r(&error, volumeId);
        if (vp) {
-           osi_Assert(V_inUse(vp) == 0);
+           opr_Assert(V_inUse(vp) == 0);
            VDetachVolume_r(ec, vp);
        }
        return NULL;
@@ -3511,6 +3594,9 @@ VHold_r(Volume * vp)
 
 #ifdef AFS_PTHREAD_ENV
 
+static struct timespec *shutdown_timeout;
+static pthread_once_t shutdown_timeout_once = PTHREAD_ONCE_INIT;
+
 static_inline int
 VTimedOut(const struct timespec *ts)
 {
@@ -3539,11 +3625,94 @@ VTimedOut(const struct timespec *ts)
     return 1;
 }
 
+/**
+ * Calculate an absolute timeout.
+ *
+ * @param[out] ts  A timeout that is "timeout" seconds from now, if we return
+ *                 NULL, the memory is not touched
+ * @param[in]  timeout  How long the timeout should be from now
+ *
+ * @return timeout to use
+ *  @retval NULL      no timeout; wait forever
+ *  @retval non-NULL  the given value for "ts"
+ *
+ * @internal
+ */
+static struct timespec *
+VCalcTimeout(struct timespec *ts, afs_int32 timeout)
+{
+    struct timeval now;
+    int code;
+
+    if (timeout < 0) {
+       return NULL;
+    }
+
+    if (timeout == 0) {
+       ts->tv_sec = ts->tv_nsec = 0;
+       return ts;
+    }
+
+    code = gettimeofday(&now, NULL);
+    if (code) {
+       Log("Error %d from gettimeofday, falling back to 'forever' timeout\n", errno);
+       return NULL;
+    }
+
+    ts->tv_sec = now.tv_sec + timeout;
+    ts->tv_nsec = now.tv_usec * 1000;
+
+    return ts;
+}
+
+/**
+ * Initialize the shutdown_timeout global.
+ */
+static void
+VShutdownTimeoutInit(void)
+{
+    struct timespec *ts;
+
+    ts = malloc(sizeof(*ts));
+
+    shutdown_timeout = VCalcTimeout(ts, vol_opts.offline_shutdown_timeout);
+
+    if (!shutdown_timeout) {
+       free(ts);
+    }
+}
+
+/**
+ * Figure out the timeout that should be used for waiting for offline volumes.
+ *
+ * @param[out] ats  Storage space for a local timeout value if needed
+ *
+ * @return The timeout value that should be used
+ *   @retval NULL      No timeout; wait forever for offlining volumes
+ *   @retval non-NULL  A pointer to the absolute time that should be used as
+ *                     the deadline for waiting for offlining volumes.
+ *
+ * @note If we return non-NULL, the pointer we return may or may not be the
+ *       same as "ats"
+ */
+static const struct timespec *
+VOfflineTimeout(struct timespec *ats)
+{
+    if (vol_shutting_down) {
+       opr_Verify(pthread_once(&shutdown_timeout_once,
+                               VShutdownTimeoutInit) == 0);
+       return shutdown_timeout;
+    } else {
+       return VCalcTimeout(ats, vol_opts.offline_timeout);
+    }
+}
+
 #else /* AFS_PTHREAD_ENV */
 
 /* Waiting a certain amount of time for offlining volumes is not supported
  * for LWP due to a lack of primitives. So, we never time out */
 # define VTimedOut(x) (0)
+# define VOfflineTimeout(x) (NULL)
 
 #endif /* !AFS_PTHREAD_ENV */
 
@@ -3687,7 +3856,7 @@ VDeregisterCall_r(Volume *vp, struct VCallByVol *cbv)
 void
 VPutVolume_r(Volume * vp)
 {
-    osi_Assert(--vp->nUsers >= 0);
+    opr_Verify(--vp->nUsers >= 0);
     if (vp->nUsers == 0) {
        VCheckOffline(vp);
        ReleaseVolumeHeader(vp->header);
@@ -3740,20 +3909,6 @@ VGetVolume(Error * ec, Error * client_ec, VolId volumeId)
     return retVal;
 }
 
-/* same as VGetVolume, but if a volume is waiting to go offline, we only wait
- * until time ts. If we have waited longer than that, we return that it is
- * actually offline, instead of waiting for it to go offline */
-Volume *
-VGetVolumeTimed(Error * ec, Error * client_ec, VolId volumeId,
-                const struct timespec *ts)
-{
-    Volume *retVal;
-    VOL_LOCK;
-    retVal = GetVolume(ec, client_ec, volumeId, NULL, ts);
-    VOL_UNLOCK;
-    return retVal;
-}
-
 /**
  * Get a volume reference associated with an RX call.
  *
@@ -3916,18 +4071,18 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint,
         *   - VOL_STATE_SHUTTING_DOWN
         */
        if ((V_attachState(vp) == VOL_STATE_ERROR) ||
-           (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN) ||
-           (V_attachState(vp) == VOL_STATE_GOING_OFFLINE)) {
+           (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN)) {
            *ec = VNOVOL;
            vp = NULL;
            break;
        }
 
        /*
-        * short circuit with VOFFLINE for VOL_STATE_UNATTACHED and
+        * short circuit with VOFFLINE for VOL_STATE_UNATTACHED/GOING_OFFLINE and
         *                    VNOVOL   for VOL_STATE_DELETED
         */
        if ((V_attachState(vp) == VOL_STATE_UNATTACHED) ||
+           (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) ||
            (V_attachState(vp) == VOL_STATE_DELETED)) {
           if (vp->specialStatus) {
               *ec = vp->specialStatus;
@@ -3952,6 +4107,11 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint,
        }
 
        if (V_attachState(vp) == VOL_STATE_PREATTACHED) {
+           if (vp->specialStatus) {
+               *ec = vp->specialStatus;
+               vp = NULL;
+               break;
+           }
            avp = VAttachVolumeByVp_r(ec, vp, 0);
            if (avp) {
                if (vp != avp) {
@@ -3972,12 +4132,18 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint,
                case VSALVAGING:
                    break;
                case VOFFLINE:
-                   if (!vp->pending_vol_op) {
-                       endloop = 1;
+                   endloop = 1;
+                   if (vp->specialStatus) {
+                       *ec = vp->specialStatus;
                    }
                    break;
+
                default:
-                   *ec = VNOVOL;
+                   if (vp->specialStatus) {
+                       *ec = vp->specialStatus;
+                   } else {
+                       *ec = VNOVOL;
+                   }
                    endloop = 1;
                }
                if (endloop) {
@@ -4002,50 +4168,24 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint,
            vp = NULL;
            break;
        }
-#endif
 
-#ifdef AFS_DEMAND_ATTACH_FS
+       if (VIsErrorState(V_attachState(vp))) {
+           /* make sure we don't take a vp in VOL_STATE_ERROR state and use
+            * it, or transition it out of that state */
+           if (!*ec) {
+               *ec = VNOVOL;
+           }
+           vp = NULL;
+           break;
+       }
+
        /*
-        * this test MUST happen after VAttachVolymeByVp, so vol_op_state is
-        * not VolOpRunningUnknown (attach2 would have converted it to Online
-        * or Offline)
+        * this test MUST happen after VAttachVolymeByVp, so we have no
+        * conflicting vol op. (attach2 would have errored out if we had one;
+        * specifically attach_check_vop must have detected a conflicting vop)
         */
+         opr_Assert(!vp->pending_vol_op || vp->pending_vol_op->vol_op_state == FSSYNC_VolOpRunningOnline);
 
-         /* only valid before/during demand attachment */
-         osi_Assert(!vp->pending_vol_op || vp->pending_vol_op->vol_op_state != FSSYNC_VolOpRunningUnknown);
-
-         /* deny getvolume due to running mutually exclusive vol op */
-         if (vp->pending_vol_op && vp->pending_vol_op->vol_op_state==FSSYNC_VolOpRunningOffline) {
-          /*
-           * volume cannot remain online during this volume operation.
-           * notify client.
-           */
-          if (vp->specialStatus) {
-              /*
-               * special status codes outrank normal VOFFLINE code
-               */
-              *ec = vp->specialStatus;
-              if (client_ec) {
-                  *client_ec = vp->specialStatus;
-              }
-          } else {
-              if (client_ec) {
-                  /* see CheckVnode() in afsfileprocs.c for an explanation
-                   * of this error code logic */
-                  afs_uint32 now = FT_ApproxTime();
-                  if ((vp->stats.last_vol_op + (10 * 60)) >= now) {
-                      *client_ec = VBUSY;
-                  } else {
-                      *client_ec = VRESTARTING;
-                  }
-              }
-              *ec = VOFFLINE;
-          }
-          VChangeState_r(vp, VOL_STATE_UNATTACHED);
-          FreeVolumeHeader(vp);
-          vp = NULL;
-          break;
-       }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
        LoadVolumeHeader(ec, vp);
@@ -4058,7 +4198,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint,
                    vp->hashid);
 #ifdef AFS_DEMAND_ATTACH_FS
            if (VCanScheduleSalvage()) {
-               VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+               VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0 /*flags*/);
            } else {
                FreeVolume(vp);
                vp = NULL;
@@ -4095,7 +4235,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint,
 #else /* AFS_PTHREAD_ENV */
                    /* LWP has no timed wait, so the caller better not be
                     * expecting one */
-                   osi_Assert(!timeout);
+                   opr_Assert(!timeout);
                    LWP_WaitProcess(VPutVolume);
 #endif /* AFS_PTHREAD_ENV */
                    continue;
@@ -4142,7 +4282,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint,
 #endif /* AFS_DEMAND_ATTACH_FS */
 
  not_inited:
-    osi_Assert(vp || *ec);
+    opr_Assert(vp || *ec);
     return vp;
 }
 
@@ -4158,8 +4298,8 @@ VTakeOffline_r(Volume * vp)
 {
     Error error;
 
-    osi_Assert(vp->nUsers > 0);
-    osi_Assert(programType == fileServer);
+    opr_Assert(vp->nUsers > 0);
+    opr_Assert(programType == fileServer);
 
     VCreateReservation_r(vp);
     VWaitExclusiveState_r(vp);
@@ -4174,8 +4314,8 @@ VTakeOffline_r(Volume * vp)
 void
 VTakeOffline_r(Volume * vp)
 {
-    osi_Assert(vp->nUsers > 0);
-    osi_Assert(programType == fileServer);
+    opr_Assert(vp->nUsers > 0);
+    opr_Assert(programType == fileServer);
 
     vp->goingOffline = 1;
     V_needsSalvaged(vp) = 1;
@@ -4243,7 +4383,7 @@ VForceOffline_r(Volume * vp, int flags)
     }
 
 #ifdef AFS_DEMAND_ATTACH_FS
-    VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+    VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0 /*flags*/);
 #endif /* AFS_DEMAND_ATTACH_FS */
 
 #ifdef AFS_PTHREAD_ENV
@@ -4270,6 +4410,160 @@ VForceOffline(Volume * vp)
     VOL_UNLOCK;
 }
 
+/**
+ * Iterate over the RX calls associated with a volume, and interrupt them.
+ *
+ * @param[in] vp The volume whose RX calls we want to scan
+ *
+ * @pre VOL_LOCK held
+ */
+static void
+VScanCalls_r(struct Volume *vp)
+{
+    struct VCallByVol *cbv, *ncbv;
+    afs_int32 err;
+#ifdef AFS_DEMAND_ATTACH_FS
+    VolState state_save;
+#endif
+
+    if (queue_IsEmpty(&vp->rx_call_list))
+       return; /* no calls to interrupt */
+    if (!vol_opts.interrupt_rxcall)
+       return; /* we have no function with which to interrupt calls */
+    err = VIsGoingOffline_r(vp);
+    if (!err)
+       return; /* we're not going offline anymore */
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    VWaitExclusiveState_r(vp);
+    state_save = VChangeState_r(vp, VOL_STATE_SCANNING_RXCALLS);
+    VOL_UNLOCK;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+    for(queue_Scan(&vp->rx_call_list, cbv, ncbv, VCallByVol)) {
+       if (LogLevel > 0) {
+           struct rx_peer *peer;
+           char hoststr[16];
+           peer = rx_PeerOf(rx_ConnectionOf(cbv->call));
+
+           Log("Offlining volume %lu while client %s:%u is trying to read "
+               "from it; kicking client off with error %ld\n",
+               (long unsigned) vp->hashid,
+               afs_inet_ntoa_r(rx_HostOf(peer), hoststr),
+               (unsigned) ntohs(rx_PortOf(peer)),
+               (long) err);
+       }
+       (*vol_opts.interrupt_rxcall) (cbv->call, err);
+    }
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_LOCK;
+    VChangeState_r(vp, state_save);
+#endif /* AFS_DEMAND_ATTACH_FS */
+}
+
+#ifdef AFS_DEMAND_ATTACH_FS
+/**
+ * Wait for a vp to go offline.
+ *
+ * @param[out] ec 1 if a salvage on the volume has been requested and
+ *                salvok == 0, 0 otherwise
+ * @param[in] vp  The volume to wait for
+ * @param[in] salvok  If 0, we return immediately with *ec = 1 if the volume
+ *                    has been requested to salvage. Otherwise we keep waiting
+ *                    until the volume has gone offline.
+ *
+ * @pre VOL_LOCK held
+ * @pre caller holds a lightweight ref on vp
+ *
+ * @note DAFS only
+ */
+static void
+VWaitForOfflineByVp_r(Error *ec, struct Volume *vp, int salvok)
+{
+    struct timespec timeout_ts;
+    const struct timespec *ts;
+    int timedout = 0;
+
+    ts = VOfflineTimeout(&timeout_ts);
+
+    *ec = 0;
+
+    while (!VIsOfflineState(V_attachState(vp)) && !timedout) {
+       if (!salvok && vp->salvage.requested) {
+           *ec = 1;
+           return;
+       }
+       VTimedWaitStateChange_r(vp, ts, &timedout);
+    }
+    if (!timedout) {
+       /* we didn't time out, so the volume must be offline, so we're done */
+       return;
+    }
+
+    /* If we got here, we timed out waiting for the volume to go offline.
+     * Kick off the accessing RX calls and wait again */
+
+    VScanCalls_r(vp);
+
+    while (!VIsOfflineState(V_attachState(vp))) {
+       if (!salvok && vp->salvage.requested) {
+           *ec = 1;
+           return;
+       }
+
+       VWaitStateChange_r(vp);
+    }
+}
+
+#else /* AFS_DEMAND_ATTACH_FS */
+
+/**
+ * Wait for a volume to go offline.
+ *
+ * @pre VOL_LOCK held
+ *
+ * @note non-DAFS only (for DAFS, use @see WaitForOfflineByVp_r)
+ */
+static void
+VWaitForOffline_r(Error *ec, VolumeId volid)
+{
+    struct Volume *vp;
+    const struct timespec *ts;
+#ifdef AFS_PTHREAD_ENV
+    struct timespec timeout_ts;
+#endif
+
+    ts = VOfflineTimeout(&timeout_ts);
+
+    vp = GetVolume(ec, NULL, volid, NULL, ts);
+    if (!vp) {
+       /* error occurred so bad that we can't even get a vp; we have no
+        * information on the vol so we don't know whether to wait, so just
+        * return */
+       return;
+    }
+    if (!VIsGoingOffline_r(vp)) {
+       /* volume is no longer going offline, so we're done */
+       VPutVolume_r(vp);
+       return;
+    }
+
+    /* If we got here, we timed out waiting for the volume to go offline.
+     * Kick off the accessing RX calls and wait again */
+
+    VScanCalls_r(vp);
+    VPutVolume_r(vp);
+    vp = NULL;
+
+    vp = VGetVolume_r(ec, volid);
+    if (vp) {
+       /* In case it was reattached... */
+       VPutVolume_r(vp);
+    }
+}
+#endif /* !AFS_DEMAND_ATTACH_FS */
+
 /* The opposite of VAttachVolume.  The volume header is written to disk, with
    the inUse bit turned off.  A copy of the header is maintained in memory,
    however (which is why this is VOffline, not VDetach).
@@ -4277,12 +4571,12 @@ VForceOffline(Volume * vp)
 void
 VOffline_r(Volume * vp, char *message)
 {
-#ifndef AFS_DEMAND_ATTACH_FS
     Error error;
+#ifndef AFS_DEMAND_ATTACH_FS
     VolumeId vid = V_id(vp);
 #endif
 
-    osi_Assert(programType != volumeUtility && programType != volumeServer);
+    opr_Assert(programType != volumeUtility && programType != volumeServer);
     if (!V_inUse(vp)) {
        VPutVolume_r(vp);
        return;
@@ -4296,17 +4590,11 @@ VOffline_r(Volume * vp, char *message)
     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
     VCreateReservation_r(vp);
     VPutVolume_r(vp);
-
-    /* wait for the volume to go offline */
-    if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
-       VWaitStateChange_r(vp);
-    }
+    VWaitForOfflineByVp_r(&error, vp, 1);
     VCancelReservation_r(vp);
 #else /* AFS_DEMAND_ATTACH_FS */
     VPutVolume_r(vp);
-    vp = VGetVolume_r(&error, vid);    /* Wait for it to go offline */
-    if (vp)                    /* In case it was reattached... */
-       VPutVolume_r(vp);
+    VWaitForOffline_r(&error, vid);
 #endif /* AFS_DEMAND_ATTACH_FS */
 }
 
@@ -4341,7 +4629,8 @@ VOffline_r(Volume * vp, char *message)
 void
 VOfflineForVolOp_r(Error *ec, Volume *vp, char *message)
 {
-    osi_Assert(vp->pending_vol_op);
+    int salvok = 1;
+    opr_Assert(vp->pending_vol_op);
     if (!V_inUse(vp)) {
        VPutVolume_r(vp);
         *ec = 1;
@@ -4356,17 +4645,14 @@ VOfflineForVolOp_r(Error *ec, Volume *vp, char *message)
     VCreateReservation_r(vp);
     VPutVolume_r(vp);
 
-    /* Wait for the volume to go offline */
-    while (!VIsOfflineState(V_attachState(vp))) {
+    if (vp->pending_vol_op->com.programType != salvageServer) {
         /* do not give corrupted volumes to the volserver */
-        if (vp->salvage.requested && vp->pending_vol_op->com.programType != salvageServer) {
-           *ec = 1;
-          goto error;
-        }
-       VWaitStateChange_r(vp);
+       salvok = 0;
     }
+
     *ec = 0;
- error:
+    VWaitForOfflineByVp_r(ec, vp, salvok);
+
     VCancelReservation_r(vp);
 }
 #endif /* AFS_DEMAND_ATTACH_FS */
@@ -4399,11 +4685,17 @@ VDetachVolume_r(Error * ec, Volume * vp)
        notifyServer = vp->needsPutBack;
        if (V_destroyMe(vp) == DESTROY_ME)
            useDone = FSYNC_VOL_LEAVE_OFF;
-#ifdef AFS_DEMAND_ATTACH_FS
+# ifdef AFS_DEMAND_ATTACH_FS
        else if (!V_blessed(vp) || !V_inService(vp))
            useDone = FSYNC_VOL_LEAVE_OFF;
-#endif
+# endif
+    }
+# ifdef AFS_DEMAND_ATTACH_FS
+    if (V_needsSalvaged(vp)) {
+       notifyServer = 0;
+       VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, 0);
     }
+# endif
     tpartp = vp->partition;
     volume = V_id(vp);
 #endif /* FSSYNC_BUILD_CLIENT */
@@ -4481,15 +4773,17 @@ VCloseVolumeHandles_r(Volume * vp)
     VolState state_save;
 
     state_save = VChangeState_r(vp, VOL_STATE_OFFLINING);
+
+    VOL_UNLOCK;
 #endif
 
-    /* demand attach fs
-     *
-     * XXX need to investigate whether we can perform
-     * DFlushVolume outside of vol_glock_mutex...
-     *
-     * VCloseVnodeFiles_r drops the glock internally */
     DFlushVolume(vp->hashid);
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_LOCK;
+#endif
+
+    /* DAFS: VCloseVnodeFiles_r drops the glock internally */
     VCloseVnodeFiles_r(vp);
 
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -4533,13 +4827,17 @@ VReleaseVolumeHandles_r(Volume * vp)
     VolState state_save;
 
     state_save = VChangeState_r(vp, VOL_STATE_DETACHING);
+
+    VOL_UNLOCK;
 #endif
 
-    /* XXX need to investigate whether we can perform
-     * DFlushVolume outside of vol_glock_mutex... */
     DFlushVolume(vp->hashid);
 
-    VReleaseVnodeFiles_r(vp); /* releases the glock internally */
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_LOCK;
+#endif
+
+    VReleaseVnodeFiles_r(vp); /* DAFS: releases the glock internally */
 
 #ifdef AFS_DEMAND_ATTACH_FS
     VOL_UNLOCK;
@@ -4647,9 +4945,9 @@ VSyncVolume_r(Error * ec, Volume * vp, int flags)
        VOL_UNLOCK;
 #endif
        fdP = IH_OPEN(V_diskDataHandle(vp));
-       osi_Assert(fdP != NULL);
+       opr_Assert(fdP != NULL);
        code = FDH_SYNC(fdP);
-       osi_Assert(code == 0);
+       opr_Assert(code == 0);
        FDH_CLOSE(fdP);
 #ifdef AFS_DEMAND_ATTACH_FS
        VOL_LOCK;
@@ -4795,8 +5093,8 @@ VCheckOffline(Volume * vp)
 
     if (vp->goingOffline && !vp->nUsers) {
        Error error;
-       osi_Assert(programType == fileServer);
-       osi_Assert((V_attachState(vp) != VOL_STATE_ATTACHED) &&
+       opr_Assert(programType == fileServer);
+       opr_Assert((V_attachState(vp) != VOL_STATE_ATTACHED) &&
               (V_attachState(vp) != VOL_STATE_FREED) &&
               (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
               (V_attachState(vp) != VOL_STATE_UNATTACHED) &&
@@ -4856,7 +5154,7 @@ VCheckOffline(Volume * vp)
 
     if (vp->goingOffline && !vp->nUsers) {
        Error error;
-       osi_Assert(programType == fileServer);
+       opr_Assert(programType == fileServer);
 
        ret = 1;
        vp->goingOffline = 0;
@@ -4926,7 +5224,7 @@ VCheckOffline(Volume * vp)
 void
 VCancelReservation_r(Volume * vp)
 {
-    osi_Assert(--vp->nWaiters >= 0);
+    opr_Verify(--vp->nWaiters >= 0);
     if (vp->nWaiters == 0) {
        VCheckOffline(vp);
        if (!VCheckDetach(vp)) {
@@ -4982,8 +5280,8 @@ VRegisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
     FSSYNC_VolOp_info * info;
 
     /* attach a vol op info node to the volume struct */
-    info = (FSSYNC_VolOp_info *) malloc(sizeof(FSSYNC_VolOp_info));
-    osi_Assert(info != NULL);
+    info = malloc(sizeof(FSSYNC_VolOp_info));
+    opr_Assert(info != NULL);
     memcpy(info, vopinfo, sizeof(FSSYNC_VolOp_info));
     vp->pending_vol_op = info;
 
@@ -5179,8 +5477,11 @@ VOfflineForSalvage_r(struct Volume *vp)
  * @param[in] vp   pointer to volume object
  *
  * @return status code
- *    @retval 0 no salvage scheduled
- *    @retval 1 a salvage has been scheduled with the salvageserver
+ *    @retval VCHECK_SALVAGE_OK (0)         no pending salvage
+ *    @retval VCHECK_SALVAGE_SCHEDULED (1)  salvage has been scheduled
+ *    @retval VCHECK_SALVAGE_ASYNC (2)      salvage being scheduled
+ *    @retval VCHECK_SALVAGE_DENIED (3)     salvage not scheduled; denied
+ *    @retval VCHECK_SALVAGE_FAIL (4)       salvage not scheduled; failed
  *
  * @pre VOL_LOCK is held
  *
@@ -5200,31 +5501,32 @@ VOfflineForSalvage_r(struct Volume *vp)
 static int
 VCheckSalvage(Volume * vp)
 {
-    int ret = 0;
+    int ret = VCHECK_SALVAGE_OK;
+
 #if defined(SALVSYNC_BUILD_CLIENT) || defined(FSSYNC_BUILD_CLIENT)
-    if (vp->nUsers)
-       return ret;
     if (!vp->salvage.requested) {
-       return ret;
+       return VCHECK_SALVAGE_OK;
+    }
+    if (vp->nUsers) {
+       return VCHECK_SALVAGE_ASYNC;
     }
 
     /* prevent recursion; some of the code below creates and removes
      * lightweight refs, which can call VCheckSalvage */
     if (vp->salvage.scheduling) {
-       return ret;
+       return VCHECK_SALVAGE_ASYNC;
     }
     vp->salvage.scheduling = 1;
 
     if (V_attachState(vp) == VOL_STATE_SALVAGE_REQ) {
        if (!VOfflineForSalvage_r(vp)) {
            vp->salvage.scheduling = 0;
-           return ret;
+           return VCHECK_SALVAGE_FAIL;
        }
     }
 
     if (vp->salvage.requested) {
-       VScheduleSalvage_r(vp);
-       ret = 1;
+       ret = VScheduleSalvage_r(vp);
     }
     vp->salvage.scheduling = 0;
 #endif /* SALVSYNC_BUILD_CLIENT || FSSYNC_BUILD_CLIENT */
@@ -5240,8 +5542,8 @@ VCheckSalvage(Volume * vp)
  * @param[in]  flags   see flags note below
  *
  * @note flags:
- *       VOL_SALVAGE_INVALIDATE_HEADER causes volume header cache entry
- *                                     to be invalidated.
+ *       VOL_SALVAGE_NO_OFFLINE do not need to wait to offline the volume; it has
+ *                              not been fully attached
  *
  * @pre VOL_LOCK is held.
  *
@@ -5314,6 +5616,10 @@ VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
                    VOfflineForSalvage_r(vp);
                }
            }
+           /* If we are non-fileserver, we're telling the fileserver to
+            * salvage the vol, so we don't need to give it back separately. */
+           vp->needsPutBack = 0;
+
            *ec = VSALVAGING;
        } else {
            Log("VRequestSalvage: volume %u online salvaged too many times; forced offline.\n", vp->hashid);
@@ -5326,15 +5632,6 @@ VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
            *ec = VSALVAGE;
            code = 1;
        }
-       if (flags & VOL_SALVAGE_INVALIDATE_HEADER) {
-           /* Instead of ReleaseVolumeHeader, we do FreeVolumeHeader()
-               so that the the next VAttachVolumeByVp_r() invocation
-               of attach2() will pull in a cached header
-               entry and fail, then load a fresh one from disk and attach
-               it to the volume.
-           */
-           FreeVolumeHeader(vp);
-       }
     }
     return code;
 }
@@ -5454,8 +5751,11 @@ try_FSSYNC(Volume *vp, char *partName, int *code) {
  * @param[in] vp  pointer to volume object
  *
  * @return operation status
- *    @retval 0 salvage scheduled successfully
- *    @retval 1 salvage not scheduled, or SALVSYNC/FSSYNC com error
+ *    @retval VCHECK_SALVAGE_OK (0)         no pending salvage
+ *    @retval VCHECK_SALVAGE_SCHEDULED (1)  salvage has been scheduled
+ *    @retval VCHECK_SALVAGE_ASYNC (2)      salvage being scheduled
+ *    @retval VCHECK_SALVAGE_DENIED (3)     salvage not scheduled; denied
+ *    @retval VCHECK_SALVAGE_FAIL (4)       salvage not scheduled; failed
  *
  * @pre
  *    @arg VOL_LOCK is held.
@@ -5478,21 +5778,22 @@ try_FSSYNC(Volume *vp, char *partName, int *code) {
 static int
 VScheduleSalvage_r(Volume * vp)
 {
-    int ret=0;
-    int code;
+    int ret = VCHECK_SALVAGE_SCHEDULED;
+    int code = 0;
     VolState state_save;
     VThreadOptions_t * thread_opts;
     char partName[16];
 
-    osi_Assert(VCanUseSALVSYNC() || VCanUseFSSYNC());
+    opr_Verify(VCanUseSALVSYNC() || VCanUseFSSYNC());
 
     if (vp->nWaiters || vp->nUsers) {
-       return 1;
+       return VCHECK_SALVAGE_ASYNC;
     }
 
     /* prevent endless salvage,attach,salvage,attach,... loops */
-    if (vp->stats.salvages >= SALVAGE_COUNT_MAX)
-       return 1;
+    if (vp->stats.salvages >= SALVAGE_COUNT_MAX) {
+       return VCHECK_SALVAGE_FAIL;
+    }
 
     /*
      * don't perform salvsync ops on certain threads
@@ -5502,11 +5803,11 @@ VScheduleSalvage_r(Volume * vp)
        thread_opts = &VThread_defaults;
     }
     if (thread_opts->disallow_salvsync || vol_disallow_salvsync) {
-       return 1;
+       return VCHECK_SALVAGE_ASYNC;
     }
 
     if (vp->salvage.scheduled) {
-       return ret;
+       return VCHECK_SALVAGE_SCHEDULED;
     }
 
     VCreateReservation_r(vp);
@@ -5516,23 +5817,26 @@ VScheduleSalvage_r(Volume * vp)
      * XXX the scheduling process should really be done asynchronously
      *     to avoid fssync deadlocks
      */
-    if (!vp->salvage.scheduled) {
+    if (vp->salvage.scheduled) {
+       ret = VCHECK_SALVAGE_SCHEDULED;
+    } else {
        /* if we haven't previously scheduled a salvage, do so now
         *
         * set the volume to an exclusive state and drop the lock
         * around the SALVSYNC call
         */
-       strlcpy(partName, VPartitionPath(vp->partition), sizeof(partName));
+       strlcpy(partName, vp->partition->name, sizeof(partName));
        state_save = VChangeState_r(vp, VOL_STATE_SALVSYNC_REQ);
        VOL_UNLOCK;
 
-       osi_Assert(try_SALVSYNC(vp, partName, &code) ||
-              try_FSSYNC(vp, partName, &code));
+       opr_Verify(try_SALVSYNC(vp, partName, &code)
+                  || try_FSSYNC(vp, partName, &code));
 
        VOL_LOCK;
        VChangeState_r(vp, state_save);
 
        if (code == SYNC_OK) {
+           ret = VCHECK_SALVAGE_SCHEDULED;
            vp->salvage.scheduled = 1;
            vp->stats.last_salvage_req = FT_ApproxTime();
            if (VCanUseSALVSYNC()) {
@@ -5542,16 +5846,23 @@ VScheduleSalvage_r(Volume * vp)
                IncUInt64(&VStats.salvages);
            }
        } else {
-           ret = 1;
            switch(code) {
            case SYNC_BAD_COMMAND:
            case SYNC_COM_ERROR:
+               ret = VCHECK_SALVAGE_FAIL;
                break;
            case SYNC_DENIED:
+               ret = VCHECK_SALVAGE_DENIED;
                Log("VScheduleSalvage_r: Salvage request for volume %lu "
                    "denied\n", afs_printable_uint32_lu(vp->hashid));
                break;
+           case SYNC_FAILED:
+               ret = VCHECK_SALVAGE_FAIL;
+               Log("VScheduleSalvage_r: Salvage request for volume %lu "
+                   "failed\n", afs_printable_uint32_lu(vp->hashid));
+               break;
            default:
+               ret = VCHECK_SALVAGE_FAIL;
                Log("VScheduleSalvage_r: Salvage request for volume %lu "
                    "received unknown protocol error %d\n",
                    afs_printable_uint32_lu(vp->hashid), code);
@@ -5570,7 +5881,7 @@ VScheduleSalvage_r(Volume * vp)
      * this, as the caller may reference vp without any refs. Instead, it
      * is the duty of the caller to inspect 'vp' after we return to see if
      * needs to be freed. */
-    osi_Assert(--vp->nWaiters >= 0);
+    opr_Verify(--vp->nWaiters >= 0);
     return ret;
 }
 #endif /* SALVSYNC_BUILD_CLIENT || FSSYNC_BUILD_CLIENT */
@@ -5786,7 +6097,7 @@ int
 VConnectFS_r(void)
 {
     int rc;
-    osi_Assert((VInit == 2) &&
+    opr_Assert((VInit == 2) &&
           (programType != fileServer) &&
           (programType != salvager));
     rc = FSYNC_clientInit();
@@ -5815,7 +6126,7 @@ VConnectFS_r(void)
 void
 VDisconnectFS_r(void)
 {
-    osi_Assert((programType != fileServer) &&
+    opr_Assert((programType != fileServer) &&
           (programType != salvager));
     FSYNC_clientFinis();
     VSetVInit_r(2);
@@ -5900,6 +6211,25 @@ VChildProcReconnectFS(void)
 /* volume bitmap routines                          */
 /***************************************************/
 
+/*
+ * Grow the bitmap by the defined increment
+ */
+void
+VGrowBitmap(struct vnodeIndex *index)
+{
+    byte *bp;
+
+    bp = realloc(index->bitmap, index->bitmapSize + VOLUME_BITMAP_GROWSIZE);
+    osi_Assert(bp != NULL);
+    index->bitmap = bp;
+    bp += index->bitmapSize;
+    memset(bp, 0, VOLUME_BITMAP_GROWSIZE);
+    index->bitmapOffset = index->bitmapSize;
+    index->bitmapSize += VOLUME_BITMAP_GROWSIZE;
+
+    return;
+}
+
 /**
  * allocate a vnode bitmap number for the vnode
  *
@@ -5982,7 +6312,7 @@ VAllocBitmapEntry_r(Error * ec, Volume * vp,
                VGetBitmap_r(ec, vp, i);
                if (*ec) {
 #ifdef AFS_DEMAND_ATTACH_FS
-                   VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+                   VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0 /*flags*/);
 #else /* AFS_DEMAND_ATTACH_FS */
                    DeleteVolumeFromHashTable(vp);
                    vp->shuttingDown = 1;       /* Let who has it free it. */
@@ -6021,14 +6351,8 @@ VAllocBitmapEntry_r(Error * ec, Volume * vp,
        bp += sizeof(bit32) /* i.e. 4 */ ;
     }
     /* No bit map entry--must grow bitmap */
-    bp = (byte *)
-       realloc(index->bitmap, index->bitmapSize + VOLUME_BITMAP_GROWSIZE);
-    osi_Assert(bp != NULL);
-    index->bitmap = bp;
-    bp += index->bitmapSize;
-    memset(bp, 0, VOLUME_BITMAP_GROWSIZE);
-    index->bitmapOffset = index->bitmapSize;
-    index->bitmapSize += VOLUME_BITMAP_GROWSIZE;
+    VGrowBitmap(index);
+    bp = index->bitmap + index->bitmapOffset;
     *bp = 1;
     ret = index->bitmapOffset * 8;
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -6056,32 +6380,50 @@ VAllocBitmapEntry(Error * ec, Volume * vp, struct vnodeIndex * index)
 }
 
 void
-VFreeBitMapEntry_r(Error * ec, struct vnodeIndex *index,
-                  unsigned bitNumber)
+VFreeBitMapEntry_r(Error * ec, Volume *vp, struct vnodeIndex *index,
+                  unsigned bitNumber, int flags)
 {
     unsigned int offset;
 
     *ec = 0;
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    if (flags & VOL_FREE_BITMAP_WAIT) {
+       /* VAllocBitmapEntry_r allocs bitmap entries under an exclusive volume
+        * state, so ensure we're not in an exclusive volume state when we update
+        * the bitmap */
+       VCreateReservation_r(vp);
+       VWaitExclusiveState_r(vp);
+    }
+#endif
+
 #ifdef BITMAP_LATER
     if (!index->bitmap)
-       return;
+       goto done;
 #endif /* BITMAP_LATER */
+
     offset = bitNumber >> 3;
     if (offset >= index->bitmapSize) {
        *ec = VNOVNODE;
-       return;
+       goto done;
     }
     if (offset < index->bitmapOffset)
        index->bitmapOffset = offset & ~3;      /* Truncate to nearest bit32 */
     *(index->bitmap + offset) &= ~(1 << (bitNumber & 0x7));
+
+ done:
+#ifdef AFS_DEMAND_ATTACH_FS
+    VCancelReservation_r(vp);
+#endif
+    return; /* make the compiler happy for non-DAFS */
 }
 
 void
-VFreeBitMapEntry(Error * ec, struct vnodeIndex *index,
+VFreeBitMapEntry(Error * ec, Volume *vp, struct vnodeIndex *index,
                 unsigned bitNumber)
 {
     VOL_LOCK;
-    VFreeBitMapEntry_r(ec, index, bitNumber);
+    VFreeBitMapEntry_r(ec, vp, index, bitNumber, VOL_FREE_BITMAP_WAIT);
     VOL_UNLOCK;
 }
 
@@ -6115,13 +6457,13 @@ VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class)
     VOL_UNLOCK;
 
     fdP = IH_OPEN(vip->handle);
-    osi_Assert(fdP != NULL);
+    opr_Assert(fdP != NULL);
     file = FDH_FDOPEN(fdP, "r");
-    osi_Assert(file != NULL);
-    vnode = (VnodeDiskObject *) malloc(vcp->diskSize);
-    osi_Assert(vnode != NULL);
+    opr_Assert(file != NULL);
+    vnode = malloc(vcp->diskSize);
+    opr_Assert(vnode != NULL);
     size = OS_SIZE(fdP->fd_fd);
-    osi_Assert(size != -1);
+    opr_Assert(size != -1);
     nVnodes = (size <= vcp->diskSize ? 0 : size - vcp->diskSize)
        >> vcp->logSize;
     vip->bitmapSize = ((nVnodes / 8) + 10) / 4 * 4;    /* The 10 is a little extra so
@@ -6131,10 +6473,10 @@ VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class)
                                                         * it that way */
 #ifdef BITMAP_LATER
     BitMap = (byte *) calloc(1, vip->bitmapSize);
-    osi_Assert(BitMap != NULL);
+    opr_Assert(BitMap != NULL);
 #else /* BITMAP_LATER */
     vip->bitmap = (byte *) calloc(1, vip->bitmapSize);
-    osi_Assert(vip->bitmap != NULL);
+    opr_Assert(vip->bitmap != NULL);
     vip->bitmapOffset = 0;
 #endif /* BITMAP_LATER */
     if (STREAM_ASEEK(file, vcp->diskSize) != -1) {
@@ -6185,7 +6527,7 @@ VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class)
        vip->bitmap = BitMap;
        vip->bitmapOffset = 0;
     } else
-       free((byte *) BitMap);
+       free(BitMap);
 #endif /* BITMAP_LATER */
 #ifdef AFS_DEMAND_ATTACH_FS
     VChangeState_r(vp, state_save);
@@ -6225,10 +6567,11 @@ VGetVolumePath(Error * ec, VolId volumeId, char **partitionp, char **namep)
     struct DiskPartition64 *dp;
 
     *ec = 0;
-    name[0] = '/';
-    (void)afs_snprintf(&name[1], (sizeof name) - 1, VFORMAT, afs_printable_uint32_lu(volumeId));
+    name[0] = OS_DIRSEPC;
+    snprintf(&name[1], (sizeof name) - 1, VFORMAT,
+            afs_printable_uint32_lu(volumeId));
     for (dp = DiskPartitionList; dp; dp = dp->next) {
-       struct afs_stat status;
+       struct afs_stat_st status;
        strcpy(path, VPartitionPath(dp));
        strcat(path, name);
        if (afs_stat(path, &status) == 0) {
@@ -6254,16 +6597,16 @@ VGetVolumePath(Error * ec, VolId volumeId, char **partitionp, char **namep)
  * @return volume number
  *
  * @note the string must be of the form VFORMAT.  the only permissible
- *       deviation is a leading '/' character.
+ *       deviation is a leading OS_DIRSEPC character.
  *
  * @see VFORMAT
  */
 int
 VolumeNumber(char *name)
 {
-    if (*name == '/')
+    if (*name == OS_DIRSEPC)
        name++;
-    return atoi(name + 1);
+    return strtoul(name + 1, NULL, 10);
 }
 
 /**
@@ -6289,7 +6632,7 @@ char *
 VolumeExternalName(VolumeId volumeId)
 {
     static char name[VMAXPATHLEN];
-    (void)afs_snprintf(name, sizeof name, VFORMAT, afs_printable_uint32_lu(volumeId));
+    snprintf(name, sizeof name, VFORMAT, afs_printable_uint32_lu(volumeId));
     return name;
 }
 
@@ -6310,7 +6653,7 @@ VolumeExternalName(VolumeId volumeId)
 int
 VolumeExternalName_r(VolumeId volumeId, char * name, size_t len)
 {
-    return afs_snprintf(name, len, VFORMAT, afs_printable_uint32_lu(volumeId));
+    return snprintf(name, len, VFORMAT, afs_printable_uint32_lu(volumeId));
 }
 
 
@@ -6318,11 +6661,7 @@ VolumeExternalName_r(VolumeId volumeId, char * name, size_t len)
 /* Volume Usage Statistics routines                */
 /***************************************************/
 
-#if OPENAFS_VOL_STATS
 #define OneDay (86400)         /* 24 hours' worth of seconds */
-#else
-#define OneDay (24*60*60)      /* 24 hours */
-#endif /* OPENAFS_VOL_STATS */
 
 static time_t
 Midnight(time_t t) {
@@ -6393,14 +6732,12 @@ VAdjustVolumeStatistics_r(Volume * vp)
        V_dayUse(vp) = 0;
        V_dayUseDate(vp) = Midnight(now);
 
-#if OPENAFS_VOL_STATS
        /*
         * All we need to do is bzero the entire VOL_STATS_BYTES of
         * the detailed volume statistics area.
         */
        memset((V_stat_area(vp)), 0, VOL_STATS_BYTES);
-#endif /* OPENAFS_VOL_STATS */
-    }
+       }
 
     /*It's been more than a day of collection */
     /*
@@ -6427,10 +6764,16 @@ VBumpVolumeUsage_r(Volume * vp)
     if (now - V_dayUseDate(vp) > OneDay)
        VAdjustVolumeStatistics_r(vp);
     /*
-     * Save the volume header image to disk after every 128 bumps to dayUse.
+     * Save the volume header image to disk after a threshold of bumps to dayUse,
+     * at most every usage_rate_limit seconds.
      */
-    if ((V_dayUse(vp)++ & 127) == 0) {
+    V_dayUse(vp)++;
+    vp->usage_bumps_outstanding++;
+    if (vp->usage_bumps_outstanding >= vol_opts.usage_threshold
+       && vp->usage_bumps_next_write <= now) {
        Error error;
+       vp->usage_bumps_outstanding = 0;
+       vp->usage_bumps_next_write = now + vol_opts.usage_rate_limit;
        VUpdateVolume_r(&error, vp, VOL_UPDATE_WAIT);
     }
 }
@@ -6518,7 +6861,7 @@ VAddToVolumeUpdateList_r(Error * ec, Volume * vp)
        return;
     if (UpdateList == NULL) {
        updateSize = UPDATE_LIST_SIZE;
-       UpdateList = (VolumeId *) malloc(sizeof(VolumeId) * updateSize);
+       UpdateList = malloc(sizeof(VolumeId) * updateSize);
     } else {
        if (nUpdatedVolumes == updateSize) {
            updateSize <<= 1;
@@ -6526,12 +6869,11 @@ VAddToVolumeUpdateList_r(Error * ec, Volume * vp)
                Log("warning: there is likely a bug in the volume update scanner\n");
                return;
            }
-           UpdateList =
-               (VolumeId *) realloc(UpdateList,
-                                    sizeof(VolumeId) * updateSize);
+           UpdateList = realloc(UpdateList,
+                                sizeof(VolumeId) * updateSize);
        }
     }
-    osi_Assert(UpdateList != NULL);
+    opr_Assert(UpdateList != NULL);
     UpdateList[nUpdatedVolumes++] = V_id(vp);
 #endif /* !AFS_DEMAND_ATTACH_FS */
 }
@@ -6806,9 +7148,11 @@ VInitVLRU(void)
     volume_LRU.scanner_state = VLRU_SCANNER_STATE_OFFLINE;
     if (programType == fileServer) {
        CV_INIT(&volume_LRU.cv, "vol lru", CV_DEFAULT, 0);
-       osi_Assert(pthread_attr_init(&attrs) == 0);
-       osi_Assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
-       osi_Assert(pthread_create(&tid, &attrs, &VLRU_ScannerThread, NULL) == 0);
+       opr_Verify(pthread_attr_init(&attrs) == 0);
+       opr_Verify(pthread_attr_setdetachstate(&attrs,
+                                              PTHREAD_CREATE_DETACHED) == 0);
+       opr_Verify(pthread_create(&tid, &attrs,
+                                 &VLRU_ScannerThread, NULL) == 0);
     }
 }
 
@@ -6834,7 +7178,7 @@ VLRU_Init_Node_r(Volume * vp)
     if (!VLRU_enabled)
        return;
 
-    osi_Assert(queue_IsNotOnQueue(&vp->vlru));
+    opr_Assert(queue_IsNotOnQueue(&vp->vlru));
     vp->vlru.idx = VLRU_QUEUE_INVALID;
 }
 
@@ -6978,7 +7322,7 @@ VLRU_UpdateAccess_r(Volume * vp)
     if (queue_IsNotOnQueue(&vp->vlru))
        return;
 
-    osi_Assert(V_attachFlags(vp) & VOL_ON_VLRU);
+    opr_Assert(V_attachFlags(vp) & VOL_ON_VLRU);
 
     /* update the access timestamp */
     vp->stats.last_get = FT_ApproxTime();
@@ -7290,7 +7634,7 @@ VLRU_Demote_r(int idx)
     Volume ** salv_flag_vec = NULL;
     int salv_vec_offset = 0;
 
-    osi_Assert(idx == VLRU_QUEUE_MID || idx == VLRU_QUEUE_OLD);
+    opr_Assert(idx == VLRU_QUEUE_MID || idx == VLRU_QUEUE_OLD);
 
     /* get exclusive access to two chains, and drop the glock */
     VLRU_Wait_r(&volume_LRU.q[idx-1]);
@@ -7301,7 +7645,7 @@ VLRU_Demote_r(int idx)
 
     /* no big deal if this allocation fails */
     if (volume_LRU.q[idx].len) {
-       salv_flag_vec = (Volume **) malloc(volume_LRU.q[idx].len * sizeof(Volume *));
+       salv_flag_vec = malloc(volume_LRU.q[idx].len * sizeof(Volume *));
     }
 
     now = FT_ApproxTime();
@@ -7389,7 +7733,7 @@ VLRU_Scan_r(int idx)
     Volume * vp;
     int i, locked = 1;
 
-    osi_Assert(idx == VLRU_QUEUE_NEW || idx == VLRU_QUEUE_CANDIDATE);
+    opr_Assert(idx == VLRU_QUEUE_NEW || idx == VLRU_QUEUE_CANDIDATE);
 
     /* gain exclusive access to the idx VLRU */
     VLRU_Wait_r(&volume_LRU.q[idx]);
@@ -7480,7 +7824,7 @@ VCheckSoftDetachCandidate(Volume * vp, afs_uint32 thresh)
 
     idx = vp->vlru.idx;
 
-    osi_Assert(idx == VLRU_QUEUE_NEW);
+    opr_Assert(idx == VLRU_QUEUE_NEW);
 
     if (vp->stats.last_get <= thresh) {
        /* move to candidate pool */
@@ -7500,7 +7844,7 @@ VCheckSoftDetachCandidate(Volume * vp, afs_uint32 thresh)
 static void
 VLRU_BeginExclusive_r(struct VLRU_q * q)
 {
-    osi_Assert(q->busy == 0);
+    opr_Assert(q->busy == 0);
     q->busy = 1;
 }
 
@@ -7508,7 +7852,7 @@ VLRU_BeginExclusive_r(struct VLRU_q * q)
 static void
 VLRU_EndExclusive_r(struct VLRU_q * q)
 {
-    osi_Assert(q->busy);
+    opr_Assert(q->busy);
     q->busy = 0;
     CV_BROADCAST(&q->cv);
 }
@@ -7532,7 +7876,7 @@ VSoftDetachVolume_r(Volume * vp, afs_uint32 thresh)
     afs_uint32 ts_save;
     int ret = 0;
 
-    osi_Assert(vp->vlru.idx == VLRU_QUEUE_CANDIDATE);
+    opr_Assert(vp->vlru.idx == VLRU_QUEUE_CANDIDATE);
 
     ts_save = vp->stats.last_get;
     if (ts_save > thresh)
@@ -7587,7 +7931,7 @@ VSoftDetachVolume_r(Volume * vp, afs_uint32 thresh)
            vp = NULL;
        } else {
            /* pull it off the VLRU */
-           osi_Assert(vp->vlru.idx == VLRU_QUEUE_CANDIDATE);
+           opr_Assert(vp->vlru.idx == VLRU_QUEUE_CANDIDATE);
            volume_LRU.q[VLRU_QUEUE_CANDIDATE].len--;
            queue_Remove(&vp->vlru);
            vp->vlru.idx = VLRU_QUEUE_INVALID;
@@ -7649,7 +7993,7 @@ VInitVolumeHeaderCache(afs_uint32 howMany)
     volume_hdr_LRU.stats.used = howMany;
     volume_hdr_LRU.stats.attached = 0;
     hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
-    osi_Assert(hp != NULL);
+    opr_Assert(hp != NULL);
 
     while (howMany--)
        /* We are using ReleaseVolumeHeader to initialize the values on the header list
@@ -7658,6 +8002,51 @@ VInitVolumeHeaderCache(afs_uint32 howMany)
        ReleaseVolumeHeader(hp++);
 }
 
+/* get a volume header off of the volume header LRU.
+ *
+ * @return volume header
+ *  @retval NULL no usable volume header is available on the LRU
+ *
+ * @pre VOL_LOCK held
+ *
+ * @post for DAFS, if the returned header is associated with a volume, that
+ *       volume is NOT in an exclusive state
+ *
+ * @internal volume package internal use only.
+ */
+#ifdef AFS_DEMAND_ATTACH_FS
+static struct volHeader*
+GetVolHeaderFromLRU(void)
+{
+    struct volHeader *hd = NULL, *qh, *nqh;
+    /* Usually, a volume in an exclusive state will not have its header on
+     * the LRU. However, it is possible for this to occur when a salvage
+     * request is received over FSSYNC, and possibly in other corner cases.
+     * So just skip over headers whose volumes are in an exclusive state. We
+     * could VWaitExclusiveState_r instead, but not waiting is faster and
+     * easier to do */
+    for (queue_Scan(&volume_hdr_LRU, qh, nqh, volHeader)) {
+       if (!qh->back || !VIsExclusiveState(V_attachState(qh->back))) {
+           queue_Remove(qh);
+           hd = qh;
+           break;
+       }
+    }
+    return hd;
+}
+#else /* AFS_DEMAND_ATTACH_FS */
+static struct volHeader*
+GetVolHeaderFromLRU(void)
+{
+    struct volHeader *hd = NULL;
+    if (queue_IsNotEmpty(&volume_hdr_LRU)) {
+       hd = queue_First(&volume_hdr_LRU, volHeader);
+       queue_Remove(hd);
+    }
+    return hd;
+}
+#endif /* !AFS_DEMAND_ATTACH_FS */
+
 /**
  * get a volume header and attach it to the volume object.
  *
@@ -7706,8 +8095,8 @@ GetVolumeHeader(Volume * vp)
     if (programType != fileServer) {
        /* for volume utilities, we allocate volHeaders as needed */
        if (!vp->header) {
-           hd = (struct volHeader *)calloc(1, sizeof(*vp->header));
-           osi_Assert(hd != NULL);
+           hd = calloc(1, sizeof(*vp->header));
+           opr_Assert(hd != NULL);
            vp->header = hd;
            hd->back = vp;
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -7721,21 +8110,17 @@ GetVolumeHeader(Volume * vp)
             * still available. pull it off the lru and return */
            hd = vp->header;
            queue_Remove(hd);
-           osi_Assert(hd->back == vp);
+           opr_Assert(hd->back == vp);
 #ifdef AFS_DEMAND_ATTACH_FS
             V_attachFlags(vp) &= ~(VOL_HDR_IN_LRU);
 #endif
        } else {
-           /* we need to grab a new element off the LRU */
-           if (queue_IsNotEmpty(&volume_hdr_LRU)) {
-               /* grab an element and pull off of LRU */
-               hd = queue_First(&volume_hdr_LRU, volHeader);
-               queue_Remove(hd);
-           } else {
+           hd = GetVolHeaderFromLRU();
+           if (!hd) {
                /* LRU is empty, so allocate a new volHeader
                 * this is probably indicative of a leak, so let the user know */
-               hd = (struct volHeader *)calloc(1, sizeof(struct volHeader));
-               osi_Assert(hd != NULL);
+               hd = calloc(1, sizeof(struct volHeader));
+               opr_Assert(hd != NULL);
                if (!everLogged) {
                    Log("****Allocated more volume headers, probably leak****\n");
                    everLogged = 1;
@@ -7748,9 +8133,9 @@ GetVolumeHeader(Volume * vp)
                 * be sync'd out to disk */
 
 #ifdef AFS_DEMAND_ATTACH_FS
-               /* if hd->back were in an exclusive state, then
-                * its volHeader would not be on the LRU... */
-               osi_Assert(!VIsExclusiveState(V_attachState(hd->back)));
+               /* GetVolHeaderFromLRU had better not give us back a header
+                * with a volume in exclusive state... */
+               opr_Assert(!VIsExclusiveState(V_attachState(hd->back)));
 #endif
 
                if (hd->diskstuff.inUse) {
@@ -7980,7 +8365,7 @@ VInitVolumeHash(void)
 
     VolumeHashTable.Table = (VolumeHashChainHead *) calloc(VolumeHashTable.Size,
                                                           sizeof(VolumeHashChainHead));
-    osi_Assert(VolumeHashTable.Table != NULL);
+    opr_Assert(VolumeHashTable.Table != NULL);
 
     for (i=0; i < VolumeHashTable.Size; i++) {
        queue_Init(&VolumeHashTable.Table[i]);
@@ -8132,7 +8517,7 @@ VLookupVolume_r(Error * ec, VolId volumeId, Volume * hint)
     /* search the chain for this volume id */
     for(queue_Scan(head, vp, np, Volume)) {
        looks++;
-       if ((vp->hashid == volumeId)) {
+       if (vp->hashid == volumeId) {
            break;
        }
     }
@@ -8254,7 +8639,7 @@ VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp)
 static void
 VHashBeginExclusive_r(VolumeHashChainHead * head)
 {
-    osi_Assert(head->busy == 0);
+    opr_Assert(head->busy == 0);
     head->busy = 1;
 }
 
@@ -8278,7 +8663,7 @@ VHashBeginExclusive_r(VolumeHashChainHead * head)
 static void
 VHashEndExclusive_r(VolumeHashChainHead * head)
 {
-    osi_Assert(head->busy);
+    opr_Assert(head->busy);
     head->busy = 0;
     CV_BROADCAST(&head->chain_busy_cv);
 }
@@ -8418,7 +8803,7 @@ DeleteVolumeFromVByPList_r(Volume * vp)
 static void
 VVByPListBeginExclusive_r(struct DiskPartition64 * dp)
 {
-    osi_Assert(dp->vol_list.busy == 0);
+    opr_Assert(dp->vol_list.busy == 0);
     dp->vol_list.busy = 1;
 }
 
@@ -8442,7 +8827,7 @@ VVByPListBeginExclusive_r(struct DiskPartition64 * dp)
 static void
 VVByPListEndExclusive_r(struct DiskPartition64 * dp)
 {
-    osi_Assert(dp->vol_list.busy);
+    opr_Assert(dp->vol_list.busy);
     dp->vol_list.busy = 0;
     CV_BROADCAST(&dp->vol_list.cv);
 }
@@ -8487,16 +8872,14 @@ VVByPListWait_r(struct DiskPartition64 * dp)
 void
 VPrintCacheStats_r(void)
 {
-    afs_uint32 get_hi, get_lo, load_hi, load_lo;
     struct VnodeClassInfo *vcp;
     vcp = &VnodeClassInfo[vLarge];
     Log("Large vnode cache, %d entries, %d allocs, %d gets (%d reads), %d writes\n", vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
     vcp = &VnodeClassInfo[vSmall];
     Log("Small vnode cache,%d entries, %d allocs, %d gets (%d reads), %d writes\n", vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
-    SplitInt64(VStats.hdr_gets, get_hi, get_lo);
-    SplitInt64(VStats.hdr_loads, load_hi, load_lo);
-    Log("Volume header cache, %d entries, %d gets, %d replacements\n",
-       VStats.hdr_cache_size, get_lo, load_lo);
+    Log("Volume header cache, %d entries, %"AFS_INT64_FMT" gets, "
+        "%"AFS_INT64_FMT" replacements\n",
+       VStats.hdr_cache_size, VStats.hdr_gets, VStats.hdr_loads);
 }
 
 void
@@ -8618,9 +9001,7 @@ VVLRUExtStats_r(struct VLRUExtStats * stats, afs_uint32 nvols)
 
 #define ENUMTOSTRING(en)  #en
 #define ENUMCASE(en) \
-    case en: \
-        return ENUMTOSTRING(en); \
-        break
+    case en: return ENUMTOSTRING(en)
 
 static char *
 vlru_idx_to_string(int idx)