diff sbin/ftjail @ 660:82a98db83a13

ftjail: Implement a "-R" option for "ftjail freebsd-update" and "ftjail check-freebsd-update". This option creates/checks ZFS checkpoints that should be taken before really do some unmounts and calling freebsd-update for a thin jail.
author Franz Glasner <fzglas.hg@dom66.de>
date Sun, 29 Sep 2024 16:04:59 +0200
parents 260a81d769a6
children 83ec66c64f47
line wrap: on
line diff
--- a/sbin/ftjail	Sun Sep 29 12:18:02 2024 +0200
+++ b/sbin/ftjail	Sun Sep 29 16:04:59 2024 +0200
@@ -45,7 +45,9 @@
 
   build-etcupdate-current-tmpl DIRECTORY TARBALL
 
-  freebsd-update [-k] [-o OLD-ORIGIN] DIRECTORY NEW-ORIGIN [ETCUPDATE-TARBALL]
+  check-freebsd-update [-k] [-o OLD-ORIGIN] [-R SNAPSHOT] DIRECTORY NEW-ORIGIN [ETCUPDATE-TARBALL]
+
+  freebsd-update [-k] [-o OLD-ORIGIN] [-R SNAPSHOT] DIRECTORY NEW-ORIGIN [ETCUPDATE-TARBALL]
 
 ENVIRONMENT:
 
@@ -901,30 +903,47 @@
 #:
 command_check_freebsd_update() {
     local _directory _new_origin _etcupdate_tarball
-    local _opt_keep _opt_old_origin
+    local _opt_keep _opt_old_origin _opt_snapshots
 
     local _errors _warnings _rc
     local _directory _new_origin _etcupdate_tarball
     local _dir_basename _dir_mounts _jailname _tmp _line _log_sock
     local _root_dataset _root_mountpoint _root_type _root_options
     local _mnt_device _mnt_mountpoint _mnt_type _mnt_options
+    local _idx _sn_ds _sn_name _sn_ds_related
 
     _rc=0
 
     _warnings=''
-    farray_create _warnings XXX
+    farray_create _warnings
     _errors=''
     farray_create _errors
 
+    _opt_snapshots=''
+    falist_create _opt_snapshots
     _opt_keep="no"
     _opt_old_origin=""
-    while getopts "ko:" _opt ; do
+    while getopts "R:ko:" _opt ; do
         case "${_opt}" in
+            R)
+                case "${OPTARG}" in
+                    *?\@?*)
+                        #
+                        # Split in two parts: dataset hierarchy and the name of the
+                        # snapshot
+                        #
+                        falist_set _opt_snapshots "${OPTARG%%@*}" "${OPTARG#*@}"
+                        ;;
+                    *)
+                        farray_append _errors "argument \`${OPTARG}' is not a snapshot name"
+                        ;;
+                    esac
+                ;;
             k)
                 _opt_keep="yes"
                 ;;
             o)
-                _opt_old_origin="$OPTARG"
+                _opt_old_origin="${OPTARG}"
                 ;;
             \?|:)
                 return 2;
@@ -952,6 +971,24 @@
         [ -r "${_etcupdate_tarball}" ] || farray_append _errors "given etcupdate tarball does not exist and/or is not readable"
     fi
 
+    # Check snapshotting
+    _idx=1
+    while falist_tryget_item_at_index _sn_ds _sn_name _opt_snapshots ${_idx}; do
+        if zfs get -H -o value name "${_sn_ds}" >/dev/null 2>/dev/null; then
+            # yes dataset exists: check that snapshots do not exist
+            while IFS=$'\t' read -r _line; do
+                if zfs get -H -o value name "${_line}@${_sn_name}" >/dev/null 2>/dev/null; then
+                    farray_append _errors "snapshot \`${_line}@${_sn_name}' already exists"
+                fi
+            done <<EOF2988ee715b2d93fd93bdce23
+$(zfs list -H -r -o name "${_sn_ds}")
+EOF2988ee715b2d93fd93bdce23
+        else
+            farray_append _errors "dataset for snapshots \`${_sn_ds}' does not exist"
+        fi
+        _idx=$((_idx + 1))
+    done
+
     if [ -n "${_directory}" ]; then
 
         _dir_basename="$(basename "${_directory}")"
@@ -1017,16 +1054,38 @@
             [ "${_root_origin}" = '-' ] &&  farray_append _errors "the root dataset is not a ZFS clone"
         fi
         #
-        # Check for open files on all the mounted filesystems
+        # Check for open files on all the mounted filesystems.
+        # If snapshots are requested check that they are related somehow to
+        # mounted filesystems.
         #
+        _sn_ds_related=''
         while IFS=$'\t' read -r _mnt_device _mnt_mountpoint _mnt_type _mnt_options _line; do
             if ! _check_no_open_files_on_filesystem "${_mnt_mountpoint}" ; then
                 farray_append _errors "There are open files or memory mapping on file system \`${_mnt_mountpoint}'"
             fi
