changeset 256:68f091c9524a

Command "copy-skel" implemented
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 12 Sep 2022 09:40:20 +0200
parents 7f21d242f79f
children 71fcef7b8e65
files sbin/ftjail
diffstat 1 files changed, 93 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/sbin/ftjail	Mon Sep 12 08:26:57 2022 +0200
+++ b/sbin/ftjail	Mon Sep 12 09:40:20 2022 +0200
@@ -71,6 +71,15 @@
 
   snapshot-tmpl BASE-RO SKELETON-RW SNAPSHOT-NAME
 
+  copy-skel [ OPTIONS ] SOURCE-DS SNAPSHOT-NAME TARGET-DS
+
+    -A        Set "canmount=noauto" for all datasets in the target dataset
+    -L        Copy dataset properties optimized for employing a
+              "skeleton" subdirectory
+    -P        Copy dataset properties optimized for direct mounts
+              of skeleton children over an already mounted base
+    -u        Do not mount the target dataset automatically
+
 ENVIRONMENT:
 
   All environment variables that affect "zfs" are effective also.
@@ -242,9 +251,10 @@
         # In this case the skeleton root needs to be mounted into a "skeleton" subdir
         zfs create -u -o canmount=noauto "${_ds_skel}"
     else
-        # Only childres are to be mounted
+        # Only children are to be mounted
         zfs create -u -o canmount=off "${_ds_skel}"
     fi
+    # "usr" is only a container holding "usr/local"
     zfs create -u -o canmount=off "${_ds_skel}/usr"
     #
     # XXX FIXME: What about usr/ports/distfiles
@@ -692,6 +702,85 @@
 }
 
 
+#:
+#: Implementation of "copy-skel"
+#:
+command_copy_skel() {
+    local _ds_source _snapshot_name _ds_target
+    local _opt_symlink _opt_nomount
+
+    local _opt _name _relative_name _opt_canmount
+
+    _opt_symlink=""
+    _opt_nomount=""
+    _opt_canmount="-o canmount=on"
+
+
+    while getopts "ALPu" _opt ; do
+        case ${_opt} in
+            A)
+                _opt_canmount="-o canmount=noauto"
+                ;;
+            L)
+                _opt_symlink="yes"
+                ;;
+            P)
+                _opt_symlink="no"
+                ;;
+            u)
+                _opt_nomount="-u"
+                ;;
+            \?)
+                return 2;
+                ;;
+        esac
+    done
+    shift $((OPTIND-1))
+    OPTIND=1
+
+    [ -z "${_opt_symlink}" ] && { echo "ERROR: -L or -P must be given" 1>&2; return 2; }
+
+    _ds_source="${1-}"
+    _snapshot_name="${2-}"
+    _ds_target="${3-}"
+
+    [ -z "${_ds_source}" ] && { echo "ERROR: no source given" 1>&2; return 2; }
+    [ -z "${_snapshot_name}" ] && { echo "ERROR: no snapshot name given" 1>&2; return 2; }
+    [ -z "${_ds_target}" ] && { echo "ERROR: no target given" 1>&2; return 2; }
+
+    zfs list -r -t all -o name "${_ds_source}" \
+    | {
+        while IFS=$'\t' read -r _name ; do
+            if [ "${_name}" = "${_name%@*}@${_snapshot_name}" ]; then
+                echo "FOUND: $_name"
+                # Determine the relative name of the dataset
+                _relative_name="${_name#${_ds_source}}"
+                _relative_name="${_relative_name%@*}"
+                echo "  -> $_relative_name"
+                if [ -z "${_relative_name}" ]; then
+                    # root
+                    if [ "${_opt_symlink}" = "yes" ]; then
+                        zfs send -Lec -p -v "${_name}" | zfs receive ${_opt_nomount} -v ${_opt_canmount} -x mountpoint "${_ds_target}${_relative_name}"
+                    else
+                        zfs send -Lec -p -v "${_name}" | zfs receive ${_opt_nomount} -v -o canmount=off -x mountpoint "${_ds_target}${_relative_name}"
+                    fi
+                else
+                    # child
+                    if [ "${_relative_name}" = "/usr" ]; then
+                        zfs send -Lec -p -v "${_name}" | zfs receive ${_opt_nomount} -v -o canmount=off -x mountpoint "${_ds_target}${_relative_name}"
+                    else
+                        zfs send -Lec -p -v "${_name}" | zfs receive ${_opt_nomount} -v ${_opt_canmount} -x mountpoint "${_ds_target}${_relative_name}"
+                    fi
+                fi
+            fi
+        done
+    }
+    # Need only the filesystem data (no associated snapshots)
+    echo "Destroying unneeded snapshots ..."
+    zfs destroy -rv "${_ds_target}@${_snapshot_name}"
+}
+
+
 #
 # Global option handling
 #
@@ -746,6 +835,9 @@
     snapshot-tmpl)
         command_snapshot_tmpl "$@"
         ;;
+    copy-skel)
+        command_copy_skel "$@"
+        ;;
     configure)
         echo "ERROR: use \`fjail configure' instead" 1>&2;
         exit 2