changeset 276:3c24b07240f2

Move the implementation of "mount" and "umount" into the new tool fzfs. It is not jail-specific but in reality a helper for some ZFS management issues.
author Franz Glasner <fzglas.hg@dom66.de>
date Sat, 17 Sep 2022 16:47:32 +0200
parents 5bb4c4044e48
children 0da26750b6fb
files Makefile pkg-plist sbin/fjail sbin/fzfs
diffstat 4 files changed, 276 insertions(+), 182 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sat Sep 17 16:29:17 2022 +0200
+++ b/Makefile	Sat Sep 17 16:47:32 2022 +0200
@@ -30,7 +30,7 @@
 do-extract:
 	${MKDIR} ${WRKSRC}/bin
 	${MKDIR} ${WRKSRC}/sbin
-.for _rp in sbin/check-ports sbin/fjail sbin/ftjail sbin/fpkg sbin/bsmtp2dma
+.for _rp in sbin/check-ports sbin/fjail sbin/ftjail sbin/fzfs sbin/fpkg sbin/bsmtp2dma
 	${CP} -v ${SRC}/${_rp} ${WRKSRC}/${_rp}
 	${SED} -i "" -E -e "s|\\\$$Date\\\$$|\$$Date: ${HGDATE} \$$|" ${WRKSRC}/${_rp}
 	${SED} -i "" -E -e "s|\\\$$Revision\\\$$|\$$Revision: ${HGREVISION} \$$|" ${WRKSRC}/${_rp}
@@ -52,7 +52,7 @@
 .endfor
 
 do-install:
-.for _rp in sbin/check-ports sbin/fjail sbin/ftjail sbin/fpkg sbin/bsmtp2dma
+.for _rp in sbin/check-ports sbin/fjail sbin/ftjail sbin/fzfs sbin/fpkg sbin/bsmtp2dma
 	${INSTALL_SCRIPT} ${WRKSRC}/${_rp} ${STAGEDIR}${PREFIX}/${_rp}
 .endfor
 	${MKDIR} ${STAGEDIR}${ETCDIR}
--- a/pkg-plist	Sat Sep 17 16:29:17 2022 +0200
+++ b/pkg-plist	Sat Sep 17 16:47:32 2022 +0200
@@ -5,6 +5,7 @@
 sbin/fjail
 sbin/ftjail
 sbin/fpkg
+sbin/fzfs
 @sample %%ETCDIR%%/bsmtp2dma.conf.sample
 @sample %%ETCDIR%%/package-mapping.conf.sample
 @sample %%ETCDIR%%/pkgtools.conf.sample
--- a/sbin/fjail	Sat Sep 17 16:29:17 2022 +0200
+++ b/sbin/fjail	Sat Sep 17 16:47:32 2022 +0200
@@ -41,21 +41,13 @@
     -T        Create only an extra tiny set of datasets
     -u        Do not automatically mount newly created datasets
 
-  mount [-O] [-u] [-n] DATASET [MOUNTPOINT]
+  mount
 
-    Mount the ZFS dataset DATASET and all its children to mountpoint
-    MOUNTPOINT
+    See sibling tool `fzfs'"'"'
 
-    -O        Also mount datasets at mountpoints outside of their "natural"
-              and inherited mountpoints
-    -N        Mount at their "natural" configured ZFS mountpoints (MOUNTPOINT is not required)
-    -P        Do not mount the given prent DATASET but only its children
-    -n        Do not really mount but show what would be mounted where
-    -u        Alias of -n
+  umount
 
-  umount DATASET
-
-    Unmount the mounted DATASET and all its children
+    See sibling tool `fzfs'"'"'
 
   privs MOUNTPOINT
 
@@ -490,172 +482,6 @@
 
 
 #
