changeset 500:7d498093d4c2

fzfs: Try to unmount filesystems that have been mounted by this tool when an error occurs
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 30 Aug 2024 18:50:12 +0200
parents 562e630afb25
children 5255651502bb
files sbin/fzfs
diffstat 1 files changed, 56 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/sbin/fzfs	Fri Aug 30 17:29:20 2024 +0200
+++ b/sbin/fzfs	Fri Aug 30 18:50:12 2024 +0200
@@ -43,6 +43,7 @@
 
 _p_datadir="$(dirname "$0")"/../share/local-bsdtools
 . "${_p_datadir}/common.subr"
+. "${_p_datadir}/array.sh"
 
 
 #
@@ -123,6 +124,14 @@
 
     zfs list -H -o name,mountpoint,canmount,mounted -s mountpoint -t filesystem -r "${_dsname}" \
     | {
+
+        #
+        # _mounted_datasets is an array of ZFS datasets that have been
+        # mounted by this routine and should be unmounted on errors
+        # -- if possible.
+        #
+        array_create _mounted_datasets
+
         while IFS=$'\t' read -r _name _mp _canmount _mounted ; do
             # Skip filesystems that are already mounted
             [ "${_mounted}" = "yes" ] && continue
@@ -151,8 +160,9 @@
                     if [ "${_mp}" = "${_rootds_mountpoint}" ]; then
                         if [ "${_name}" != "${_dsname}" ]; then
                             echo "ERROR: child dataset mounts over root dataset" >&2
+                            _umount_datasets _mounted_datasets || true
                             return 1
-                        fi                            
+                        fi
                         _relative_mp=""
                         _real_mp="${_mountpoint}"
                     else
@@ -161,6 +171,7 @@
                         _relative_mp="${_relative_mp%/}"
                         if [ -z "${_relative_mp}" ]; then
                             echo "ERROR: got an empty relative mountpoint in child" >&2
+                            _umount_datasets _mounted_datasets || true
                             return 1
                         fi
                         # The real effective full mountpoint
@@ -175,6 +186,7 @@
                     if [ "${_opt_mount_natural}" = "yes" ]; then
                         if [ "${_real_mp}" != "${_mp}" ]; then
                             echo "ERROR: mountpoint mismatch" >&2
+                            _umount_datasets _mounted_datasets || true
                             return 1
                         fi
                     fi
@@ -183,9 +195,14 @@
                         echo "Would mount ${_name} on ${_real_mp}"
                     else
                         mkdir -p "${_real_mp}" 1> /dev/null 2> /dev/null || \
-                            { echo "ERROR: cannot create mountpoint ${_real_mp}" >&2; return 1; }
+                            { echo "ERROR: cannot create mountpoint ${_real_mp}" >&2; _umount_datasets _mounted_datasets || true; return 1; }
                         echo "Mounting ${_name} on ${_real_mp}"
-                        mount -t zfs "${_name}" "${_real_mp}" || return 1
+                        if mount -t zfs "${_name}" "${_real_mp}"; then
+                            array_append _mounted_datasets "${_name}"
+                        else
+                            _umount_datasets _mounted_datasets || true
+                            return 1
+                        fi
                     fi
                     ;;
                 *)
@@ -194,7 +211,12 @@
                             echo "Would mount ${_name} on configured ZFS dataset mountpoint ${_mp}"
                         else
                             echo "Mounting ${_name} on configured ZFS dataset mountpoint ${_mp}"
-                            zfs mount "${_name}" || return 1
+                            if zfs mount "${_name}"; then
+                                array_append _mounted_datasets "${_name}"
+                            else
+                                _umount_datasets _mounted_datasets || true
+                                return 1
+                            fi
                         fi
                     else
                         echo "Skipping ${_name} because its configured ZFS mountpoint is not relative to given root dataset" 2>&1
@@ -202,13 +224,42 @@
                     ;;
             esac
         done
-
+        array_destroy _mounted_datasets
         return 0
     }
 }
 
 
 #:
+#: Helper to unmount mounted ZFS filesystems
+#:
+#: Args:
+#:   $1 (str): The name of the array that contains the datasets to unmount to
+#:
+#: Returns:
+#:   0 if successfully unmounted all given datasets,
+#:   1 otherwise
+#:
+#: Unmounting is done in the reverse order.
+#:
+_umount_datasets() {
+    array_reversed_for_each "$1" _umount_datasets_umount
+}
+
+
+#:
+#: Array callback to unmount a single ZFS filesystem
+#:
+_umount_datasets_umount() {
+    if ! zfs umount "$3" ; then
+        warn "Dataset \`${3}' cannot unmounted"
+        return 1
+    fi
+    return 0
+}
+
+
+#:
 #: Implement the "umount" command.
 #:
 #: Umount a datasets and recursively all its children datasets.