viced: Correct unblessed attach2 errors
[openafs.git] / src / vol / volume.c
index b3e29d7..2fc88ab 100644 (file)
 #include "volume.h"
 #include "partition.h"
 #include "volume_inline.h"
+#include "common.h"
+
 #ifdef AFS_PTHREAD_ENV
 #include <assert.h>
 #else /* AFS_PTHREAD_ENV */
@@ -174,8 +176,6 @@ static volatile sig_atomic_t vol_disallow_salvsync = 0;
 extern void *calloc(), *realloc();
 #endif
 
-/*@printflike@*/ extern void Log(const char *format, ...);
-
 /* Forward declarations */
 static Volume *attach2(Error * ec, VolId volumeId, char *path,
                       struct DiskPartition64 *partp, Volume * vp, 
@@ -489,6 +489,12 @@ bit32 VolumeCacheCheck;            /* Incremented everytime a volume goes on line--
 /***************************************************/
 /* Startup routines                                */
 /***************************************************/
+
+#if defined(FAST_RESTART) && defined(AFS_DEMAND_ATTACH_FS)
+# error FAST_RESTART and DAFS are incompatible. For the DAFS equivalent \
+        of FAST_RESTART, use the -unsafe-nosalvage fileserver argument
+#endif
+
 /**
  * assign default values to a VolumePackageOptions struct.
  *
@@ -508,6 +514,12 @@ VOptDefaults(ProgramType pt, VolumePackageOptions *opts)
     opts->canUseFSSYNC = 0;
     opts->canUseSALVSYNC = 0;
 
+#ifdef FAST_RESTART
+    opts->unsafe_attach = 1;
+#else /* !FAST_RESTART */
+    opts->unsafe_attach = 0;
+#endif /* !FAST_RESTART */
+
     switch (pt) {
     case fileServer:
        opts->canScheduleSalvage = 1;
@@ -836,14 +848,15 @@ VInitAttachVolumes(ProgramType pt)
         /* create threads to scan disk partitions. */
        for (i=0; i < threads; i++) {
            struct vinitvolumepackage_thread_param *params;
-           params = (struct vinitvolumepackage_thread_param *)malloc(sizeof(struct vinitvolumepackage_thread_param));
+            AFS_SIGSET_DECL;
+
+            params = (struct vinitvolumepackage_thread_param *)malloc(sizeof(struct vinitvolumepackage_thread_param));
             assert(params);
             params->pq = &pq;
             params->vq = &vq;
             params->nthreads = threads;
             params->thread = i+1;
 
-            AFS_SIGSET_DECL;
             AFS_SIGSET_CLEAR();
            assert(pthread_create (&tid, &attrs, &VInitVolumePackageThread, (void*)params) == 0);
             AFS_SIGSET_RESTORE();
@@ -2447,13 +2460,6 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
            goto done;
        }
 #endif
-       V_needsCallback(vp) = 0;
-#ifdef notdef
-       if (VInit >= 2 && V_BreakVolumeCallbacks) {
-           Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
-           (*V_BreakVolumeCallbacks) (V_id(vp));
-       }
-#endif
        VUpdateVolume_r(ec, vp, 0);
        if (*ec) {
            Log("VAttachVolume: Error updating volume\n");
@@ -2601,7 +2607,6 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
        goto done;
     }
 
-    V_needsCallback(vp) = 0;
     VUpdateVolume_r(ec, vp, 0);
     if (*ec) {
        Log("VAttachVolume: Error updating volume %u\n", vp->hashid);
@@ -3067,6 +3072,12 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
      * cleanup? */
     int forcefree = 0;
 
+#ifdef AFS_DEMAND_ATTACH_FS
+    /* in the case of an error, to what state should the volume be
+     * transitioned? */
+    VolState error_state = VOL_STATE_ERROR;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
     *ec = 0;
 
     vp->vnodeIndex[vLarge].handle = NULL;
@@ -3207,49 +3218,46 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
 
     VOL_LOCK;
     vp->nextVnodeUnique = V_uniquifier(vp);
-    if (VShouldCheckInUse(mode)) {
-#ifndef FAST_RESTART
-       if (V_inUse(vp) && VolumeWriteable(vp)) {
-           if (!V_needsSalvaged(vp)) {
-               V_needsSalvaged(vp) = 1;
-               VUpdateVolume_r(ec, vp, 0);
-           }
+
+    if (VShouldCheckInUse(mode) && V_inUse(vp) && VolumeWriteable(vp)) {
+       if (!V_needsSalvaged(vp)) {
+           V_needsSalvaged(vp) = 1;
+           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;
+       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;
 
 #else /* AFS_DEMAND_ATTACH_FS */
-           Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
-           *ec = VSALVAGE;
+       Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
+       *ec = VSALVAGE;
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-           goto error;
-       }
-#endif /* FAST_RESTART */
+       goto error;
+    }
 
-       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 (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);
-           VChangeState_r(vp, VOL_STATE_ERROR);
-           vp->nUsers = 0;
+       /* schedule a salvage so the volume goes away on disk */
+       VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+       VChangeState_r(vp, VOL_STATE_ERROR);
+       vp->nUsers = 0;
 #endif /* AFS_DEMAND_ATTACH_FS */
-           Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
-           *ec = VNOVOL;
-           forcefree = 1;
-           goto error;
-       }
+       Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
+       *ec = VNOVOL;
+       forcefree = 1;
+       goto error;
     }
 
     vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
@@ -3271,6 +3279,52 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
     }
 #endif /* BITMAP_LATER */
 
+    if (VInit >= 2 && V_needsCallback(vp)) {
+       if (V_BreakVolumeCallbacks) {
+           Log("VAttachVolume: Volume %lu was changed externally; breaking callbacks\n",
+               afs_printable_uint32_lu(V_id(vp)));
+           V_needsCallback(vp) = 0;
+           VOL_UNLOCK;
+           (*V_BreakVolumeCallbacks) (V_id(vp));
+           VOL_LOCK;
+
+           VUpdateVolume_r(ec, vp, 0);
+       }
+#ifdef FSSYNC_BUILD_CLIENT
+       else if (VCanUseFSSYNC()) {
+           afs_int32 fsync_code;
+
+           V_needsCallback(vp) = 0;
+           VOL_UNLOCK;
+           fsync_code = FSYNC_VolOp(V_id(vp), NULL, FSYNC_VOL_BREAKCBKS, FSYNC_WHATEVER, NULL);
+           VOL_LOCK;
+
+           if (fsync_code) {
+               V_needsCallback(vp) = 1;
+               Log("Error trying to tell the fileserver to break callbacks for "
+                   "changed volume %lu; error code %ld\n",
+                   afs_printable_uint32_lu(V_id(vp)),
+                   afs_printable_int32_ld(fsync_code));
+           } else {
+               VUpdateVolume_r(ec, vp, 0);
+           }
+       }
+#endif /* FSSYNC_BUILD_CLIENT */
+
+       if (*ec) {
+           Log("VAttachVolume: error %d clearing needsCallback on volume "
+               "%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);
+           vp->nUsers = 0;
+#else /* !AFS_DEMAND_ATTACH_FS */
+           *ec = VSALVAGE;
+#endif /* !AFS_DEMAND_ATTACh_FS */
+           goto error;
+       }
+    }
+
     if (programType == fileServer) {
        if (vp->specialStatus)
            vp->specialStatus = 0;
@@ -3278,6 +3332,37 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
            V_inUse(vp) = fileServer;
            V_offlineMessage(vp)[0] = '\0';
        }
+       if (!V_inUse(vp)) {
+           *ec = VNOVOL;
+#ifdef AFS_DEMAND_ATTACH_FS
+           /* Put the vol into PREATTACHED state, so if someone tries to
+            * access it again, we try to attach, see that we're not blessed,
+            * and give a VNOVOL error again. Putting it into UNATTACHED state
+            * would result in a VOFFLINE error instead. */
+           error_state = VOL_STATE_PREATTACHED;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+           /* mimic e.g. GetVolume errors */
+           if (!V_blessed(vp)) {
+               Log("Volume %lu offline: not blessed\n", afs_printable_uint32_lu(V_id(vp)));
+               FreeVolumeHeader(vp);
+           } else if (!V_inService(vp)) {
+               Log("Volume %lu offline: not in service\n", afs_printable_uint32_lu(V_id(vp)));
+               FreeVolumeHeader(vp);
+           } else {
+               Log("Volume %lu offline: needs salvage\n", afs_printable_uint32_lu(V_id(vp)));
+               *ec = VSALVAGE;
+#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);
+#endif
+           }
+#ifdef AFS_DEMAND_ATTACH_FS
+           vp->nUsers = 0;
+#endif
+           goto error;
+       }
     } else {
 #ifdef AFS_DEMAND_ATTACH_FS
        if ((mode != V_PEEK) && (mode != V_SECRETLY))
@@ -3306,7 +3391,7 @@ attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
  error:
 #ifdef AFS_DEMAND_ATTACH_FS
     if (!VIsErrorState(V_attachState(vp))) {
-       VChangeState_r(vp, VOL_STATE_ERROR);
+       VChangeState_r(vp, error_state);
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
@@ -3472,6 +3557,18 @@ VGetVolume(Error * ec, Error * client_ec, VolId volumeId)
     return retVal;
 }
 
+/* same as VGetVolume, but if a volume is waiting to go offline, we return
+ * that it is actually offline, instead of waiting for it to go offline */
+Volume *
+VGetVolumeNoWait(Error * ec, Error * client_ec, VolId volumeId)
+{
+    Volume *retVal;
+    VOL_LOCK;
+    retVal = GetVolume(ec, client_ec, volumeId, NULL, 1);
+    VOL_UNLOCK;
+    return retVal;
+}
+
 Volume *
 VGetVolume_r(Error * ec, VolId volumeId)
 {
@@ -3486,14 +3583,24 @@ VGetVolumeByVp_r(Error * ec, Volume * vp)
     return GetVolume(ec, NULL, vp->hashid, vp, 0);
 }
 
-/* private interface for getting a volume handle
- * volumeId must be provided.
- * hint is an optional parameter to speed up hash lookups
- * flags is not used at this time
+/**
+ * private interface for getting a volume handle
+ *
+ * @param[out] ec         error code (0 if no error)
+ * @param[out] client_ec  wire error code to be given to clients
+ * @param[in]  volumeId   ID of the volume we want
+ * @param[in]  hint       optional hint for hash lookups, or NULL
+ * @param[in]  nowait     0 to wait for a 'goingOffline' volume to go offline
+ *                        before returning, 1 to return immediately
+ *
+ * @return a volume handle for the specified volume
+ *  @retval NULL an error occurred, or the volume is in such a state that
+ *               we cannot load a header or return any volume struct
+ *
+ * @note for DAFS, caller must NOT hold a ref count on 'hint'
  */
-/* for demand attach fs, caller MUST NOT hold a ref count on hint */
 static Volume *
-GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags)
+GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int nowait)
 {
     Volume *vp = hint;
     /* pull this profiling/debugging code out of regular builds */
@@ -3746,7 +3853,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
 
        if (programType == fileServer) {
            VGET_CTR_INC(V9);
-           if (vp->goingOffline) {
+           if (vp->goingOffline && !nowait) {
                VGET_CTR_INC(V10);
 #ifdef AFS_DEMAND_ATTACH_FS
                /* wait for the volume to go offline */
@@ -3766,7 +3873,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
            } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
                VGET_CTR_INC(V12);
                *ec = VNOVOL;
-           } else if (V_inUse(vp) == 0) {
+           } else if (V_inUse(vp) == 0 || vp->goingOffline) {
                VGET_CTR_INC(V13);
                *ec = VOFFLINE;
            } else {
@@ -4955,10 +5062,13 @@ VUpdateSalvagePriority_r(Volume * vp)
  * If we did try a salvage then the results are contained in code.
  */
 
-static inline int
+static_inline int
 try_SALVSYNC(Volume *vp, char *partName, int *code) {
 #ifdef SALVSYNC_BUILD_CLIENT
     if (VCanUseSALVSYNC()) {
+       Log("Scheduling salvage for volume %lu on part %s over SALVSYNC\n",
+           afs_printable_uint32_lu(vp->hashid), partName);
+
        /* can't use V_id() since there's no guarantee
         * we have the disk data header at this point */
        *code = SALVSYNC_SalvageVolume(vp->hashid,
@@ -4977,6 +5087,9 @@ static_inline int
 try_FSSYNC(Volume *vp, char *partName, int *code) {
 #ifdef FSSYNC_BUILD_CLIENT
     if (VCanUseFSSYNC()) {
+       Log("Scheduling salvage for volume %lu on part %s over FSSYNC\n",
+           afs_printable_uint32_lu(vp->hashid), partName);
+
        /*
         * If we aren't the fileserver, tell the fileserver the volume
         * needs to be salvaged. We could directly tell the
@@ -5621,8 +5734,7 @@ static void
 VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class)
 {
     StreamHandle_t *file;
-    int nVnodes;
-    int size;
+    afs_sfsize_t nVnodes, size;
     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
     struct vnodeIndex *vip = &vp->vnodeIndex[class];
     struct VnodeDiskObject *vnode;
@@ -7249,6 +7361,9 @@ GetVolumeHeader(register Volume * vp)
            hd = vp->header;
            queue_Remove(hd);
            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)) {
@@ -8384,9 +8499,10 @@ VPrintExtendedCacheStats_r(int flags)
     /* print extended VLRU statistics */
     if (VVLRUExtStats_r(&vlru_stats, vol_sum) == 0) {
        afs_uint32 idx, cur, lpos;
-       VOL_UNLOCK;
        VolumeId line[5];
 
+        VOL_UNLOCK;
+
        Log("VLRU State Dump:\n\n");
 
        for (idx = VLRU_QUEUE_NEW; idx < VLRU_QUEUE_INVALID; idx++) {
@@ -8446,3 +8562,9 @@ VCanUseSALVSYNC(void)
 {
     return vol_opts.canUseSALVSYNC;
 }
+
+afs_int32
+VCanUnsafeAttach(void)
+{
+    return vol_opts.unsafe_attach;
+}