comparison sbin/fzfs @ 521:c05ef1c86c9c

fzfs: Implement option "-k" for clone-tree and mount to keep cloned datasets or keep datasets mounted on errors
author Franz Glasner <fzglas.hg@dom66.de>
date Sun, 01 Sep 2024 21:52:01 +0200
parents 7d08fd78775c
children b7d60802b25f
comparison
equal deleted inserted replaced
520:7d08fd78775c 521:c05ef1c86c9c
26 26
27 -h Print this help message to stdout and exit 27 -h Print this help message to stdout and exit
28 28
29 COMMANDS: 29 COMMANDS:
30 30
31 clone-tree [-n] SOUECE-DATASET DEST-DATASET 31 clone-tree [-k] [-n] SOUECE-DATASET DEST-DATASET
32 32
33 copy-tree [-A] [-M MOUNTPOINT] [-n] [-u] SOURCE-DATASET DEST-DATASET 33 copy-tree [-A] [-M MOUNTPOINT] [-n] [-u] SOURCE-DATASET DEST-DATASET
34 34
35 create-tree [-A] [-M MOUNTPOINT] [-n] [-p] [-u] SOURCE-DATASET DEST-DATASET 35 create-tree [-A] [-M MOUNTPOINT] [-n] [-p] [-u] SOURCE-DATASET DEST-DATASET
36 36
37 mount [-O] [-N] [-P] [-u] [-n] DATASET [MOUNTPOINT] 37 mount [-O] [-N] [-P] [-k] [-u] [-n] DATASET [MOUNTPOINT]
38 38
39 umount DATASET 39 umount DATASET
40 40
41 unmount 41 unmount
42 42
54 #: Mount a dataset and recursively all its children datasets. 54 #: Mount a dataset and recursively all its children datasets.
55 #: 55 #:
56 command_mount() { 56 command_mount() {
57 local _dsname _mountpoint 57 local _dsname _mountpoint
58 local _opt_dry_run _opt_mount_outside _opt_mount_natural 58 local _opt_dry_run _opt_mount_outside _opt_mount_natural
59 local _opt_mount_children_only 59 local _opt_mount_children_only _opt_keep
60 60
61 local _name _mp _canmount _mounted _rootds_mountpoint _rootds_mountpoint_prefix _relative_mp _real_mp 61 local _name _mp _canmount _mounted _rootds_mountpoint _rootds_mountpoint_prefix _relative_mp _real_mp
62 62
63 _opt_dry_run="" 63 _opt_dry_run=""
64 _opt_keep=""
64 _opt_mount_outside="" 65 _opt_mount_outside=""
65 _opt_mount_natural="" 66 _opt_mount_natural=""
66 _opt_mount_children_only="" 67 _opt_mount_children_only=""
67 while getopts "ONPnu" _opt ; do 68 while getopts "ONPknu" _opt ; do
68 case ${_opt} in 69 case ${_opt} in
69 O) 70 O)
70 _opt_mount_outside="yes" 71 _opt_mount_outside="yes"
71 ;; 72 ;;
72 N) 73 N)
73 _opt_mount_natural="yes" 74 _opt_mount_natural="yes"
74 ;; 75 ;;
75 P) 76 P)
76 _opt_mount_children_only="yes" 77 _opt_mount_children_only="yes"
78 ;;
79 k)
80 _opt_keep="yes"
77 ;; 81 ;;
78 n|u) 82 n|u)
79 _opt_dry_run="yes" 83 _opt_dry_run="yes"
80 ;; 84 ;;
81 \?|:) 85 \?|:)
160 # filesystem mount is just a single slash. 164 # filesystem mount is just a single slash.
161 # 165 #
162 if [ "${_mp}" = "${_rootds_mountpoint}" ]; then 166 if [ "${_mp}" = "${_rootds_mountpoint}" ]; then
163 if [ "${_name}" != "${_dsname}" ]; then 167 if [ "${_name}" != "${_dsname}" ]; then
164 echo "ERROR: child dataset mounts over root dataset" >&2 168 echo "ERROR: child dataset mounts over root dataset" >&2
165 _umount_datasets _mounted_datasets || true 169 if ! checkyes _opt_keep; then
170 _umount_datasets _mounted_datasets || true
171 fi
166 return 1 172 return 1
167 fi 173 fi
168 _relative_mp="" 174 _relative_mp=""
169 _real_mp="${_mountpoint}" 175 _real_mp="${_mountpoint}"
170 else 176 else
171 _relative_mp="${_mp#${_rootds_mountpoint_prefix}}" 177 _relative_mp="${_mp#${_rootds_mountpoint_prefix}}"
172 # Eventually remove a trailing slash 178 # Eventually remove a trailing slash
173 _relative_mp="${_relative_mp%/}" 179 _relative_mp="${_relative_mp%/}"
174 if [ -z "${_relative_mp}" ]; then 180 if [ -z "${_relative_mp}" ]; then
175 echo "ERROR: got an empty relative mountpoint in child" >&2 181 echo "ERROR: got an empty relative mountpoint in child" >&2
176 _umount_datasets _mounted_datasets || true 182 if ! checkyes _opt_keep; then
183 _umount_datasets _mounted_datasets || true
184 fi
177 return 1 185 return 1
178 fi 186 fi
179 # The real effective full mountpoint 187 # The real effective full mountpoint
180 _real_mp="${_mountpoint}/${_relative_mp}" 188 _real_mp="${_mountpoint}/${_relative_mp}"
181 fi 189 fi
186 # is given. 194 # is given.
187 # 195 #
188 if [ "${_opt_mount_natural}" = "yes" ]; then 196 if [ "${_opt_mount_natural}" = "yes" ]; then
189 if [ "${_real_mp}" != "${_mp}" ]; then 197 if [ "${_real_mp}" != "${_mp}" ]; then
190 echo "ERROR: mountpoint mismatch" >&2 198 echo "ERROR: mountpoint mismatch" >&2
191 _umount_datasets _mounted_datasets || true 199 if ! checkyes _opt_keep; then
200 _umount_datasets _mounted_datasets || true
201 fi
192 return 1 202 return 1
193 fi 203 fi
194 fi 204 fi
195 205
196 if [ "${_opt_dry_run}" = "yes" ]; then 206 if [ "${_opt_dry_run}" = "yes" ]; then
200 { echo "ERROR: cannot create mountpoint ${_real_mp}" >&2; _umount_datasets _mounted_datasets || true; return 1; } 210 { echo "ERROR: cannot create mountpoint ${_real_mp}" >&2; _umount_datasets _mounted_datasets || true; return 1; }
201 echo "Mounting ${_name} on ${_real_mp}" 211 echo "Mounting ${_name} on ${_real_mp}"
202 if mount -t zfs "${_name}" "${_real_mp}"; then 212 if mount -t zfs "${_name}" "${_real_mp}"; then
203 array_append _mounted_datasets "${_name}" 213 array_append _mounted_datasets "${_name}"
204 else 214 else
205 _umount_datasets _mounted_datasets || true 215 if ! checkyes _opt_keep; then
216 _umount_datasets _mounted_datasets || true
217 fi
206 return 1 218 return 1
207 fi 219 fi
208 fi 220 fi
209 ;; 221 ;;
210 *) 222 *)
214 else 226 else
215 echo "Mounting ${_name} on configured ZFS dataset mountpoint ${_mp}" 227 echo "Mounting ${_name} on configured ZFS dataset mountpoint ${_mp}"
216 if zfs mount "${_name}"; then 228 if zfs mount "${_name}"; then
217 array_append _mounted_datasets "${_name}" 229 array_append _mounted_datasets "${_name}"
218 else 230 else
219 _umount_datasets _mounted_datasets || true 231 if ! checkyes _opt_keep; then
232 _umount_datasets _mounted_datasets || true
233 fi
220 return 1 234 return 1
221 fi 235 fi
222 fi 236 fi
223 else 237 else
224 echo "Skipping ${_name} because its configured ZFS mountpoint is not relative to given root dataset" 2>&1 238 echo "Skipping ${_name} because its configured ZFS mountpoint is not relative to given root dataset" 2>&1
434 #: 448 #:
435 #: Implementation of "clone-tree" 449 #: Implementation of "clone-tree"
436 #: 450 #:
437 command_clone_tree() { 451 command_clone_tree() {
438 local _ds_source _ds_dest 452 local _ds_source _ds_dest
439 local _opt_dry_run 453 local _opt_keep _opt_dry_run
440 454
441 local _ds snapshot_name _ds_source_base _ds_relname 455 local _ds snapshot_name _ds_source_base _ds_relname
442 local _ds_canmount _ds_mountpoint 456 local _ds_canmount _ds_mountpoint
443 local _clone_props _arg_canmount _arg_other_clone_props 457 local _clone_props _arg_canmount _arg_other_clone_props
444 local _opt _idx _idx_lp _prop _propval 458 local _opt _idx _idx_lp _prop _propval
445 459
446 _opt_dry_run="" 460 _opt_dry_run=""
447 461 _opt_keep=""
448 while getopts "n" _opt ; do 462
463 while getopts "kn" _opt ; do
449 case ${_opt} in 464 case ${_opt} in
465 k)
466 _opt_keep="yes"
467 ;;
450 n) 468 n)
451 _opt_dry_run="yes" 469 _opt_dry_run="yes"
452 ;; 470 ;;
453 \?|:) 471 \?|:)
454 return 2; 472 return 2;
491 509
492 # Check the existence of all intermediate datasets and their shapshots 510 # Check the existence of all intermediate datasets and their shapshots
493 _idx=1 511 _idx=1
494 while array_tryget _ds _ds_tree ${_idx}; do 512 while array_tryget _ds _ds_tree ${_idx}; do
495 if ! zfs get -H name "${_ds}@${_snapshot_name}" >/dev/null 2>&1; then 513 if ! zfs get -H name "${_ds}@${_snapshot_name}" >/dev/null 2>&1; then
496 err "child dataset does not exist: ${_ds}@${_snapshot_name}" 1>&2 514 err "child dataset (snapshot) does not exist: ${_ds}@${_snapshot_name}" 1>&2
497 array_destroy _ds_tree
498 return 1 515 return 1
499 fi 516 fi
500 _idx=$((${_idx} + 1)) 517 _idx=$((${_idx} + 1))
501 done 518 done
502 519
554 if ! checkyes _opt_dry_run; then 571 if ! checkyes _opt_dry_run; then
555 echo "Cloning ${_ds}@${_snapshot_name} into ${_ds_dest}${_ds_relname} with ${_arg_canmount} ${_arg_other_clone_props}" 572 echo "Cloning ${_ds}@${_snapshot_name} into ${_ds_dest}${_ds_relname} with ${_arg_canmount} ${_arg_other_clone_props}"
556 if zfs clone ${_arg_canmount} ${_arg_other_clone_props} "${_ds}@${_snapshot_name}" "${_ds_dest}${_ds_relname}"; then 573 if zfs clone ${_arg_canmount} ${_arg_other_clone_props} "${_ds}@${_snapshot_name}" "${_ds_dest}${_ds_relname}"; then
557 array_append _cloned_datasets "${_ds_dest}${_ds_relname}" 574 array_append _cloned_datasets "${_ds_dest}${_ds_relname}"
558 else 575 else
559 _destroy_datasets _cloned_datasets || true 576 if ! checkyes _opt_keep; then
577 _destroy_datasets _cloned_datasets || true
578 fi
560 return 1 579 return 1
561 fi 580 fi
562 else 581 else
563 echo "Would execute: zfs clone ${_arg_canmount} ${_arg_other_clone_props} '${_ds}@${_snapshot_name}' '${_ds_dest}${_ds_relname}'" 582 echo "Would execute: zfs clone ${_arg_canmount} ${_arg_other_clone_props} '${_ds}@${_snapshot_name}' '${_ds_dest}${_ds_relname}'"
564 fi 583 fi
608 fi 627 fi
609 fi 628 fi
610 fi 629 fi
611 _idx=$((${_idx} + 1)) 630 _idx=$((${_idx} + 1))
612 done 631 done
613 632 array_destroy _cloned_datasets
614 return 0 633 return 0
615 } 634 }
616 635
617 636
618 #: 637 #: