changeset 194:379d3178f3ce

mount and umount support for ZFS datasets: recursively mount and unmount
author Franz Glasner <fzglas.hg@dom66.de>
date Sun, 21 Aug 2022 08:35:18 +0200
parents 62a24dfb238c
children 4a0cb73945a8
files sbin/fjail
diffstat 1 files changed, 133 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/sbin/fjail	Sat Aug 20 13:07:53 2022 +0200
+++ b/sbin/fjail	Sun Aug 21 08:35:18 2022 +0200
@@ -39,6 +39,18 @@
     -T        Create only an extra tiny set of datasets
     -u        Do not automatically mount newly created datasets
 
+  mount [-u] [-n] DATASET MOUNTPOINT
+
+    Mount the ZFS dataset DATASET and all its children to mountpoint
+    MOUNTPOINT
+
+    -n        Do not really mount but show what would be mounted where
+    -u        Alias of -n
+
+  umount DATASET
+
+    Unmount the mounted DATASET and all its children
+
   privs MOUNTPOINT
 
     Adjust some Unix privileges to mounted jail datasets
@@ -337,6 +349,121 @@
 
 
 #
+# "mount" -- recursively mount a dataset including subordinate datasets
+#
+# command_mount dataset mountpoint
+#
+command_mount() {
+    local _dsname _mountpoint
+    local _name _mp _canmount _mounted
+    local _rootds_mountpoint _relative_mp _real_mp
+    local _dry_run
+
+    _dry_run=""
+    while getopts "nu" _opt ; do
+        case ${_opt} in
+            n|u)
+                _dry_run="yes"
+                ;;
+            \?|:)
+                return 2;
+                ;;
+        esac
+    done
+    shift $((OPTIND-1))
+    OPTIND=1
+
+    _dsname="${1-}"
+    _mountpoint="${2-}"
+
+    if [ -z "${_dsname}" ]; then
+        echo "ERROR: no dataset given" >&2
+        return 2
+    fi
+    if [ -z "${_mountpoint}" ]; then
+        echo "ERROR: no mountpoint given" >&2
+        return 2
+    fi
+    # Remove a trailing slash
+    _mountpoint="${_mountpoint%/}"
+    if [ -z "${_mountpoint}" ]; then
+        echo "ERROR: would mount over the root filesystem" >&2
+        return 1
+    fi
+
+    _rootds_mountpoint="$(zfs list -H -o mountpoint -t filesystem "${_dsname}")"  || \
+        { echo "ERROR: root dataset does not exist" >&2; return 1; }
+
+    zfs list -H -o name,mountpoint,canmount,mounted -s mountpoint -t filesystem -r "${_dsname}" | \
+        while IFS=$'\t' read _name _mp _canmount _mounted ; do
+            # Skip filesystems that are already mounted
+            [ "${_mounted}" = "yes" ] && continue
+            # Skip filesystems that must not be mounted
+            [ "${_canmount}" = "off" ] && continue
+            case "${_mp}" in
+                "none"|"legacy")
+                    # Do nothing for filesystem with unset or legacy mountpoints
+                    ;;
+                "${_rootds_mountpoint}"|"${_rootds_mountpoint}/"*)
+                    #
+                    # Handle only mountpoints that have a mountpoint below
+                    # the parent datasets mountpoint
+                    #
+
+                    # Determine the mountpoint relative to the parent mountpoint
+                    _relative_mp="${_mp#${_rootds_mountpoint}}"
+                    # Remove a trailing slash
+                    _relative_mp="${_relative_mp%/}"
+                    _real_mp="${_mountpoint}${_relative_mp}"
+                    if [ "${_dry_run}" = "yes" ]; then
+                        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 "Mounting ${_name} on ${_real_mp}"
+                        mount -t zfs "${_name}" "${_real_mp}" || return 1
+                    fi
+                    ;;
+                *)
+                    # XXX FIXME Option to do a zfs mount $_name ???
+                    echo "Skipping ${_name} because its configured mountpoint is not relative to given root dataset" 2>&1
+                    ;;
+            esac
+        done
+
+    return 0
+}
+
+
+#
+# "umount" -- Recursively unmount ZFS datasets
+#
+# command_umount dataset
+#
+command_umount() {
+    local _dsname
+    local _name _mp _rest
+    local _rootds_mountpoint
+
+    _dsname="${1-}"
+    [ -z "${_dsname}" ] && \
+        { echo "ERROR: no dataset given" >&2; return 2; }
+
+    # Just determine whether the given dataset name exists
+    _rootds_mountpoint="$(zfs list -H -o mountpoint -t filesystem "${_dsname}")" | return 1
+
+    mount -t zfs -p | \
+        grep -E "^${_dsname}(/|\s)" | \
+        sort -n -r | \
+        while IFS=' '$'\t' read _name _mp _rest ; do
+            echo "Umounting ${_name} on ${_mp}"
+            umount "${_mp}" || return 1
+        done
+    return 0
+}
+
+
+#
 # "privs" -- adjust privileges
 #
 # To be used when all ZFS datasets are mounted.
@@ -424,6 +551,12 @@
     datasets)
         command_datasets "$@"
         ;;
+    mount)
+        command_mount "$@"
+        ;;
+    umount|unmount)
+        command_umount "$@"
+        ;;
     privs)
         command_privs "$@"
         ;;