-# "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 _mount_outside _mount_natural _mount_children_only
-
-    _dry_run=""
-    _mount_outside=""
-    _mount_natural=""
-    _mount_children_only=""
-    while getopts "ONPnu" _opt ; do
-        case ${_opt} in
-            O)
-                _mount_outside="yes"
-                ;;
-            N)
-                _mount_natural="yes"
-                ;;
-            P)
-                _mount_children_only="yes"
-                ;;
-            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
-
-    _rootds_mountpoint="$(zfs list -H -o mountpoint -t filesystem "${_dsname}")"  || \
-        { echo "ERROR: root dataset does not exist" >&2; return 1; }
-
-    if [ -z "${_mountpoint}" ]; then
-        if [ "${_mount_natural}" = "yes" ]; then
-            _mountpoint="${_rootds_mountpoint}"
-        else
-            echo "ERROR: no mountpoint given" >&2
-            return 2
-        fi
-    else
-        if [ "${_mount_natural}" = "yes" ]; then
-            echo "ERROR: Cannot have a custom mountpoint when \"-O\" is given" >&2
-            return 2
-        fi
-    fi
-
-    # Eventually remove a trailing slash
-    _mountpoint="${_mountpoint%/}"
-    if [ -z "${_mountpoint}" ]; then
-        echo "ERROR: would mount over the root filesystem" >&2
-        return 1
-    fi
-
-    zfs list -H -o name,mountpoint,canmount,mounted -s mountpoint -t filesystem -r "${_dsname}" \
-    | {
-        while IFS=$'\t' read -r _name _mp _canmount _mounted ; do
-            # Skip filesystems that are already mounted
-            [ "${_mounted}" = "yes" ] && continue
-            # Skip filesystems that must not be mounted
-            [ "${_canmount}" = "off" ] && continue
-            # Mount only the children and skip the given dataset it required
-            [ \( "${_mount_children_only}" = "yes" \) -a \( "${_name}" = "${_dsname}" \) ] && 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}}"
-                    # Eventually remove a trailing slash
-                    _relative_mp="${_relative_mp%/}"
-                    # The real effective full mountpoint
-                    _real_mp="${_mountpoint}${_relative_mp}"
-
-                    #
-                    # Consistency and sanity check: computed real mountpoint must
-                    # be equal to the configured mountpoint when no custom mountpoint
-                    # is given.
-                    #
-                    if [ "${_mount_natural}" = "yes" ]; then
-                        if [ "${_real_mp}" != "${_mp}" ]; then
-                            echo "ERROR: mountpoint mismatch" >&2
-                            return 1
-                        fi
-                    fi
-
-                    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
-                    ;;
-                *)
-                    if [ "${_mount_outside}" = "yes" ]; then
-                        if [ "${_dry_run}" = "yes" ]; then
-                            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
-                        fi
-                    else
-                        echo "Skipping ${_name} because its configured ZFS mountpoint is not relative to given root dataset" 2>&1
-                    fi
-                    ;;
-            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}")" || { echo "ERROR: dataset not found" >&2; return 1; }
-
-    mount -t zfs -p \
-    | grep -E "^${_dsname}(/|\s)" \
-    | sort -n -r \
-    | {
-        while IFS=' '$'\t' read -r _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.
@@ -741,10 +567,10 @@
         command_datasets "$@"
         ;;
     mount)
-        command_mount "$@"
+        exec "$(dirname $0)/fzfs" mount "$@"
         ;;
     umount|unmount)
-        command_umount "$@"
+        exec "$(dirname $0)/fzfs" umount "$@"
         ;;
     privs)
         command_privs "$@"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/fzfs	Sat Sep 17 16:47:32 2022 +0200