+            _idx=1
+            while falist_tryget_key_at_index _sn_ds _opt_snapshots ${_idx}; do
+                case "${_mnt_device}" in
+                    "${_sn_ds}")
+                        _sn_ds_related="yes"
+                        ;;
+                    "${_sn_ds}"/*)
+                        _sn_ds_related="yes"
+                        ;;
+                    *)
+                        ;;
+                esac
+                _idx=$((_idx + 1))
+            done
         done <<EOF4tHGCAASL775f9f320205
 ${_dir_mounts}
 EOF4tHGCAASL775f9f320205
     fi
+    if falist_istrue _opt_snapshots; then
+        if ! checkyes _sn_ds_related; then
+            farray_append _warnings "snapshot datasets and mounted datasets are not related"
+        fi
+    fi
 
     if farray_istrue _errors; then
         _print_check_errors _errors
@@ -1037,6 +1096,7 @@
 
     farray_destroy _errors
     farray_destroy _warnings
+    falist_destroy _opt_snapshots
     return ${_rc}
 }
 
@@ -1049,27 +1109,45 @@
 #:
 command_freebsd_update() {
     local _directory _new_origin _etcupdate_tarball
-    local _opt_keep _opt_old_origin
+    local _opt_keep _opt_old_origin _opt_snapshots
 
     local _res _jailname _dir_mounts _dir_fn_fstab _dir_fn_fstab2
     local _dir_basename _dir_fn_tldir
     local _root_dataset _root_mountpoint _root_type _root_options
     local _mnt_device _mnt_mountpoint _mnt_type _mnt_options
+    local _idx _sn_ds _sn_name
     local _clone_extra_props _canmount_prop
     local _line _opt
     local _root_readonly _root_origin
     local _u_tmpdir
     local _add_log_sock
 
+    _opt_snapshots=''
+    falist_create _opt_snapshots
     _opt_keep="no"
     _opt_old_origin=""
-    while getopts "ko:" _opt ; do
+    while getopts "R:ko:" _opt ; do
         case "${_opt}" in
+            R)
+                case "${OPTARG}" in
+                    *?\@?*)
+                        #
+                        # Split in two parts: dataset hierarchy and the name of the
+                        # snapshot
+                        #
+                        falist_set _opt_snapshots "${OPTARG%%@*}" "${OPTARG#*@}"
+                        ;;
+                    *)
+                        err "argument \`${OPTARG}' is not a snapshot name"
+                        return 1
+                        ;;
+                    esac
+                ;;
             k)
                 _opt_keep="yes"
                 ;;
             o)
-                _opt_old_origin="$OPTARG"
+                _opt_old_origin="${OPTARG}"
                 ;;
             \?|:)
                 return 2;
@@ -1092,6 +1170,26 @@
         [ -r "${_etcupdate_tarball}" ] || { echo "ERROR: given etcupdate tarball does not exist and/or is not readable" 1>&2; return 1; }
     fi
 
+    # Check snapshotting
+    _idx=1
+    while falist_tryget_item_at_index _sn_ds _sn_name _opt_snapshots ${_idx}; do
+        if zfs get -H -o value name "${_sn_ds}" >/dev/null 2>/dev/null; then
+            # yes dataset exists: check that snapshots do not exist
+            while IFS=$'\t' read -r _line; do
+                if zfs get -H -o value name "${_line}@${_sn_name}" >/dev/null 2>/dev/null; then
+                    err "snapshot \`${_line}@${_sn_name}' already exists"
+                    return 1
+                fi
+            done <<EOF2988ee715b2d93fd93bdce23
+$(zfs list -H -r -o name "${_sn_ds}")
+EOF2988ee715b2d93fd93bdce23
+        else
+            err "dataset for snapshots \`${_sn_ds}' does not exist"
+            return 1
+        fi
+        _idx=$((_idx + 1))
+    done
+
     _dir_basename="$(basename "${_directory}")"
 
     set +e
@@ -1191,6 +1289,13 @@
     _dir_fn_tldir="${_u_tmpdir}/tldirs"
     LC_ALL=C /usr/bin/find "${_directory}" -depth 1 -type d 2>/dev/null | LC_ALL=C /usr/bin/sort >>"${_dir_fn_tldir}"
 
+    _idx=1
+    while falist_tryget_item_at_index _sn_ds _sn_name _opt_snapshots ${_idx}; do
+        echo "Creating snapshot \`${_sn_ds}@${_sn_name}'"
+        zfs snapshot -r "${_sn_ds}@${_sn_name}" || { err "cannot snapshot \`${_sn_ds}@${_sn_name}'"; return 1; }
+        _idx=$((_idx + 1))
+    done
+
     # Unmount in reverse order: unmount can do it for us
     echo "Unmounting all datasets mounted at \`${_directory}'"
     /sbin/umount -a -F "${_dir_fn_fstab2}" -v
@@ -1277,6 +1382,8 @@
         [ -n "${_u_tmpdir}" ] && [ -d "${_u_tmpdir}" ] && rm -rvf "${_u_tmpdir}"
     fi
     echo "Done."
+
+    falist_destroy _opt_snapshots
 }