Do not rely on vol header for V*VolumeHandles_r
[openafs.git] / src / vol / volume.c
index 47fc80e..304e244 100644 (file)
@@ -157,6 +157,9 @@ pthread_cond_t vol_init_attach_cond;
 int vol_attach_threads = 1;
 #endif /* AFS_PTHREAD_ENV */
 
+/* start-time configurable I/O parameters */
+ih_init_params vol_io_params;
+
 #ifdef AFS_DEMAND_ATTACH_FS
 pthread_mutex_t vol_salvsync_mutex;
 
@@ -202,13 +205,11 @@ static void LoadVolumeHeader(Error * ec, Volume * vp);
 static int VCheckOffline(register Volume * vp);
 static int VCheckDetach(register Volume * vp);
 static Volume * GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags);
-#ifdef AFS_DEMAND_ATTACH_FS
-static int VolumeExternalName_r(VolumeId volumeId, char * name, size_t len);
-#endif
 
 int LogLevel;                  /* Vice loglevel--not defined as extern so that it will be
                                 * defined when not linked with vice, XXXX */
 ProgramType programType;       /* The type of program using the package */
+static VolumePackageOptions vol_opts;
 
 /* extended volume package statistics */
 VolPkgStats VStats;
@@ -359,7 +360,6 @@ static void VVByPListWait_r(struct DiskPartition64 * dp);
 
 /* online salvager */
 static int VCheckSalvage(register Volume * vp);
-static int VUpdateSalvagePriority_r(Volume * vp);
 #ifdef SALVSYNC_BUILD_CLIENT
 static int VScheduleSalvage_r(Volume * vp);
 #endif
@@ -409,8 +409,6 @@ struct Lock vol_listLock;   /* Lock obtained when listing volumes:
                                 * list volumes */
 
 
-static int TimeZoneCorrection; /* Number of seconds west of GMT */
-
 /* Common message used when the volume goes off line */
 char *VSalvageMessage =
     "Files in this volume are currently unavailable; call operations";
@@ -435,16 +433,56 @@ bit32 VolumeCacheCheck;           /* Incremented everytime a volume goes on line--
 /***************************************************/
 /* Startup routines                                */
 /***************************************************/
+/**
+ * assign default values to a VolumePackageOptions struct.
+ *
+ * Always call this on a VolumePackageOptions struct first, then set any
+ * specific options you want, then call VInitVolumePackage2.
+ *
+ * @param[in]  pt   caller's program type
+ * @param[out] opts volume package options
+ */
+void
+VOptDefaults(ProgramType pt, VolumePackageOptions *opts)
+{
+    opts->nLargeVnodes = opts->nSmallVnodes = 5;
+    opts->volcache = 0;
+
+    opts->canScheduleSalvage = 0;
+    opts->canUseFSSYNC = 0;
+    opts->canUseSALVSYNC = 0;
+
+    switch (pt) {
+    case fileServer:
+       opts->canScheduleSalvage = 1;
+       opts->canUseSALVSYNC = 1;
+       break;
+
+    case salvageServer:
+       opts->canUseFSSYNC = 1;
+       break;
+
+    case volumeServer:
+       opts->nLargeVnodes = 0;
+       opts->nSmallVnodes = 0;
+
+       opts->canScheduleSalvage = 1;
+       opts->canUseFSSYNC = 1;
+       break;
+
+    default:
+       /* noop */
+       break;
+    }
+}
 
 int
-VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVnodes,
-                  int connect, afs_uint32 volcache)
+VInitVolumePackage2(ProgramType pt, VolumePackageOptions * opts)
 {
     int errors = 0;            /* Number of errors while finding vice partitions. */
-    struct timeval tv;
-    struct timezone tz;
 
     programType = pt;
+    vol_opts = *opts;
 
     memset(&VStats, 0, sizeof(VStats));
     VStats.hdr_cache_size = 200;
@@ -472,8 +510,6 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
     Lock_Init(&vol_listLock);
 
     srandom(time(0));          /* For VGetVolumeInfo */
-    gettimeofday(&tv, &tz);
-    TimeZoneCorrection = tz.tz_minuteswest * 60;
 
 #ifdef AFS_DEMAND_ATTACH_FS
     assert(pthread_mutex_init(&vol_salvsync_mutex, NULL) == 0);
@@ -496,18 +532,18 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
     }
 #endif
 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_CLIENT)
