#!/bin/sh -e # Copyright (c) 2012 Slawomir Wojciech Wojtczak (vermaden). All rights reserved. # Copyright (c) 2012 Bryan Drewery (bdrewery). All rights reserved. # Copyright (c) 2012 Mike Clarke (rawthey). All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that following conditions are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS 'AS IS' AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. unset LC_ALL unset LANG PATH=${PATH}:/bin:/usr/bin:/sbin:/usr/sbin if [ $( uname -r | cut -d '.' -f1 ) -lt 8 ] then echo "ERROR: beadm works on FreeBSD 8.0 or later" exit 1 fi __usage() { local NAME=${0##*/} echo "usage:" echo " ${NAME} activate beName" echo " ${NAME} create [-e nonActiveBe] beName" echo " ${NAME} create [-e beName@snapshot] beName" echo " ${NAME} create beName@snapshot" echo " ${NAME} destroy [-F] beName" echo " ${NAME} destroy [-F] beName@snapshot" echo " ${NAME} list [-a]" echo " ${NAME} list [-D]" echo " ${NAME} mount beName [mountpoint]" echo " ${NAME} umount [-f] beName" echo " ${NAME} unmount [-f] beName" echo " ${NAME} rename origBeName newBeName" exit 1 } # check if boot environment exists __be_exist() { # 1=DATASET if ! zfs list -H -o name ${1} 1> /dev/null 2> /dev/null then echo "ERROR: Boot environment '${1##*/}' does not exist" exit 1 fi } # check if argument is a snapshot __be_snapshot() { # 1=DATASET/SNAPSHOT echo "${1}" | grep -q "@" 2> /dev/null } # check if boot environment is mounted __be_mounted() { # 1=BE mount 2> /dev/null | grep -q -E "^${1} " 2> /dev/null } # check if boot environment is a clone __be_clone() { # 1=DATASET if zfs list ${1} 1> /dev/null 2> /dev/null then local ORIGIN="$( zfs list -H -o origin ${1} )" if [ "${ORIGIN}" = "-" ] then # boot environment is not a clone return 1 else # boot environment is a clone return 0 fi else # boot environment does not exist return 2 fi } # create new boot environment __be_new() { # 1=SOURCE 2=TARGET local SOURCE=$( echo ${1} | cut -d '@' -f 1 ) if __be_snapshot ${1} then # create boot environment from snapshot local SNAPSHOT=$( echo ${1} | cut -d '@' -f 2 ) zfs list -r -H -t filesystem -o name ${SOURCE} \ | while read FS do if ! zfs list -H -o name ${FS}@${SNAPSHOT} 1> /dev/null 2> /dev/null then echo "ERROR: Child snapshot '${FS}@${SNAPSHOT}' does not exist" exit 1 fi done else # create boot environment from other boot environment if zfs list -H -o name ${1}@${2##*/} 1> /dev/null 2> /dev/null then echo "ERROR: Snapshot '${1}@${2##*/}' already exists" exit 1 fi # snapshot format FMT=$( date "+%Y-%m-%d-%H:%M:%S" ) if ! zfs snapshot -r ${1}@${FMT} 1> /dev/null 2> /dev/null then echo "ERROR: Cannot create snapshot '${1}@${FMT}'" exit 1 fi fi # clone properties of source boot environment zfs list -H -o name -r ${SOURCE} \ | while read FS do local OPTS="" while read NAME PROPERTY VALUE do local OPTS="-o ${PROPERTY}=${VALUE} ${OPTS}" done << EOF $( zfs get -o name,property,value -s local,received -H all ${FS} | awk '!/[\t ]canmount[\t ]/' ) EOF DATASET=$( echo ${FS} | awk '{print $1}' | sed -E s/"^${POOL}\/ROOT\/${SOURCE##*/}"/"${POOL}\/ROOT\/${2##*/}"/g ) if [ "${OPTS}" = "-o = " ] then local OPTS="" fi if __be_snapshot ${1} then zfs clone -o canmount=off ${OPTS} ${FS}@${1##*@} ${DATASET} else zfs clone -o canmount=off ${OPTS} ${FS}@${FMT} ${DATASET} fi done } ROOTFS=$( mount | awk '/ \/ / {print $1}' ) if echo ${ROOTFS} | grep -q -m 1 -E "^/dev/" then echo "ERROR: This system does not boot from ZFS pool" exit 1 fi POOL=$( echo ${ROOTFS} | awk -F '/' '{print $1}' ) if ! zfs list ${POOL}/ROOT 1> /dev/null 2> /dev/null then echo "ERROR: This system is not configured for boot environments" exit 1 fi BOOTFS=$( zpool list -H -o bootfs ${POOL} ) if [ -z "${BOOTFS}" -o "${BOOTFS}" = "-" ] then echo "ERROR: ZFS boot pool '${POOL}' has unset 'bootfs' property" exit 1 fi case ${1} in (list) # -------------------------------------------------------------------- if [ ${#} -ne 1 -a "${2}" != "-a" -a "${2}" != "-D" ] then __usage fi if [ "${2}" = "-D" ] then awk -v pool="${POOL}" -v rootfs="${ROOTFS}" -v bootfs="${BOOTFS}" \ 'function normalize(v) { return substr(v, 1, length(v) - 1) * multiplier[substr(v, length(v))] } function show_units(v) { if(v <= 1024) { unit = "K"; } else if(v <= 1048576) { v = v / 1024; unit = "M"; } else if(v <= 1073741824) { v = v / 1048576; unit = "G"; } else if(v <= 1099511627776) { v = v / 1073741824; unit = "T"; } else if(v <= 1125899906842624) { v = v / 1099511627776; unit = "P"; } else if(v <= 1152921504606846976) { v = v / 1125899906842624; unit = "E"; } else { v = v / 1152921504606846976; unit = "Z"; } fmt = "%.1f" return sprintf(fmt "%s", v, unit) } function get_bename(v) { sub(bename_begins_with "\/", "", v) sub("/.*", "", v) return v } BEGIN { FS = "\\t" bename_begins_with = pool "/ROOT" multiplier["K"] = 1 multiplier["M"] = 1024 multiplier["G"] = 1048576 multiplier["T"] = 1073741824 multiplier["P"] = 1099511627776 multiplier["E"] = 1125899906842624 multiplier["Z"] = 1152921504606846976 mountpoint_length = 10 while("zfs list -H -o name,usedds,usedbysnapshots,usedrefreserv,refer,origin -r " bename_begins_with | getline) { bename = get_bename($1) if(! (bename in size)) { l = length(bename) if(l > bename_length) bename_length = l mountpoint = "-" mountline = "" "mount | grep \"^" $1 " \"" | getline mountline split(mountline, mountdata, " ") mountpoint = mountdata[3] mount[bename] = mountpoint l = length(mountpoint) if(l > mountpoint_length) mountpoint_length = l } if($6 == "-") size[bename] += (normalize($2) + normalize($4)) else size[bename] += (normalize($2) + normalize($4) + normalize($5)) } printf "%-" bename_length "s %-6s %-" mountpoint_length "s %6s %s\n", "BE", "Active", "Mountpoint", "Space", "Created" while("zfs list -H -o name,mountpoint,creation -s creation -d 1 " bename_begins_with | getline) { if($1 !~ /ROOT$/) { active = "" if($1 == rootfs) active = active "N" if($1 == bootfs) active = active "R" if(! active) {active = "-"} bename = get_bename($1) sub("^.*/", "", bename) "date -j -f \"%a %b %d %H:%M %Y\" \"" $3 "\" +\"%Y-%m-%d %H:%M\"" | getline creation printf "%-" bename_length "s %-6s %-" mountpoint_length "s %6s %-6s\n", bename, active, mount[bename], show_units(size[bename]), creation } } }' exit 0 fi if [ "${2}" = "-a" ] then ZFS_LIST=$( zfs list -o name,mountpoint,used,creation -H -t all -r ${POOL}/ROOT | sed 1d ) else ZFS_LIST=$( zfs list -o name,mountpoint,used,creation -s creation -H -d 1 -r ${POOL}/ROOT | sed 1d ) fi WIDTH_CREATION=$( echo "${ZFS_LIST}" | awk '{print $5}' | wc -L ) WIDTH_NAME=$( echo "${ZFS_LIST}" | awk '{print $1}' | wc -L ) ZFS_MOUNT_LIST=$( zfs mount | grep "^${POOL}/ROOT/" ) if [ "${2}" = "-a" ] then WIDTH_MOUNT=$( echo "${ZFS_MOUNT_LIST}" | awk '{print $2}' | wc -L ) else BENAMES=$( echo "${ZFS_LIST}" | awk '{print $1}' | xargs basename | tr '\n' '|' | sed 's/.$//' ) WIDTH_MOUNT=$( echo "${ZFS_MOUNT_LIST}" | grep -E "(${BENAMES}) " | awk '{print $2}' | wc -L ) WIDTH_NAME=$(( ${WIDTH_NAME} - ${#POOL} - 6 )) if [ ${WIDTH_MOUNT} -lt 10 ] then WIDTH_MOUNT=10 fi fi # get list of USEDBYDATASET and USED properties USED_ALL=$( zfs list -H -t all -o name,usedbydataset,usedbysnapshots,used -r ${POOL}/ROOT \ | sed 1d \ | sed '/0$/d' \ | sed '/-$/d' \ | awk '{ gsub("-"," 0 0 ",$2); gsub("-"," 0 0 ",$3); gsub("-"," 0 0 ",$4); gsub("K"," 1 ",$2); gsub("K"," 1 ",$3); gsub("K"," 1 ",$4) gsub("M"," 1024 ",$2); gsub("M"," 1024 ",$3); gsub("M"," 1024 ",$4); gsub("G"," 1048576 ",$2); gsub("G"," 1048576 ",$3); gsub("G"," 1048576 ",$4); gsub("T"," 1073741824 ",$2); gsub("T"," 1073741824 ",$3); gsub("T"," 1073741824 ",$4); gsub("P"," 1099511627776 ",$2); gsub("P"," 1099511627776 ",$3); gsub("P"," 1099511627776 ",$4); gsub("E"," 1125899906842624 ",$2); gsub("E"," 1125899906842624 ",$3); gsub("E"," 1125899906842624 ",$4); gsub("Z"," 1152921504606846976 ",$2); gsub("Z"," 1152921504606846976 ",$3); gsub("Z"," 1152921504606846976 ",$4); print $0; }' ) # get the list of names and origins for all boot environments SNAPSHOT_ALL=$( zfs list -H -t all -o name,origin -d 1 ${POOL}/ROOT ) if [ "${2}" = "-a" ] then printf "%-${WIDTH_NAME}s %-6s %-${WIDTH_MOUNT}s %6s %s\n" \ BE/Dataset/Snapshot Active Mountpoint Space Created else printf "%-${WIDTH_NAME}s %-6s %-${WIDTH_MOUNT}s %6s %s\n" \ BE Active Mountpoint Space Created fi echo "${ZFS_LIST}" \ | while read NAME MOUNTPOINT USED a b d HM Y do TOTAL=0 DATASET=${NAME} NAME=${NAME##*/} ACTIVE='' if [ "${POOL}/ROOT/${NAME}" = "${ROOTFS}" ] then ACTIVE="${ACTIVE}N" fi if [ "${POOL}/ROOT/${NAME}" = "${BOOTFS}" ] then ACTIVE="${ACTIVE}R" fi if [ -z "${ACTIVE}" ] then ACTIVE="-" fi if [ "${2}" = "-a" ] then MOUNT=$( echo "${ZFS_MOUNT_LIST}" | grep -m 1 "^${DATASET}" | awk '{print $2}' ) else MOUNT=$( echo "${ZFS_MOUNT_LIST}" | grep -m 1 "^${POOL}/ROOT/${NAME}" | awk '{print $2}' ) fi if [ -z "${MOUNT}" ] then MOUNT="-" fi # get the name of origin snapshot for boot environment SNAPSHOT=$( echo "${SNAPSHOT_ALL}" | awk "/^${POOL}\/ROOT\/${NAME}\t/" | awk -F '@' '{print $2}' ) if [ "${2}" = "-a" ] then # use the USED field from ZFS LIST TOTAL=${USED} else # calculate overhead space usage for boot environment OVERHEAD=$( echo "${USED_ALL}" \ | grep "^${POOL}\/ROOT\/${NAME}" \ | grep -v "@" \ | awk \ 'BEGIN { overhead = 0 } { overhead += $4 * $5 } END { print overhead }' ) # calculate snapshots space usage for boot environment SNAPS=$( echo "${USED_ALL}" \ | grep "^${POOL}\/ROOT\/${NAME}" \ | grep "@" \ | awk \ 'BEGIN { snaps = 0 } { snaps += $6 * $7 } END { print snaps }' ) # calculate space usage for boot environment datasets USED=$( echo "${USED_ALL}" \ | awk -v name="^${POOL}\/ROOT\/${NAME}" -v snapshot="@${SNAPSHOT}$" \ 'BEGIN { used = 0 } ( $1 ~ name ) { used += $2 * $3 } ( $1 ~ snapshot ) { used += $6 * $7 } END { print used }' ) # calculate total space usage for boot environment TOTAL=$( echo | awk -v used=${USED} -v snaps=${SNAPS} -v overhead=${OVERHEAD} \ 'BEGIN { total = used + overhead - snaps } END { if(total <= 1024) { unit = "K"; } else if(total <= 1048576) { total = total / 1024; unit = "M"; } else if(total <= 1073741824) { total = total / 1048576; unit = "G"; } else if(total <= 1099511627776) { total = total / 1073741824; unit = "T"; } else if(total <= 1125899906842624) { total = total / 1099511627776; unit = "P"; } else if(total <= 1152921504606846976) { total = total / 1125899906842624; unit = "E"; } else { total = total / 1152921504606846976; unit = "Z"; } printf("%.1f%s", total, unit); }' ) fi if [ "${2}" = "-a" ] then if echo "${DATASET}" | grep -v "@" | grep -q "${POOL}/ROOT/${NAME}" 2> /dev/null then echo ${NAME} fi printf " %-${WIDTH_NAME}s %-6s %-${WIDTH_MOUNT}s %6s " ${DATASET} ${ACTIVE} ${MOUNT} ${TOTAL} date -j -f "%a %b %d %H:%M %Y" "${a} ${b} ${d} ${HM} ${y}" +"%Y-%m-%d %H:%M" else printf "%-${WIDTH_NAME}s %-6s %-${WIDTH_MOUNT}s %6s " ${NAME} ${ACTIVE} ${MOUNT} ${TOTAL} date -j -f "%a %b %d %H:%M %Y" "${a} ${b} ${d} ${HM} ${y}" +"%Y-%m-%d %H:%M" fi done ;; (create) # ------------------------------------------------------------------ case ${#} in (4) if ! [ ${2} = "-e" ] then __usage fi # check if argument for -e option is full path dataset # argument for -e option must be 'beName' or 'beName@snapshot' if echo ${3} | grep -q "/" 2> /dev/null then __usage fi __be_exist ${POOL}/ROOT/${3} if zfs list -H -o name ${POOL}/ROOT/${4} 1> /dev/null 2> /dev/null then echo "ERROR: Boot environment '${4}' already exists" exit 1 fi __be_new ${POOL}/ROOT/${3} ${POOL}/ROOT/${4} ;; (2) if __be_snapshot ${2} then if ! zfs snapshot -r ${POOL}/ROOT/${2} 1> /dev/null 2> /dev/null then echo "ERROR: Cannot create '${2}' recursive snapshot" exit 1 fi else __be_new ${ROOTFS} ${POOL}/ROOT/${2} fi ;; (*) __usage ;; esac echo "Created successfully" ;; (activate) # ---------------------------------------------------------------- if [ ${#} -ne 2 ] then __usage fi __be_exist ${POOL}/ROOT/${2} if [ "${BOOTFS}" = "${POOL}/ROOT/${2}" ] then echo "Already activated" exit 0 else if __be_mounted ${POOL}/ROOT/${2} then MNT=$( mount | grep -E "^${POOL}/ROOT/${2} " | awk '{print $3}' ) if [ "${MNT}" != "/" ] then # boot environment is not current root and its mounted echo "ERROR: Boot environment '${2}' is mounted at '${MNT}'" echo "ERROR: Cannot activate manually mounted boot environment" exit 1 fi fi # do not change root (/) mounted boot environment mountpoint if [ "${ROOTFS}" != "${POOL}/ROOT/${2}" ] then TMPMNT=$( mktemp -d /tmp/tmp.XXXXXX ) if ! mkdir -p ${TMPMNT} 2> /dev/null then echo "ERROR: Cannot create '${TMPMNT}' directory" exit 1 fi MOUNT=0 while read FS MNT TYPE OPTS DUMP FSCK; do if [ "${FS}" = "${POOL}/ROOT/${2}" ] then MOUNT=${MNT} break fi done << EOF $( mount -p ) EOF if [ ${MOUNT} -eq 0 ] then zfs set canmount=noauto ${POOL}/ROOT/${2} zfs set mountpoint=${TMPMNT} ${POOL}/ROOT/${2} zfs mount ${POOL}/ROOT/${2} else TMPMNT=${MOUNT} fi cp /boot/zfs/zpool.cache ${TMPMNT}/boot/zfs/zpool.cache LOADER_CONFIGS=${TMPMNT}/boot/loader.conf if [ -f ${TMPMNT}/boot/loader.conf.local ] then LOADER_CONFIGS="${LOADER_CONFIGS} ${TMPMNT}/boot/loader.conf.local" fi sed -i '' -E s/"^vfs.root.mountfrom=.*$"/"vfs.root.mountfrom=\"zfs:${POOL}\/ROOT\/${2##*/}\""/g ${LOADER_CONFIGS} if [ ${MOUNT} -eq 0 ] then zfs umount ${POOL}/ROOT/${2} zfs set mountpoint=legacy ${POOL}/ROOT/${2} fi fi if ! zpool set bootfs=${POOL}/ROOT/${2} ${POOL} 1> /dev/null 2> /dev/null then echo "ERROR: Failed to activate '${2}' boot environment" exit 1 fi fi # execute ZFS LIST only once ZFS_LIST=$( zfs list -H -o name -r ${POOL}/ROOT ) # disable automatic mount on all inactive boot environments echo "${ZFS_LIST}" \ | grep -v "^${POOL}/ROOT/${2}" \ | while read NAME do zfs set canmount=noauto ${NAME} done # enable automatic mount for active boot environment and promote it echo "${ZFS_LIST}" \ | grep "^${POOL}/ROOT/${2}" \ | while read NAME do zfs set canmount=on ${NAME} while __be_clone ${NAME} do zfs promote ${NAME} done done echo "Activated successfully" ;; (destroy) # ----------------------------------------------------------------- if [ "${2}" != "-F" ] then DESTROY=${2} else DESTROY=${3} fi __be_exist ${POOL}/ROOT/${DESTROY} case ${#} in (2) echo "Are you sure you want to destroy '${2}'?" echo -n "This action cannot be undone (y/[n]): " read CHOICE ;; (3) if [ "${2}" != "-F" ] then __usage fi CHOICE=Y ;; (*) __usage ;; esac if [ "${BOOTFS}" = "${POOL}/ROOT/${DESTROY}" ] then echo "ERROR: Cannot destroy active boot environment" exit 1 fi case ${CHOICE} in (Y|y|[Yy][Ee][Ss]) # destroy snapshot or boot environment if __be_snapshot ${POOL}/ROOT/${DESTROY} then # destroy desired snapshot if ! zfs destroy -r ${POOL}/ROOT/${DESTROY} 1> /dev/null 2> /dev/null then echo "ERROR: Snapshot '${2}' is origin for other boot environment" exit 1 fi else if __be_clone ${POOL}/ROOT/${DESTROY} then # promote clones dependent on snapshots used by destroyed boot environment zfs list -H -t all -o name,origin \ | while read NAME ORIGIN do if echo "${ORIGIN}" | grep -q -E "${POOL}/ROOT/${DESTROY}(/.*@|@)" 2> /dev/null then zfs promote ${NAME} fi done # get origins used by destroyed boot environment ORIGIN_SNAPSHOTS=$( zfs list -H -t all -o origin -r ${POOL}/ROOT/${DESTROY} | grep -v '^-$' | awk -F "@" '{print $2}' | sort -u ) fi # check if boot environment was created from existing snapshot ORIGIN=$( zfs list -H -o origin ${POOL}/ROOT/${DESTROY} ) CREATION=$( zfs list -H -o creation ${POOL}/ROOT/${DESTROY} ) CREATION=$( date -j -f "%a %b %d %H:%M %Y" "${CREATION}" +"%Y-%m-%d-%H:%M" ) SNAPSHOT_NAME=$( echo "${ORIGIN}" | cut -d '@' -f 2 | sed -E 's/:[0-9]{2}$//g' ) if [ "${2}" = "-F" ] then CHOICE=1 elif [ "${SNAPSHOT_NAME}" != "${CREATION}" ] then ORIGIN=$( basename ${ORIGIN} ) echo "Boot environment '${DESTROY}' was created from existing snapshot" echo -n "Destroy '${ORIGIN}' snapshot? (y/[n]): " read CHOICE case ${CHOICE} in (Y|y|[Yy][Ee][Ss]) CHOICE=1 ;; (*) CHOICE=0 echo "Origin snapshot '${ORIGIN}' will be preserved" ;; esac else CHOICE=1 fi # destroy boot environment zfs destroy -r ${POOL}/ROOT/${DESTROY} # check if boot environment is a clone if __be_clone ${POOL}/ROOT/${DESTROY} then # promote datasets dependent on origins used by destroyed boot environment ALL_ORIGINS=$( zfs list -H -t all -o name,origin ) echo "${ORIGIN_SNAPSHOTS}" \ | while read S do echo "${ALL_ORIGINS}" \ | grep "${S}" \ | awk '{print $1}' \ | while read I do zfs promote ${I} done done fi # destroy origins used by destroyed boot environment SNAPSHOTS=$( zfs list -H -t snapshot -o name ) echo "${ORIGIN_SNAPSHOTS}" \ | while read S do echo "${SNAPSHOTS}" \ | grep "@${S}$" \ | while read I do if [ ${CHOICE} -eq 1 ] then zfs destroy ${I} fi done done fi echo "Destroyed successfully" ;; (*) echo "Boot environment '${DESTROY}' has not been destroyed" ;; esac ;; (rename) # ------------------------------------------------------------------ if [ ${#} -ne 3 ] then __usage fi __be_exist ${POOL}/ROOT/${2} if [ "${BOOTFS}" = "${POOL}/ROOT/${2}" ] then echo "ERROR: Renaming active boot environment is not supported" exit 1 fi if zfs list -H -o name ${POOL}/ROOT/${3} 2> /dev/null then echo "ERROR: Boot environment '${3}' already exists" exit 1 fi zfs rename ${POOL}/ROOT/${2} ${POOL}/ROOT/${3} echo "Renamed successfully" ;; (mount) # ------------------------------------------------------------------- if [ ${#} -eq 2 ] then TARGET=$( mktemp -d /tmp/tmp.XXXXXX ) elif [ ${#} -eq 3 ] then TARGET=${3} else __usage fi __be_exist "${POOL}/ROOT/${2}" if __be_mounted "${POOL}/ROOT/${2}" then MNT=$( mount | grep -E "^${POOL}/ROOT/${2} " | awk '{print $3}' ) echo "Boot environment '${2}' is already mounted at '${MNT}'" exit 1 fi if ! mkdir -p ${TARGET} 2> /dev/null then echo "ERROR: Cannot create '${TARGET}' mountpoint" exit 1 fi if ! mount -t zfs ${POOL}/ROOT/${2} ${TARGET} then echo "ERROR: Cannot mount '${2}' at '${TARGET}' mountpoint" exit 1 fi PREFIX=$( echo ${POOL}/ROOT/${2}/ | sed 's/\//\\\//g' ) zfs list -H -o name,mountpoint -r ${POOL}/ROOT/${2} \ | grep -v "legacy$" \ | sort -n \ | grep -E "^${POOL}/ROOT/${2}/" \ | while read FS MOUNTPOINT do if [ "{FS}" != "${POOL}/ROOT/${2}" ] then INHERIT=$( zfs get -H -o source mountpoint ${FS} ) if [ "${INHERIT}" = "local" ] then if [ "${MOUNTPOINT}" = "legacy" ] then continue else MOUNTPOINT="/$( echo "${FS}" | sed s/"${PREFIX}"//g )" fi fi fi if ! mkdir -p ${TARGET}${MOUNTPOINT} 1> /dev/null 2> /dev/null then echo "ERROR: Cannot create '${TARGET}${MOUNTPOINT}' mountpoint" exit 1 fi if ! mount -t zfs ${FS} ${TARGET}${MOUNTPOINT} 1> /dev/null 2> /dev/null then echo "ERROR: Cannot mount '${FS}' at '${TARGET}${MOUNTPOINT}' mountpoint" exit 1 fi done echo "Mounted successfully on '${TARGET}'" ;; (umount|unmount) # ---------------------------------------------------------- if [ ${#} -eq 2 ] then # we need this empty section for argument checking : elif [ ${#} -eq 3 -a "${2}" = "-f" ] then OPTS="-f" shift else __usage fi __be_exist "${POOL}/ROOT/${2}" if ! __be_mounted "${POOL}/ROOT/${2}" then echo "Boot environment '${2}' is not mounted" exit 1 fi mount \ | grep "^${POOL}/ROOT/${2}" \ | awk '{print $1}' \ | sort -n -r \ | while read FS do if ! umount ${OPTS} ${FS} 1> /dev/null 2> /dev/null then echo "ERROR: Cannot umount '${FS}' dataset" exit 1 fi done echo "Unmounted successfully" ;; (*) # ----------------------------------------------------------------------- __usage ;; esac