changeset 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 25de0d27e407
children 1e6c86f0a850
files docs/man/man8/ftjail-check-freebsd-update.rst docs/man/man8/ftjail-freebsd-update.rst sbin/ftjail share/examples/local-bsdtools/freebsd-update-ftjail.sh
diffstat 4 files changed, 146 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/docs/man/man8/ftjail-check-freebsd-update.rst	Sun Sep 29 12:18:02 2024 +0200
+++ b/docs/man/man8/ftjail-check-freebsd-update.rst	Sun Sep 29 16:04:59 2024 +0200
@@ -9,7 +9,7 @@
 Synopsis
 --------
 
-**ftjail check-freebsd-update** [**-k**] [**-o** `old-origin`] `directory` `new-origin` [`etcupdate-tarball`]
+**ftjail check-freebsd-update** [**-k**] [**-o** `old-origin`] [[**-R** `dataset@snapname`]...] `directory` `new-origin` [`etcupdate-tarball`]
 
 
 Description
@@ -29,6 +29,18 @@
 Options
 -------
 
+.. option:: -R <dataset@snapname>
+
+   Check that the creation of ZFS snapshots for `dataset` and all its children
+   will succeed:
+   
+     The existence if `dataset` will be prooved.
+
+     The snapshots may not exist already; this is prooved for `dataset`
+     and all its children.
+
+   Can be given more than once.
+
 .. option:: -k
 
    Does nothing.
--- a/docs/man/man8/ftjail-freebsd-update.rst	Sun Sep 29 12:18:02 2024 +0200
+++ b/docs/man/man8/ftjail-freebsd-update.rst	Sun Sep 29 16:04:59 2024 +0200
@@ -3,10 +3,13 @@
 ftjail-freebsd-update
 =====================
 
+.. program:: ftjail freebsd-update
+
+
 Synopsis
 --------
 
-**ftjail freebsd-update** [**-k**] [**-o** `old-origin`] `directory` `new-origin` [`etcupdate-tarball`]
+**ftjail freebsd-update** [**-k**] [**-o** `old-origin`] [[**-R** `dataset@snapname`]...] `directory` `new-origin` [`etcupdate-tarball`]
 
 
 Description
@@ -27,7 +30,14 @@
 Options
 -------
 
-.. program:: ftjail freebsd-update
+.. option:: -R <dataset@snapname>
+
+   Create ZFS snapshots for `dataset` and all its children before
+   updating.
+
+   The snapshots may not exist already.
+
+   Can be given more than once.
 
 .. option:: -k
 
--- 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
 }
 
 
--- a/share/examples/local-bsdtools/freebsd-update-ftjail.sh	Sun Sep 29 12:18:02 2024 +0200
+++ b/share/examples/local-bsdtools/freebsd-update-ftjail.sh	Sun Sep 29 16:04:59 2024 +0200
@@ -22,6 +22,8 @@
 
 BASE_DIR="/jail/TROOT"
 
+ftjail check-freebsd-update -R "${SNAPSHOT_ROOT}/${JAIL_NAME}@${SNAPSHOT_NAME}" -o "${OLD_BASE_RO}" "${BASE_DIR}/${JAIL_NAME}" "${NEW_BASE_RO}" "${NEW_ETCUPDATE}" || exit 1
+
 case "${JAIL_NAME}" in
   ftp)
     zfs umount dpool/data/ftp/home-data
@@ -34,9 +36,10 @@
     ;;
 esac
 
-zfs snapshot -r "${SNAPSHOT_ROOT}/${JAIL_NAME}@${SNAPSHOT_NAME}"
+# Is now requested with option -R
+#   zfs snapshot -r "${SNAPSHOT_ROOT}/${JAIL_NAME}@${SNAPSHOT_NAME}"
 
-ftjail freebsd-update -o "${OLD_BASE_RO}" "${BASE_DIR}/${JAIL_NAME}" "${NEW_BASE_RO}" "${NEW_ETCUPDATE}"
+ftjail freebsd-update -R "${SNAPSHOT_ROOT}/${JAIL_NAME}@${SNAPSHOT_NAME}" -o "${OLD_BASE_RO}" "${BASE_DIR}/${JAIL_NAME}" "${NEW_BASE_RO}" "${NEW_ETCUPDATE}"
 
 case "${JAIL_NAME}" in
   ftp)