Mercurial > hgrepos > FreeBSD > ports > sysutils > local-bsdtools
comparison sbin/fzfs @ 276:3c24b07240f2
Move the implementation of "mount" and "umount" into the new tool fzfs.
It is not jail-specific but in reality a helper for some ZFS management
issues.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sat, 17 Sep 2022 16:47:32 +0200 |
| parents | |
| children | 1fc3b04b39fa |
comparison
equal
deleted
inserted
replaced
| 275:5bb4c4044e48 | 276:3c24b07240f2 |
|---|---|
| 1 #!/bin/sh | |
| 2 # -*- indent-tabs-mode: nil; -*- | |
| 3 #: | |
| 4 #: A ZFS management helper tool. | |
| 5 #: | |
| 6 #: :Author: Franz Glasner | |
| 7 #: :Copyright: (c) 2022 Franz Glasner. | |
| 8 #: All rights reserved. | |
| 9 #: :License: BSD 3-Clause "New" or "Revised" License. | |
| 10 #: See LICENSE for details. | |
| 11 #: If you cannot find LICENSE see | |
| 12 #: <https://opensource.org/licenses/BSD-3-Clause> | |
| 13 #: :ID: @(#)@@PKGORIGIN@@ $HGid$ | |
| 14 #: | |
| 15 | |
| 16 set -eu | |
| 17 | |
| 18 VERSION="@@VERSION@@" | |
| 19 | |
| 20 USAGE=' | |
| 21 USAGE: fzfs [ OPTIONS ] COMMAND [ COMMAND OPTIONS ] [ ARG ... ] | |
| 22 | |
| 23 OPTIONS: | |
| 24 | |
| 25 -V Print the program name and version number to stdout and exit | |
| 26 | |
| 27 -h Print this help message to stdout and exit | |
| 28 | |
| 29 COMMANDS: | |
| 30 | |
| 31 mount [-O] [-u] [-n] DATASET [MOUNTPOINT] | |
| 32 | |
| 33 Mount the ZFS dataset DATASET and all its children to mountpoint | |
| 34 MOUNTPOINT | |
| 35 | |
| 36 -O Also mount datasets at mountpoints outside of their "natural" | |
| 37 and inherited mountpoints | |
| 38 -N Mount at their "natural" configured ZFS mountpoints | |
| 39 (MOUNTPOINT is not required) | |
| 40 -P Do not mount the given parent DATASET but only its children | |
| 41 -n Do not really mount but show what would be mounted where | |
| 42 -u Alias of -n | |
| 43 | |
| 44 umount DATASET | |
| 45 | |
| 46 Unmount the mounted DATASET and all its children | |
| 47 | |
| 48 unmount | |
| 49 | |
| 50 Alias for `umount'"'"' | |
| 51 | |
| 52 ' | |
| 53 | |
| 54 # | |
| 55 #: Implementation of the "mount" command. | |
| 56 #: | |
| 57 #: Mount a dataset and recursively all its children datasets. | |
| 58 #: | |
| 59 command_mount() { | |
| 60 local _dsname _mountpoint | |
| 61 local _opt_dry_run _opt_mount_outside _opt_mount_natural | |
| 62 local _opt_mount_children_only | |
| 63 | |
| 64 local _name _mp _canmount _mounted _rootds_mountpoint _relative_mp _real_mp | |
| 65 | |
| 66 _opt_dry_run="" | |
| 67 _opt_mount_outside="" | |
| 68 _opt_mount_natural="" | |
| 69 _opt_mount_children_only="" | |
| 70 while getopts "ONPnu" _opt ; do | |
| 71 case ${_opt} in | |
| 72 O) | |
| 73 _opt_mount_outside="yes" | |
| 74 ;; | |
| 75 N) | |
| 76 _opt_mount_natural="yes" | |
| 77 ;; | |
| 78 P) | |
| 79 _opt_mount_children_only="yes" | |
| 80 ;; | |
| 81 n|u) | |
| 82 _opt_dry_run="yes" | |
| 83 ;; | |
| 84 \?|:) | |
| 85 return 2; | |
| 86 ;; | |
| 87 esac | |
| 88 done | |
| 89 shift $((OPTIND-1)) | |
| 90 OPTIND=1 | |
| 91 | |
| 92 _dsname="${1-}" | |
| 93 _mountpoint="${2-}" | |
| 94 | |
| 95 if [ -z "${_dsname}" ]; then | |
| 96 echo "ERROR: no dataset given" >&2 | |
| 97 return 2 | |
| 98 fi | |
| 99 | |
| 100 _rootds_mountpoint="$(zfs list -H -o mountpoint -t filesystem "${_dsname}")" || \ | |
| 101 { echo "ERROR: root dataset does not exist" >&2; return 1; } | |
| 102 | |
| 103 if [ -z "${_mountpoint}" ]; then | |
| 104 if [ "${_opt_mount_natural}" = "yes" ]; then | |
| 105 _mountpoint="${_rootds_mountpoint}" | |
| 106 else | |
| 107 echo "ERROR: no mountpoint given" >&2 | |
| 108 return 2 | |
| 109 fi | |
| 110 else | |
| 111 if [ "${_opt_mount_natural}" = "yes" ]; then | |
| 112 echo "ERROR: Cannot have a custom mountpoint when \"-O\" is given" >&2 | |
| 113 return 2 | |
| 114 fi | |
| 115 fi | |
| 116 | |
| 117 # Eventually remove a trailing slash | |
| 118 _mountpoint="${_mountpoint%/}" | |
| 119 if [ -z "${_mountpoint}" ]; then | |
| 120 echo "ERROR: would mount over the root filesystem" >&2 | |
| 121 return 1 | |
| 122 fi | |
| 123 | |
| 124 zfs list -H -o name,mountpoint,canmount,mounted -s mountpoint -t filesystem -r "${_dsname}" \ | |
| 125 | { | |
| 126 while IFS=$'\t' read -r _name _mp _canmount _mounted ; do | |
| 127 # Skip filesystems that are already mounted | |
| 128 [ "${_mounted}" = "yes" ] && continue | |
| 129 # Skip filesystems that must not be mounted | |
| 130 [ "${_canmount}" = "off" ] && continue | |
| 131 # | |
| 132 # Mount only the children and skip the given parent dataset | |
| 133 # if required | |
| 134 # | |
| 135 [ \( "${_opt_mount_children_only}" = "yes" \) -a \( "${_name}" = "${_dsname}" \) ] && continue | |
| 136 case "${_mp}" in | |
| 137 "none"|"legacy") | |
| 138 # Do nothing for filesystem with unset or legacy mountpoints | |
| 139 ;; | |
| 140 "${_rootds_mountpoint}"|"${_rootds_mountpoint}/"*) | |
| 141 # | |
| 142 # Handle only mountpoints that have a mountpoint below | |
| 143 # the parent datasets mountpoint | |
| 144 # | |
| 145 | |
| 146 # Determine the mountpoint relative to the parent mountpoint | |
| 147 _relative_mp="${_mp#${_rootds_mountpoint}}" | |
| 148 # Eventually remove a trailing slash | |
| 149 _relative_mp="${_relative_mp%/}" | |
| 150 # The real effective full mountpoint | |
| 151 _real_mp="${_mountpoint}${_relative_mp}" | |
| 152 | |
| 153 # | |
| 154 # Consistency and sanity check: computed real mountpoint must | |
| 155 # be equal to the configured mountpoint when no custom mountpoint | |
| 156 # is given. | |
| 157 # | |
| 158 if [ "${_opt_mount_natural}" = "yes" ]; then | |
| 159 if [ "${_real_mp}" != "${_mp}" ]; then | |
| 160 echo "ERROR: mountpoint mismatch" >&2 | |
| 161 return 1 | |
| 162 fi | |
| 163 fi | |
| 164 | |
| 165 if [ "${_opt_dry_run}" = "yes" ]; then | |
| 166 echo "Would mount ${_name} on ${_real_mp}" | |
| 167 else | |
| 168 mkdir -p "${_real_mp}" 1> /dev/null 2> /dev/null || \ | |
| 169 { echo "ERROR: cannot create mountpoint ${_real_mp}" >&2; return 1; } | |
| 170 echo "Mounting ${_name} on ${_real_mp}" | |
| 171 mount -t zfs "${_name}" "${_real_mp}" || return 1 | |
| 172 fi | |
| 173 ;; | |
| 174 *) | |
| 175 if [ "${_opt_mount_outside}" = "yes" ]; then | |
| 176 if [ "${_opt_dry_run}" = "yes" ]; then | |
| 177 echo "Would mount ${_name} on configured ZFS dataset mountpoint ${_mp}" | |
| 178 else | |
| 179 echo "Mounting ${_name} on configured ZFS dataset mountpoint ${_mp}" | |
| 180 zfs mount "${_name}" || return 1 | |
| 181 fi | |
| 182 else | |
| 183 echo "Skipping ${_name} because its configured ZFS mountpoint is not relative to given root dataset" 2>&1 | |
| 184 fi | |
| 185 ;; | |
| 186 esac | |
| 187 done | |
| 188 | |
| 189 return 0 | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 | |
| 194 #: | |
| 195 #: Implement the "umount" command. | |
| 196 #: | |
| 197 #: Umount a datasets and recursively all its children datasets. | |
| 198 #: | |
| 199 command_umount() { | |
| 200 local _dsname | |
| 201 | |
| 202 local _name _mp _rest _rootds_mountpoint | |
| 203 | |
| 204 _dsname="${1-}" | |
| 205 [ -z "${_dsname}" ] && { echo "ERROR: no dataset given" 1>&2; return 2; } | |
| 206 | |
| 207 # Just determine whether the given dataset name exists | |
| 208 _rootds_mountpoint="$(zfs list -H -o mountpoint -t filesystem "${_dsname}")" || { echo "ERROR: dataset not found" 1>&2; return 1; } | |
| 209 | |
| 210 mount -t zfs -p \ | |
| 211 | grep -E "^${_dsname}(/|\s)" \ | |
| 212 | sort -n -r \ | |
| 213 | { | |
| 214 while IFS=' '$'\t' read -r _name _mp _rest ; do | |
| 215 echo "Umounting ${_name} on ${_mp}" | |
| 216 umount "${_mp}" || return 1 | |
| 217 done | |
| 218 } | |
| 219 return 0 | |
| 220 } | |
| 221 | |
| 222 # | |
| 223 # Global option handling | |
| 224 # | |
| 225 while getopts "Vh" _opt ; do | |
| 226 case ${_opt} in | |
| 227 V) | |
| 228 printf 'ftjail v%s (rv:%s)\n' "${VERSION}" '@@HGREVISION@@' | |
| 229 exit 0 | |
| 230 ;; | |
| 231 h) | |
| 232 echo "${USAGE}" | |
| 233 exit 0 | |
| 234 ;; | |
| 235 \?) | |
| 236 exit 2; | |
| 237 ;; | |
| 238 *) | |
| 239 echo "ERROR: option handling failed" 1>&2 | |
| 240 exit 2 | |
| 241 ;; | |
| 242 esac | |
| 243 done | |
| 244 # | |
| 245 # Reset the Shell's option handling system to prepare for handling | |
| 246 # command-local options. | |
| 247 # | |
| 248 shift $((OPTIND-1)) | |
| 249 OPTIND=1 | |
| 250 | |
| 251 test $# -gt 0 || { echo "ERROR: no command given" 1>&2; exit 2; } | |
| 252 | |
| 253 command="$1" | |
| 254 shift | |
| 255 | |
| 256 case "${command}" in | |
| 257 mount) | |
| 258 command_mount "$@" | |
| 259 ;; | |
| 260 umount|unmount) | |
| 261 command_umount "$@" | |
| 262 ;; | |
| 263 *) | |
| 264 echo "ERROR: unknown command \`${command}'" 1>&2 | |
| 265 exit 2 | |
| 266 ;; | |
| 267 esac |