-    if (programType == fileServer) {
+    if (VCanUseSALVSYNC()) {
        /* establish a connection to the salvager at this point */
        assert(VConnectSALV() != 0);
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-    if (volcache > VStats.hdr_cache_size)
-       VStats.hdr_cache_size = volcache;
+    if (opts->volcache > VStats.hdr_cache_size)
+       VStats.hdr_cache_size = opts->volcache;
     VInitVolumeHeaderCache(VStats.hdr_cache_size);
 
-    VInitVnodes(vLarge, nLargeVnodes);
-    VInitVnodes(vSmall, nSmallVnodes);
+    VInitVnodes(vLarge, opts->nLargeVnodes);
+    VInitVnodes(vSmall, opts->nSmallVnodes);
 
 
     errors = VAttachPartitions();
@@ -522,20 +558,17 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
     }
 
 #ifdef FSSYNC_BUILD_CLIENT
-    if (programType == volumeUtility && connect) {
+    if (VCanUseFSSYNC()) {
        if (!VConnectFS()) {
-           Log("Unable to connect to file server; will retry at need\n");
-           /*exit(1);*/
-       }
-    }
 #ifdef AFS_DEMAND_ATTACH_FS
-    else if (programType == salvageServer) {
-       if (!VConnectFS()) {
-           Log("Unable to connect to file server; aborted\n");
-           exit(1);
+           if (programType == salvageServer) {
+               Log("Unable to connect to file server; aborted\n");
+               exit(1);
+           }
+#endif /* AFS_DEMAND_ATTACH_FS */
+           Log("Unable to connect to file server; will retry at need\n");
        }
     }
-#endif /* AFS_DEMAND_ATTACH_FS */
 #endif /* FSSYNC_BUILD_CLIENT */
     return 0;
 }
@@ -1864,7 +1897,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
        goto done;
     }
 
