comparison 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
comparison
equal deleted inserted replaced
659:25de0d27e407 660:82a98db83a13
43 43
44 copy-skel [-A] [-D] [-L] [-M MOUNTPOINT] [-P] [-u] SOURCE-DS SNAPSHOT-NAME TARGET-DS 44 copy-skel [-A] [-D] [-L] [-M MOUNTPOINT] [-P] [-u] SOURCE-DS SNAPSHOT-NAME TARGET-DS
45 45
46 build-etcupdate-current-tmpl DIRECTORY TARBALL 46 build-etcupdate-current-tmpl DIRECTORY TARBALL
47 47
48 freebsd-update [-k] [-o OLD-ORIGIN] DIRECTORY NEW-ORIGIN [ETCUPDATE-TARBALL] 48 check-freebsd-update [-k] [-o OLD-ORIGIN] [-R SNAPSHOT] DIRECTORY NEW-ORIGIN [ETCUPDATE-TARBALL]
49
50 freebsd-update [-k] [-o OLD-ORIGIN] [-R SNAPSHOT] DIRECTORY NEW-ORIGIN [ETCUPDATE-TARBALL]
49 51
50 ENVIRONMENT: 52 ENVIRONMENT:
51 53
52 All environment variables that affect "zfs" are effective also. 54 All environment variables that affect "zfs" are effective also.
53 55
899 #: 901 #:
900 #: Implement the "check-freebsd-update" command for a thin jail 902 #: Implement the "check-freebsd-update" command for a thin jail
901 #: 903 #:
902 command_check_freebsd_update() { 904 command_check_freebsd_update() {
903 local _directory _new_origin _etcupdate_tarball 905 local _directory _new_origin _etcupdate_tarball
904 local _opt_keep _opt_old_origin 906 local _opt_keep _opt_old_origin _opt_snapshots
905 907
906 local _errors _warnings _rc 908 local _errors _warnings _rc
907 local _directory _new_origin _etcupdate_tarball 909 local _directory _new_origin _etcupdate_tarball
908 local _dir_basename _dir_mounts _jailname _tmp _line _log_sock 910 local _dir_basename _dir_mounts _jailname _tmp _line _log_sock
909 local _root_dataset _root_mountpoint _root_type _root_options 911 local _root_dataset _root_mountpoint _root_type _root_options
910 local _mnt_device _mnt_mountpoint _mnt_type _mnt_options 912 local _mnt_device _mnt_mountpoint _mnt_type _mnt_options
913 local _idx _sn_ds _sn_name _sn_ds_related
911 914
912 _rc=0 915 _rc=0
913 916
914 _warnings='' 917 _warnings=''
915 farray_create _warnings XXX 918 farray_create _warnings
916 _errors='' 919 _errors=''
917 farray_create _errors 920 farray_create _errors
918 921
922 _opt_snapshots=''
923 falist_create _opt_snapshots
919 _opt_keep="no" 924 _opt_keep="no"
920 _opt_old_origin="" 925 _opt_old_origin=""
921 while getopts "ko:" _opt ; do 926 while getopts "R:ko:" _opt ; do
922 case "${_opt}" in 927 case "${_opt}" in
928 R)
929 case "${OPTARG}" in
930 *?\@?*)
931 #
932 # Split in two parts: dataset hierarchy and the name of the
933 # snapshot
934 #
935 falist_set _opt_snapshots "${OPTARG%%@*}" "${OPTARG#*@}"
936 ;;
937 *)
938 farray_append _errors "argument \`${OPTARG}' is not a snapshot name"
939 ;;
940 esac
941 ;;
923 k) 942 k)
924 _opt_keep="yes" 943 _opt_keep="yes"
925 ;; 944 ;;
926 o) 945 o)
927 _opt_old_origin="$OPTARG" 946 _opt_old_origin="${OPTARG}"
928 ;; 947 ;;
929 \?|:) 948 \?|:)
930 return 2; 949 return 2;
931 ;; 950 ;;
932 esac 951 esac
949 zfs list -H -o name -t snapshot "${_new_origin}" >/dev/null 2>/dev/null || farray_append _errors "ZFS dataset snapshot for the new origin \`${_new_origin}' does not exist" 968 zfs list -H -o name -t snapshot "${_new_origin}" >/dev/null 2>/dev/null || farray_append _errors "ZFS dataset snapshot for the new origin \`${_new_origin}' does not exist"
950 fi 969 fi
951 if [ -n "${_etcupdate_tarball}" ]; then 970 if [ -n "${_etcupdate_tarball}" ]; then
952 [ -r "${_etcupdate_tarball}" ] || farray_append _errors "given etcupdate tarball does not exist and/or is not readable" 971 [ -r "${_etcupdate_tarball}" ] || farray_append _errors "given etcupdate tarball does not exist and/or is not readable"
953 fi 972 fi
973
974 # Check snapshotting
975 _idx=1
976 while falist_tryget_item_at_index _sn_ds _sn_name _opt_snapshots ${_idx}; do
977 if zfs get -H -o value name "${_sn_ds}" >/dev/null 2>/dev/null; then
978 # yes dataset exists: check that snapshots do not exist
979 while IFS=$'\t' read -r _line; do
980 if zfs get -H -o value name "${_line}@${_sn_name}" >/dev/null 2>/dev/null; then
981 farray_append _errors "snapshot \`${_line}@${_sn_name}' already exists"
982 fi
983 done <<EOF2988ee715b2d93fd93bdce23
984 $(zfs list -H -r -o name "${_sn_ds}")
985 EOF2988ee715b2d93fd93bdce23
986 else
987 farray_append _errors "dataset for snapshots \`${_sn_ds}' does not exist"
988 fi
989 _idx=$((_idx + 1))
990 done
954 991
955 if [ -n "${_directory}" ]; then 992 if [ -n "${_directory}" ]; then
956 993
957 _dir_basename="$(basename "${_directory}")" 994 _dir_basename="$(basename "${_directory}")"
958 995
1015 [ "${_opt_old_origin}" != "${_root_origin}" ] && farray_append _errors "origin mismatch" 1052 [ "${_opt_old_origin}" != "${_root_origin}" ] && farray_append _errors "origin mismatch"
1016 else 1053 else
1017 [ "${_root_origin}" = '-' ] && farray_append _errors "the root dataset is not a ZFS clone" 1054 [ "${_root_origin}" = '-' ] && farray_append _errors "the root dataset is not a ZFS clone"
1018 fi 1055 fi
1019 # 1056 #
1020 # Check for open files on all the mounted filesystems 1057 # Check for open files on all the mounted filesystems.
1058 # If snapshots are requested check that they are related somehow to
1059 # mounted filesystems.
1021 # 1060 #
1061 _sn_ds_related=''
1022 while IFS=$'\t' read -r _mnt_device _mnt_mountpoint _mnt_type _mnt_options _line; do 1062 while IFS=$'\t' read -r _mnt_device _mnt_mountpoint _mnt_type _mnt_options _line; do
1023 if ! _check_no_open_files_on_filesystem "${_mnt_mountpoint}" ; then 1063 if ! _check_no_open_files_on_filesystem "${_mnt_mountpoint}" ; then
1024 farray_append _errors "There are open files or memory mapping on file system \`${_mnt_mountpoint}'" 1064 farray_append _errors "There are open files or memory mapping on file system \`${_mnt_mountpoint}'"
1025 fi 1065 fi
1066 _idx=1
1067 while falist_tryget_key_at_index _sn_ds _opt_snapshots ${_idx}; do
1068 case "${_mnt_device}" in
1069 "${_sn_ds}")
1070 _sn_ds_related="yes"
1071 ;;
1072 "${_sn_ds}"/*)
1073 _sn_ds_related="yes"
1074 ;;
1075 *)
1076 ;;
1077 esac
1078 _idx=$((_idx + 1))
1079 done
1026 done <<EOF4tHGCAASL775f9f320205 1080 done <<EOF4tHGCAASL775f9f320205
1027 ${_dir_mounts} 1081 ${_dir_mounts}
1028 EOF4tHGCAASL775f9f320205 1082 EOF4tHGCAASL775f9f320205
1029 fi 1083 fi
1084 if falist_istrue _opt_snapshots; then
1085 if ! checkyes _sn_ds_related; then
1086 farray_append _warnings "snapshot datasets and mounted datasets are not related"
1087 fi
1088 fi
1030 1089
1031 if farray_istrue _errors; then 1090 if farray_istrue _errors; then
1032 _print_check_errors _errors 1091 _print_check_errors _errors
1033 _rc=1 1092 _rc=1
1034 fi 1093 fi
1035 # Warnings do not influence the return code 1094 # Warnings do not influence the return code
1036 _print_check_warnings _warnings 1095 _print_check_warnings _warnings
1037 1096
1038 farray_destroy _errors 1097 farray_destroy _errors
1039 farray_destroy _warnings 1098 farray_destroy _warnings
1099 falist_destroy _opt_snapshots
1040 return ${_rc} 1100 return ${_rc}
1041 } 1101 }
1042 1102
1043 1103
1044 #: 1104 #:
1047 #: .. note:: FreeBSD's :command:`etcupdate` also executes 1107 #: .. note:: FreeBSD's :command:`etcupdate` also executes
1048 #: :command:`certctl rehash` if certs are to be added or removed! 1108 #: :command:`certctl rehash` if certs are to be added or removed!
1049 #: 1109 #:
1050 command_freebsd_update() { 1110 command_freebsd_update() {
1051 local _directory _new_origin _etcupdate_tarball 1111 local _directory _new_origin _etcupdate_tarball
1052 local _opt_keep _opt_old_origin 1112 local _opt_keep _opt_old_origin _opt_snapshots
1053 1113
1054 local _res _jailname _dir_mounts _dir_fn_fstab _dir_fn_fstab2 1114 local _res _jailname _dir_mounts _dir_fn_fstab _dir_fn_fstab2
1055 local _dir_basename _dir_fn_tldir 1115 local _dir_basename _dir_fn_tldir
1056 local _root_dataset _root_mountpoint _root_type _root_options 1116 local _root_dataset _root_mountpoint _root_type _root_options
1057 local _mnt_device _mnt_mountpoint _mnt_type _mnt_options 1117 local _mnt_device _mnt_mountpoint _mnt_type _mnt_options
1118 local _idx _sn_ds _sn_name
1058 local _clone_extra_props _canmount_prop 1119 local _clone_extra_props _canmount_prop
1059 local _line _opt 1120 local _line _opt
1060 local _root_readonly _root_origin 1121 local _root_readonly _root_origin
1061 local _u_tmpdir 1122 local _u_tmpdir
1062 local _add_log_sock 1123 local _add_log_sock
1063 1124
1125 _opt_snapshots=''
1126 falist_create _opt_snapshots
1064 _opt_keep="no" 1127 _opt_keep="no"
1065 _opt_old_origin="" 1128 _opt_old_origin=""
1066 while getopts "ko:" _opt ; do 1129 while getopts "R:ko:" _opt ; do
1067 case "${_opt}" in 1130 case "${_opt}" in
1131 R)
1132 case "${OPTARG}" in
1133 *?\@?*)
1134 #
1135 # Split in two parts: dataset hierarchy and the name of the
1136 # snapshot
1137 #
1138 falist_set _opt_snapshots "${OPTARG%%@*}" "${OPTARG#*@}"
1139 ;;
1140 *)
1141 err "argument \`${OPTARG}' is not a snapshot name"
1142 return 1
1143 ;;
1144 esac
1145 ;;
1068 k) 1146 k)
1069 _opt_keep="yes" 1147 _opt_keep="yes"
1070 ;; 1148 ;;
1071 o) 1149 o)
1072 _opt_old_origin="$OPTARG" 1150 _opt_old_origin="${OPTARG}"
1073 ;; 1151 ;;
1074 \?|:) 1152 \?|:)
1075 return 2; 1153 return 2;
1076 ;; 1154 ;;
1077 esac 1155 esac
1089 [ -z "${_new_origin}" ] && { echo "ERROR: no new origin given" 1>&2; return 2; } 1167 [ -z "${_new_origin}" ] && { echo "ERROR: no new origin given" 1>&2; return 2; }
1090 zfs list -H -o name -t snapshot "${_new_origin}" >/dev/null 2>/dev/null || { echo "ERROR: ZFS dataset snapshot for the new origin \`${_new_origin}' does not exist" 1>&2; return 1; } 1168 zfs list -H -o name -t snapshot "${_new_origin}" >/dev/null 2>/dev/null || { echo "ERROR: ZFS dataset snapshot for the new origin \`${_new_origin}' does not exist" 1>&2; return 1; }
1091 if [ -n "${_etcupdate_tarball}" ]; then 1169 if [ -n "${_etcupdate_tarball}" ]; then
1092 [ -r "${_etcupdate_tarball}" ] || { echo "ERROR: given etcupdate tarball does not exist and/or is not readable" 1>&2; return 1; } 1170 [ -r "${_etcupdate_tarball}" ] || { echo "ERROR: given etcupdate tarball does not exist and/or is not readable" 1>&2; return 1; }
1093 fi 1171 fi
1172
1173 # Check snapshotting
1174 _idx=1
1175 while falist_tryget_item_at_index _sn_ds _sn_name _opt_snapshots ${_idx}; do
1176 if zfs get -H -o value name "${_sn_ds}" >/dev/null 2>/dev/null; then
1177 # yes dataset exists: check that snapshots do not exist
1178 while IFS=$'\t' read -r _line; do
1179 if zfs get -H -o value name "${_line}@${_sn_name}" >/dev/null 2>/dev/null; then
1180 err "snapshot \`${_line}@${_sn_name}' already exists"
1181 return 1
1182 fi
1183 done <<EOF2988ee715b2d93fd93bdce23
1184 $(zfs list -H -r -o name "${_sn_ds}")
1185 EOF2988ee715b2d93fd93bdce23
1186 else
1187 err "dataset for snapshots \`${_sn_ds}' does not exist"
1188 return 1
1189 fi
1190 _idx=$((_idx + 1))
1191 done
1094 1192
1095 _dir_basename="$(basename "${_directory}")" 1193 _dir_basename="$(basename "${_directory}")"
1096 1194
1097 set +e 1195 set +e
1098 _jailname="$(_get_jail_from_path "${_directory}")" 1196 _jailname="$(_get_jail_from_path "${_directory}")"
1189 # Replace all spaces with a sequence that is understood by mount 1287 # Replace all spaces with a sequence that is understood by mount
1190 LC_ALL=C /usr/bin/sed -e 's/ /\\040/g' <"${_dir_fn_fstab}" >"${_dir_fn_fstab2}" 1288 LC_ALL=C /usr/bin/sed -e 's/ /\\040/g' <"${_dir_fn_fstab}" >"${_dir_fn_fstab2}"
1191 _dir_fn_tldir="${_u_tmpdir}/tldirs" 1289 _dir_fn_tldir="${_u_tmpdir}/tldirs"
1192 LC_ALL=C /usr/bin/find "${_directory}" -depth 1 -type d 2>/dev/null | LC_ALL=C /usr/bin/sort >>"${_dir_fn_tldir}" 1290 LC_ALL=C /usr/bin/find "${_directory}" -depth 1 -type d 2>/dev/null | LC_ALL=C /usr/bin/sort >>"${_dir_fn_tldir}"
1193 1291
1292 _idx=1
1293 while falist_tryget_item_at_index _sn_ds _sn_name _opt_snapshots ${_idx}; do
1294 echo "Creating snapshot \`${_sn_ds}@${_sn_name}'"
1295 zfs snapshot -r "${_sn_ds}@${_sn_name}" || { err "cannot snapshot \`${_sn_ds}@${_sn_name}'"; return 1; }
1296 _idx=$((_idx + 1))
1297 done
1298
1194 # Unmount in reverse order: unmount can do it for us 1299 # Unmount in reverse order: unmount can do it for us
1195 echo "Unmounting all datasets mounted at \`${_directory}'" 1300 echo "Unmounting all datasets mounted at \`${_directory}'"
1196 /sbin/umount -a -F "${_dir_fn_fstab2}" -v 1301 /sbin/umount -a -F "${_dir_fn_fstab2}" -v
1197 1302
1198 # 1303 #
1275 if [ "${_opt_keep}" != "yes" ]; then 1380 if [ "${_opt_keep}" != "yes" ]; then
1276 echo "Cleaning up...""" 1381 echo "Cleaning up..."""
1277 [ -n "${_u_tmpdir}" ] && [ -d "${_u_tmpdir}" ] && rm -rvf "${_u_tmpdir}" 1382 [ -n "${_u_tmpdir}" ] && [ -d "${_u_tmpdir}" ] && rm -rvf "${_u_tmpdir}"
1278 fi 1383 fi
1279 echo "Done." 1384 echo "Done."
1385
1386 falist_destroy _opt_snapshots
1280 } 1387 }
1281 1388
1282 1389
1283 # 1390 #
1284 # Global option handling 1391 # Global option handling