comparison sbin/fzfs @ 500:7d498093d4c2

fzfs: Try to unmount filesystems that have been mounted by this tool when an error occurs
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 30 Aug 2024 18:50:12 +0200
parents 46406503a635
children 7d08fd78775c
comparison
equal deleted inserted replaced
499:562e630afb25 500:7d498093d4c2
41 ' 41 '
42 42
43 43
44 _p_datadir="$(dirname "$0")"/../share/local-bsdtools 44 _p_datadir="$(dirname "$0")"/../share/local-bsdtools
45 . "${_p_datadir}/common.subr" 45 . "${_p_datadir}/common.subr"
46 . "${_p_datadir}/array.sh"
46 47
47 48
48 # 49 #
49 #: Implementation of the "mount" command. 50 #: Implementation of the "mount" command.
50 #: 51 #:
121 _rootds_mountpoint_prefix="${_rootds_mountpoint}/" 122 _rootds_mountpoint_prefix="${_rootds_mountpoint}/"
122 fi 123 fi
123 124
124 zfs list -H -o name,mountpoint,canmount,mounted -s mountpoint -t filesystem -r "${_dsname}" \ 125 zfs list -H -o name,mountpoint,canmount,mounted -s mountpoint -t filesystem -r "${_dsname}" \
125 | { 126 | {
127
128 #
129 # _mounted_datasets is an array of ZFS datasets that have been
130 # mounted by this routine and should be unmounted on errors
131 # -- if possible.
132 #
133 array_create _mounted_datasets
134
126 while IFS=$'\t' read -r _name _mp _canmount _mounted ; do 135 while IFS=$'\t' read -r _name _mp _canmount _mounted ; do
127 # Skip filesystems that are already mounted 136 # Skip filesystems that are already mounted
128 [ "${_mounted}" = "yes" ] && continue 137 [ "${_mounted}" = "yes" ] && continue
129 # Skip filesystems that must not be mounted 138 # Skip filesystems that must not be mounted
130 [ "${_canmount}" = "off" ] && continue 139 [ "${_canmount}" = "off" ] && continue
149 # filesystem mount is just a single slash. 158 # filesystem mount is just a single slash.
150 # 159 #
151 if [ "${_mp}" = "${_rootds_mountpoint}" ]; then 160 if [ "${_mp}" = "${_rootds_mountpoint}" ]; then
152 if [ "${_name}" != "${_dsname}" ]; then 161 if [ "${_name}" != "${_dsname}" ]; then
153 echo "ERROR: child dataset mounts over root dataset" >&2 162 echo "ERROR: child dataset mounts over root dataset" >&2
163 _umount_datasets _mounted_datasets || true
154 return 1 164 return 1
155 fi 165 fi
156 _relative_mp="" 166 _relative_mp=""
157 _real_mp="${_mountpoint}" 167 _real_mp="${_mountpoint}"
158 else 168 else
159 _relative_mp="${_mp#${_rootds_mountpoint_prefix}}" 169 _relative_mp="${_mp#${_rootds_mountpoint_prefix}}"
160 # Eventually remove a trailing slash 170 # Eventually remove a trailing slash
161 _relative_mp="${_relative_mp%/}" 171 _relative_mp="${_relative_mp%/}"
162 if [ -z "${_relative_mp}" ]; then 172 if [ -z "${_relative_mp}" ]; then
163 echo "ERROR: got an empty relative mountpoint in child" >&2 173 echo "ERROR: got an empty relative mountpoint in child" >&2
174 _umount_datasets _mounted_datasets || true
164 return 1 175 return 1
165 fi 176 fi
166 # The real effective full mountpoint 177 # The real effective full mountpoint
167 _real_mp="${_mountpoint}/${_relative_mp}" 178 _real_mp="${_mountpoint}/${_relative_mp}"
168 fi 179 fi
173 # is given. 184 # is given.
174 # 185 #
175 if [ "${_opt_mount_natural}" = "yes" ]; then 186 if [ "${_opt_mount_natural}" = "yes" ]; then
176 if [ "${_real_mp}" != "${_mp}" ]; then 187 if [ "${_real_mp}" != "${_mp}" ]; then
177 echo "ERROR: mountpoint mismatch" >&2 188 echo "ERROR: mountpoint mismatch" >&2
189 _umount_datasets _mounted_datasets || true
178 return 1 190 return 1
179 fi 191 fi
180 fi 192 fi
181 193
182 if [ "${_opt_dry_run}" = "yes" ]; then 194 if [ "${_opt_dry_run}" = "yes" ]; then
183 echo "Would mount ${_name} on ${_real_mp}" 195 echo "Would mount ${_name} on ${_real_mp}"
184 else 196 else
185 mkdir -p "${_real_mp}" 1> /dev/null 2> /dev/null || \ 197 mkdir -p "${_real_mp}" 1> /dev/null 2> /dev/null || \
186 { echo "ERROR: cannot create mountpoint ${_real_mp}" >&2; return 1; } 198 { echo "ERROR: cannot create mountpoint ${_real_mp}" >&2; _umount_datasets _mounted_datasets || true; return 1; }
187 echo "Mounting ${_name} on ${_real_mp}" 199 echo "Mounting ${_name} on ${_real_mp}"
188 mount -t zfs "${_name}" "${_real_mp}" || return 1 200 if mount -t zfs "${_name}" "${_real_mp}"; then
201 array_append _mounted_datasets "${_name}"
202 else
203 _umount_datasets _mounted_datasets || true
204 return 1
205 fi
189 fi 206 fi
190 ;; 207 ;;
191 *) 208 *)
192 if [ "${_opt_mount_outside}" = "yes" ]; then 209 if [ "${_opt_mount_outside}" = "yes" ]; then
193 if [ "${_opt_dry_run}" = "yes" ]; then 210 if [ "${_opt_dry_run}" = "yes" ]; then
194 echo "Would mount ${_name} on configured ZFS dataset mountpoint ${_mp}" 211 echo "Would mount ${_name} on configured ZFS dataset mountpoint ${_mp}"
195 else 212 else
196 echo "Mounting ${_name} on configured ZFS dataset mountpoint ${_mp}" 213 echo "Mounting ${_name} on configured ZFS dataset mountpoint ${_mp}"
197 zfs mount "${_name}" || return 1 214 if zfs mount "${_name}"; then
215 array_append _mounted_datasets "${_name}"
216 else
217 _umount_datasets _mounted_datasets || true
218 return 1
219 fi
198 fi 220 fi
199 else 221 else
200 echo "Skipping ${_name} because its configured ZFS mountpoint is not relative to given root dataset" 2>&1 222 echo "Skipping ${_name} because its configured ZFS mountpoint is not relative to given root dataset" 2>&1
201 fi 223 fi
202 ;; 224 ;;
203 esac 225 esac
204 done 226 done
205 227 array_destroy _mounted_datasets
206 return 0 228 return 0
207 } 229 }
230 }
231
232
233 #:
234 #: Helper to unmount mounted ZFS filesystems
235 #:
236 #: Args:
237 #: $1 (str): The name of the array that contains the datasets to unmount to
238 #:
239 #: Returns:
240 #: 0 if successfully unmounted all given datasets,
241 #: 1 otherwise
242 #:
243 #: Unmounting is done in the reverse order.
244 #:
245 _umount_datasets() {
246 array_reversed_for_each "$1" _umount_datasets_umount
247 }
248
249
250 #:
251 #: Array callback to unmount a single ZFS filesystem
252 #:
253 _umount_datasets_umount() {
254 if ! zfs umount "$3" ; then
255 warn "Dataset \`${3}' cannot unmounted"
256 return 1
257 fi
258 return 0
208 } 259 }
209 260
210 261
211 #: 262 #:
212 #: Implement the "umount" command. 263 #: Implement the "umount" command.