-    if (programType == volumeUtility) {
+    if (VRequiresPartLock()) {
        assert(VInit == 3);
        VLockPartition_r(partition);
     } else if (programType == fileServer) {
@@ -2022,12 +2055,24 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
 
     DiskToVolumeHeader(&iheader, &diskHeader);
 #ifdef FSSYNC_BUILD_CLIENT
-    if (programType == volumeUtility && mode != V_SECRETLY && mode != V_PEEK) {
+    if (VCanUseFSSYNC() && mode != V_SECRETLY && mode != V_PEEK) {
+        SYNC_response res;
+        memset(&res, 0, sizeof(res));
+
         VOL_LOCK;
-       if (FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_NEEDVOLUME, mode, NULL)
+       if (FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_NEEDVOLUME, mode, &res)
            != SYNC_OK) {
-           Log("VAttachVolume: attach of volume %u apparently denied by file server\n", iheader.id);
-           *ec = VNOVOL;       /* XXXX */
+
+            if (res.hdr.reason == FSYNC_SALVAGE) {
+                Log("VAttachVolume: file server says volume %u is salvaging\n",
+                     iheader.id);
+                *ec = VSALVAGING;
+            } else {
+               Log("VAttachVolume: attach of volume %u apparently denied by file server\n",
+                     iheader.id);
+               *ec = VNOVOL;   /* XXXX */
+            }
+
            goto done;
        }
        VOL_UNLOCK;
@@ -2037,6 +2082,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
     if (!vp) {
       vp = (Volume *) calloc(1, sizeof(Volume));
       assert(vp != NULL);
+      vp->hashid = volumeId;
       vp->device = partp->device;
       vp->partition = partp;
       queue_Init(&vp->vnode_list);
@@ -2049,7 +2095,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
      * with vol_glock_mutex held */
     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
 
-    if (programType == volumeUtility && vp) {
+    if (VCanUseFSSYNC() && vp) {
        if ((mode == V_VOLUPD) || (VolumeWriteable(vp) && (mode == V_CLONE))) {
            /* mark volume header as in use so that volser crashes lead to a
             * salvage attempt */
@@ -2087,8 +2133,14 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
      * fix is for the server to allow the return of readonly volumes
      * that it doesn't think are really checked out. */
 #ifdef FSSYNC_BUILD_CLIENT
-    if (programType == volumeUtility && vp == NULL &&
+    if (VCanUseFSSYNC() && vp == NULL &&
        mode != V_SECRETLY && mode != V_PEEK) {
+
+#ifdef AFS_DEMAND_ATTACH_FS
+        /* If we couldn't attach but we scheduled a salvage, we already
+         * notified the fileserver; don't online it now */
+        if (*ec != VSALVAGING)
+#endif /* AFS_DEMAND_ATTACH_FS */
        FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_ON, 0, NULL);
     } else 
 #endif
@@ -2143,7 +2195,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
     }
 
   done:
-    if (programType == volumeUtility) {
+    if (VRequiresPartLock()) {
        VUnlockPartition_r(partition);
     }
     if (*ec) {
@@ -2507,18 +2559,18 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
 #if defined(AFS_DEMAND_ATTACH_FS)
     if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
         VOL_LOCK;
-       if (programType == fileServer) {
-           VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
-           vp->nUsers = 0;
-       } else {
+       if (!VCanScheduleSalvage()) {
            Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
-           FreeVolume(vp);
-           *ec = VSALVAGE;
        }
+       VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+       vp->nUsers = 0;
+
+       VCheckFree(vp);
        return NULL;
     } else if (*ec) {
        /* volume operation in progress */
        VOL_LOCK;
+       VCheckFree(vp);
        return NULL;
     }
 #else /* AFS_DEMAND_ATTACH_FS */
@@ -2535,14 +2587,13 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
            vp->specialStatus = 0;
         VOL_LOCK;
 #if defined(AFS_DEMAND_ATTACH_FS)
-       if (programType == fileServer) {
-           VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
-           vp->nUsers = 0;
-       } else {
+       if (!VCanScheduleSalvage()) {
            Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
-           FreeVolume(vp);
-           *ec = VSALVAGE;
        }
+       VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
+       vp->nUsers = 0;
+
+       VCheckFree(vp);
 #else /* AFS_DEMAND_ATTACH_FS */
        FreeVolume(vp);
        *ec = VSALVAGE;
@@ -2551,7 +2602,7 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
     }
 
     VOL_LOCK;
-    if (programType == fileServer) {
+    if (VShouldCheckInUse(mode)) {
 #ifndef FAST_RESTART
        if (V_inUse(vp) && VolumeWriteable(vp)) {
            if (!V_needsSalvaged(vp)) {
@@ -2559,8 +2610,13 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
                VUpdateVolume_r(ec, vp, 0);
            }
 #if defined(AFS_DEMAND_ATTACH_FS)
+           if (!VCanScheduleSalvage()) {
+               Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
+           }
            VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
            vp->nUsers = 0;
+
+           VCheckFree(vp);
 #else /* AFS_DEMAND_ATTACH_FS */
            Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
            FreeVolume(vp);
@@ -2570,7 +2626,15 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
        }
 #endif /* FAST_RESTART */
 
-       if (V_destroyMe(vp) == DESTROY_ME) {
+       if (programType == fileServer && V_destroyMe(vp) == DESTROY_ME) {
+           /* Only check destroyMe if we are the fileserver, since the
+            * volserver et al sometimes need to work with volumes with
+            * destroyMe set. Examples are 'temporary' volumes the
+            * volserver creates, and when we create a volume (destroyMe
+            * is set on creation; sometimes a separate volserver
+            * transaction is created to clear destroyMe).
+            */
+
 #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);
@@ -2595,6 +2659,7 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
 #ifdef AFS_DEMAND_ATTACH_FS
                VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
                vp->nUsers = 0;
+               VCheckFree(vp);
 #else /* AFS_DEMAND_ATTACH_FS */
                FreeVolume(vp);
 #endif /* AFS_DEMAND_ATTACH_FS */
@@ -2985,7 +3050,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
                Log("Volume %u: couldn't reread volume header\n",
                    vp->hashid);
 #ifdef AFS_DEMAND_ATTACH_FS
-           if (programType == fileServer) {
+           if (VCanScheduleSalvage()) {
                VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
            } else {
                FreeVolume(vp);
@@ -3245,7 +3310,7 @@ VOffline_r(Volume * vp, char *message)
     VolumeId vid = V_id(vp);
 #endif
 
-    assert(programType != volumeUtility);
+    assert(programType != volumeUtility && programType != volumeServer);
     if (!V_inUse(vp)) {
        VPutVolume_r(vp);
        return;
@@ -3358,7 +3423,7 @@ VDetachVolume_r(Error * ec, Volume * vp)
     int  useDone = FSYNC_VOL_ON;
 
     *ec = 0;                   /* always "succeeds" */
-    if (programType == volumeUtility) {
+    if (VCanUseFSSYNC()) {
        notifyServer = vp->needsPutBack;
        if (V_destroyMe(vp) == DESTROY_ME)
            useDone = FSYNC_VOL_DONE;
@@ -3386,7 +3451,7 @@ VDetachVolume_r(Error * ec, Volume * vp)
      * is not technically detached until the refcounts reach zero
      */
 #ifdef FSSYNC_BUILD_CLIENT
-    if (programType == volumeUtility && notifyServer) {
+    if (VCanUseFSSYNC() && notifyServer) {
        /* 
         * Note:  The server is not notified in the case of a bogus volume 
         * explicitly to make it possible to create a volume, do a partial 
@@ -3443,7 +3508,7 @@ VCloseVolumeHandles_r(Volume * vp)
      * DFlushVolume outside of vol_glock_mutex... 
      *
      * VCloseVnodeFiles_r drops the glock internally */
-    DFlushVolume(V_id(vp));
+    DFlushVolume(vp->hashid);
     VCloseVnodeFiles_r(vp);
 
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -3451,7 +3516,7 @@ VCloseVolumeHandles_r(Volume * vp)
 #endif
 
     /* Too time consuming and unnecessary for the volserver */
-    if (programType != volumeUtility) {
+    if (programType == fileServer) {
        IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
        IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
        IH_CONDSYNC(vp->diskDataHandle);
@@ -3487,7 +3552,7 @@ VReleaseVolumeHandles_r(Volume * vp)
 
     /* XXX need to investigate whether we can perform
      * DFlushVolume outside of vol_glock_mutex... */
-    DFlushVolume(V_id(vp));
+    DFlushVolume(vp->hashid);
 
     VReleaseVnodeFiles_r(vp); /* releases the glock internally */
 
@@ -3496,7 +3561,7 @@ VReleaseVolumeHandles_r(Volume * vp)
 #endif
 
     /* Too time consuming and unnecessary for the volserver */
-    if (programType != volumeUtility) {
+    if (programType == fileServer) {
        IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
        IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
        IH_CONDSYNC(vp->diskDataHandle);
@@ -4017,17 +4082,6 @@ VVolOpSetVBusy_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
 /* online salvager routines                        */
 /***************************************************/
 #if defined(AFS_DEMAND_ATTACH_FS)
-#define SALVAGE_PRIO_UPDATE_INTERVAL 3      /**< number of seconds between prio updates */
-#define SALVAGE_COUNT_MAX 16                /**< number of online salvages we
-                                            *   allow before moving the volume
-                                            *   into a permanent error state
-                                            *
-                                            *   once this threshold is reached,
-                                            *   the operator will have to manually
-                                            *   issue a 'bos salvage' to bring
-                                            *   the volume back online
-                                            */
-
 /**
  * check whether a salvage needs to be performed on this volume.
  *
@@ -4102,17 +4156,65 @@ VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
      * (at some point in the future, we should consider
      *  making volser talk to salsrv)
      */
-    if (programType != fileServer) {
+    if (!VCanScheduleSalvage()) {
        VChangeState_r(vp, VOL_STATE_ERROR);
        *ec = VSALVAGE;
        return 1;
     }
 
+    if (programType != fileServer) {
+#ifdef FSSYNC_BUILD_CLIENT
+        if (VCanUseFSSYNC()) {
+            /*
+             * If we aren't the fileserver, tell the fileserver the volume
+             * needs to be salvaged. We could directly tell the
+             * salvageserver, but the fileserver keeps track of some stats
+             * related to salvages, and handles some other salvage-related
+             * complications for us.
+             */
+
+           /*
+            * You might wonder why we don't check for
+            * VIsSalvager(V_inUse(vp)) here, since we do check for that
+            * in the fileServer case (below). The reason is that the
+            * below check is done since the fileServer can't tell if a
+            * salvage is still running or not when V_inUse refers to a
+            * salvaging program. However, if we are a non-fileserver,
+            * to get here we must have checked out the volume from the
+            * fileserver and locked the partition, meaning there must
+            * be no salvager running; so we just always try to salvage
+            */
+
+            code = FSYNC_VolOp(vp->hashid, vp->partition->name,
+                               FSYNC_VOL_FORCE_ERROR, FSYNC_SALVAGE, NULL);
+            if (code == SYNC_OK) {
+                *ec = VSALVAGING;
+                return 0;
+            }
+            Log("VRequestSalvage: force error salvage state of volume %u"
+                " denied by fileserver\n", vp->hashid);
+
+            /* fall through to error condition below */
+        }
+#endif /* FSSYNC_BUILD_CLIENT */
+        VChangeState_r(vp, VOL_STATE_ERROR);
+        *ec = VSALVAGE;
+        return 1;
+    }
+
     if (!vp->salvage.requested) {
        vp->salvage.requested = 1;
        vp->salvage.reason = reason;
        vp->stats.last_salvage = FT_ApproxTime();
-       if (VIsSalvager(V_inUse(vp))) {
+
+       if (vp->header && VIsSalvager(V_inUse(vp))) {
+           /* Right now we can't tell for sure if this indicates a
+            * salvage is running, or if a running salvage crashed, so
+            * we always ERROR the volume in case a salvage is running.
+            * Once we get rid of the partition lock and instead lock
+            * individual volume header files for salvages, we will
+            * probably be able to tell if a salvage is running, and we
+            * can do away with this behavior. */
            Log("VRequestSalvage: volume %u appears to be salvaging, but we\n", vp->hashid);
            Log("  didn't request a salvage. Forcing it offline waiting for the\n");
            Log("  salvage to finish; if you are sure no salvage is running,\n");
@@ -4137,6 +4239,11 @@ VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
            *ec = VSALVAGING;
        } else {
            Log("VRequestSalvage: volume %u online salvaged too many times; forced offline.\n", vp->hashid);
+
+           /* make sure neither VScheduleSalvage_r nor
+            * VUpdateSalvagePriority_r try to schedule another salvage */
+           vp->salvage.requested = vp->salvage.scheduled = 0;
+
            VChangeState_r(vp, VOL_STATE_ERROR);
            *ec = VSALVAGE;
            code = 1;
@@ -4180,7 +4287,7 @@ VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
  *
  * @internal volume package internal use only.
  */
-static int
+int
 VUpdateSalvagePriority_r(Volume * vp)
 {
     int ret=0;
@@ -4466,7 +4573,7 @@ VReconnectSALV_r(void)
 /***************************************************/
 
 /* This must be called by any volume utility which needs to run while the
-   file server is also running.  This is separated from VInitVolumePackage so
+   file server is also running.  This is separated from VInitVolumePackage2 so
    that a utility can fork--and each of the children can independently
    initialize communication with the file server */
 #ifdef FSSYNC_BUILD_CLIENT
@@ -5028,16 +5135,12 @@ VolumeExternalName(VolumeId volumeId)
  * @see afs_snprintf
  *
  * @note re-entrant equivalent of VolumeExternalName
- *
- * @internal volume package internal use only.
  */
-#ifdef AFS_DEMAND_ATTACH_FS
-static int
+int
 VolumeExternalName_r(VolumeId volumeId, char * name, size_t len)
 {
     return afs_snprintf(name, len, VFORMAT, afs_printable_uint32_lu(volumeId));
 }
-#endif
 
 
 /***************************************************/
@@ -5050,7 +5153,34 @@ VolumeExternalName_r(VolumeId volumeId, char * name, size_t len)
 #define OneDay (24*60*60)      /* 24 hours */
 #endif /* OPENAFS_VOL_STATS */
 
-#define Midnight(date) ((date-TimeZoneCorrection)/OneDay*OneDay+TimeZoneCorrection)
+static time_t
+Midnight(time_t t) {
+    struct tm local, *l;
+    time_t midnight;
+
+#if defined(AFS_PTHREAD_ENV) && !defined(AFS_NT40_ENV)
+    l = localtime_r(&t, &local);
+#else
+    l = localtime(&t);
+#endif
+
+    if (l != NULL) {
+       /* the following is strictly speaking problematic on the
+          switching day to daylight saving time, after the switch,
+          as tm_isdst does not match.  Similarly, on the looong day when
+          switching back the OneDay check will not do what naively expected!
+          The effects are minor, though, and more a matter of interpreting
+          the numbers. */
+#ifndef AFS_PTHREAD_ENV
+       local = *l;
+#endif
+       local.tm_hour = local.tm_min=local.tm_sec = 0;
+       midnight = mktime(&local);
+       if (midnight != (time_t) -1) return(midnight);
+    }
+    return( (t/OneDay)*OneDay );
+
+}
 
 /*------------------------------------------------------------------------
  * [export] VAdjustVolumeStatistics
@@ -5393,7 +5523,7 @@ static void VLRU_Wait_r(struct VLRU_q * q);
  * @param[in] option  tunable option to modify
  * @param[in] val     new value for tunable parameter
  *
- * @pre @c VInitVolumePackage has not yet been called.
+ * @pre @c VInitVolumePackage2 has not yet been called.
  *
  * @post tunable parameter is modified
  *
@@ -5790,7 +5920,7 @@ VLRU_ScannerThread(void * args)
     }
 
     /* don't start the scanner until VLRU_offline_thresh
-     * plus a small delay for VInitVolumePackage to finish
+     * plus a small delay for VInitVolumePackage2 to finish
      * has gone by */
 
     sleep(VLRU_offline_thresh + 60);
@@ -6637,7 +6767,7 @@ FreeVolumeHeader(register Volume * vp)
  *    @retval 0 success
  *    @retval -1 failure
  *
- * @pre MUST be called prior to VInitVolumePackage
+ * @pre MUST be called prior to VInitVolumePackage2
  *
  * @post Volume Hash Table will have 2^logsize buckets
  */
@@ -7599,3 +7729,21 @@ VPrintExtendedCacheStats(int flags)
     VOL_UNLOCK;
 }
 #endif /* AFS_DEMAND_ATTACH_FS */
+
+afs_int32
+VCanScheduleSalvage(void)
+{
+    return vol_opts.canScheduleSalvage;
+}
+
+afs_int32
+VCanUseFSSYNC(void)
+{
+    return vol_opts.canUseFSSYNC;
+}
+
+afs_int32
+VCanUseSALVSYNC(void)
+{
+    return vol_opts.canUseSALVSYNC;
+}