@@ -0,0 +1,267 @@
+#!/bin/sh
+# -*- indent-tabs-mode: nil; -*-
+#:
+#: A ZFS management helper tool.
+#:
+#: :Author:    Franz Glasner
+#: :Copyright: (c) 2022 Franz Glasner.
+#:             All rights reserved.
+#: :License:   BSD 3-Clause "New" or "Revised" License.
+#:             See LICENSE for details.
+#:             If you cannot find LICENSE see
+#:             <https://opensource.org/licenses/BSD-3-Clause>
+#: :ID:        @(#)@@PKGORIGIN@@ $HGid$
+#:
+
+set -eu
+
+VERSION="@@VERSION@@"
+
+USAGE='
+USAGE: fzfs [ OPTIONS ] COMMAND [ COMMAND OPTIONS ] [ ARG ... ]
+
+OPTIONS:
+
+  -V    Print the program name and version number to stdout and exit
+
+  -h    Print this help message to stdout and exit
+
+COMMANDS:
+
+  mount [-O] [-u] [-n] DATASET [MOUNTPOINT]
+
+    Mount the ZFS dataset DATASET and all its children to mountpoint
+    MOUNTPOINT
+
+    -O        Also mount datasets at mountpoints outside of their "natural"
+              and inherited mountpoints
+    -N        Mount at their "natural" configured ZFS mountpoints
+              (MOUNTPOINT is not required)
+    -P        Do not mount the given parent DATASET but only its children
+    -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
+
+  unmount
+
+    Alias for `umount'"'"'
+
+'
+
+#
+#: Implementation of the "mount" command.
+#:
+#: Mount a dataset and recursively all its children datasets.
+#:
+command_mount() {
+    local _dsname _mountpoint
+    local _opt_dry_run _opt_mount_outside _opt_mount_natural
+    local _opt_mount_children_only
+
+    local _name _mp _canmount _mounted _rootds_mountpoint _relative_mp _real_mp
+
+    _opt_dry_run=""
+    _opt_mount_outside=""
+    _opt_mount_natural=""
+    _opt_mount_children_only=""
+    while getopts "ONPnu" _opt ; do
+        case ${_opt} in
+            O)
+                _opt_mount_outside="yes"
+                ;;
+            N)
+                _opt_mount_natural="yes"
+                ;;
+            P)
+                _opt_mount_children_only="yes"
+                ;;
+            n|u)
+                _opt_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
+
+    _rootds_mountpoint="$(zfs list -H -o mountpoint -t filesystem "${_dsname}")"  || \
+        { echo "ERROR: root dataset does not exist" >&2; return 1; }
+
+    if [ -z "${_mountpoint}" ]; then
+        if [ "${_opt_mount_natural}" = "yes" ]; then
+            _mountpoint="${_rootds_mountpoint}"
+        else
+            echo "ERROR: no mountpoint given" >&2
+            return 2
+        fi
+    else
+        if [ "${_opt_mount_natural}" = "yes" ]; then
+            echo "ERROR: Cannot have a custom mountpoint when \"-O\" is given" >&2
+            return 2
+        fi
+    fi
+
+    # Eventually remove a trailing slash
+    _mountpoint="${_mountpoint%/}"
+    if [ -z "${_mountpoint}" ]; then
+        echo "ERROR: would mount over the root filesystem" >&2
+        return 1
+    fi
+
+    zfs list -H -o name,mountpoint,canmount,mounted -s mountpoint -t filesystem -r "${_dsname}" \
+    | {
+        while IFS=$'\t' read -r _name _mp _canmount _mounted ; do
+            # Skip filesystems that are already mounted
+            [ "${_mounted}" = "yes" ] && continue
+            # Skip filesystems that must not be mounted
+            [ "${_canmount}" = "off" ] && continue
+            #
+            # Mount only the children and skip the given parent dataset
+            # if required
+            #
+            [ \( "${_opt_mount_children_only}" = "yes" \) -a \( "${_name}" = "${_dsname}" \) ] && 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}}"
+                    # Eventually remove a trailing slash
+                    _relative_mp="${_relative_mp%/}"
+                    # The real effective full mountpoint
+                    _real_mp="${_mountpoint}${_relative_mp}"
+
+                    #
+                    # Consistency and sanity check: computed real mountpoint must
+                    # be equal to the configured mountpoint when no custom mountpoint
+                    # is given.
+                    #
+                    if [ "${_opt_mount_natural}" = "yes" ]; then
+                        if [ "${_real_mp}" != "${_mp}" ]; then
+                            echo "ERROR: mountpoint mismatch" >&2
+                            return 1
+                        fi
+                    fi
+
+                    if [ "${_opt_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
+                    ;;
+                *)
+                    if [ "${_opt_mount_outside}" = "yes" ]; then
+                        if [ "${_opt_dry_run}" = "yes" ]; then
+                            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
+                        fi
+                    else
+                        echo "Skipping ${_name} because its configured ZFS mountpoint is not relative to given root dataset" 2>&1
+                    fi
+                    ;;
+            esac
+        done
+
+        return 0
+    }
+}
+
+
+#:
+#: Implement the "umount" command.
+#:
+#: Umount a datasets and recursively all its children datasets.
+#:
+command_umount() {
+    local _dsname
+
+    local _name _mp _rest _rootds_mountpoint
+
+    _dsname="${1-}"
+    [ -z "${_dsname}" ] && { echo "ERROR: no dataset given" 1>&2; return 2; }
+
+    # Just determine whether the given dataset name exists
+    _rootds_mountpoint="$(zfs list -H -o mountpoint -t filesystem "${_dsname}")" || { echo "ERROR: dataset not found" 1>&2; return 1; }
+
+    mount -t zfs -p \
+    | grep -E "^${_dsname}(/|\s)" \
+    | sort -n -r \
+    | {
+        while IFS=' '$'\t' read -r _name _mp _rest ; do
+            echo "Umounting ${_name} on ${_mp}"
+            umount "${_mp}" || return 1
+        done
+    }
+    return 0
+}
+
+#
+# Global option handling
+#
+while getopts "Vh" _opt ; do
+    case ${_opt} in
+        V)
+            printf 'ftjail v%s (rv:%s)\n' "${VERSION}" '@@HGREVISION@@'
+            exit 0
+            ;;
+        h)
+            echo "${USAGE}"
+            exit 0
+            ;;
+        \?)
+            exit 2;
+            ;;
+        *)
+            echo "ERROR: option handling failed" 1>&2
+            exit 2
+            ;;
+    esac
+done
+#
+# Reset the Shell's option handling system to prepare for handling
+# command-local options.
+#
+shift $((OPTIND-1))
+OPTIND=1
+
+test $# -gt 0 || { echo "ERROR: no command given" 1>&2; exit 2; }
+
+command="$1"
+shift
+
+case "${command}" in
+    mount)
+        command_mount "$@"
+        ;;
+    umount|unmount)
+        command_umount "$@"
+        ;;
+    *)
+        echo "ERROR: unknown command \`${command}'" 1>&2
+        exit 2
+        ;;
+esac