- #!/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 | -e beName@snapshot] <beName>"
- echo " ${NAME} create <beName@snapshot>"
- echo " ${NAME} destroy [-F] <beName | beName@snapshot>"
- echo " ${NAME} list [-a] [-s] [-D] [-H]"
- echo " ${NAME} rename <origBeName> <newBeName>"
- echo " ${NAME} mount <beName> [mountpoint]"
- echo " ${NAME} { umount | unmount } [-f] <beName>"
- 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 [ -f /usr/local/etc/beadm.conf ]
- then
- . /usr/local/etc/beadm.conf || BOOTPOOL=${POOL}
- fi
- 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
- if [ "${POOL}" = "${BOOTPOOL}" ]
- then
- BOOTFS=$( zpool list -H -o bootfs ${BOOTPOOL} )
- else
- BOOTFS=` grep -E "^vfs.root.mountfrom=" /boot/loader.conf \
- | awk -F '=' '{print $2}' | tr -d '"' | awk -F ':' '{print $2}' `
- fi
- if [ -z "${BOOTFS}" -o "${BOOTFS}" = "-" ]
- then
- echo "ERROR: ZFS boot pool '${POOL}' has unset 'bootfs' property"
- exit 1
- fi
- case ${1} in
- (list) # --------------------------------------------------------------------
- OPTION_a=0
- OPTION_D=0
- OPTION_s=0
- shift
- while getopts "aDHs" OPT
- do
- case ${OPT} in
- (a) OPTION_a=1 ;;
- (D) OPTION_D=1 ;;
- (H) OPTION_H=1 ;;
- (s) OPTION_s=1
- OPTION_a=1 ;;
- (*) __usage ;;
- esac
- done
- awk -v POOL="${POOL}" \
- -v ROOTFS="${ROOTFS}" \
- -v BOOTFS="${BOOTFS}" \
- -v OPTION_a="${OPTION_a}" \
- -v OPTION_D="${OPTION_D}" \
- -v OPTION_H="${OPTION_H}" \
- -v OPTION_s="${OPTION_s}" \
- 'function __normalize(VALUE) {
- if(VALUE == "-" || VALUE == 0)
- return 0
- else
- return substr(VALUE, 1, length(VALUE) - 1) * MULTIPLIER[substr(VALUE, length(VALUE))]
- }
- function __show_units(VALUE) {
- if(VALUE < 1024) { UNIT = "K"; }
- else if(VALUE < 1048576) { VALUE /= 1024; UNIT = "M"; }
- else if(VALUE < 1073741824) { VALUE /= 1048576; UNIT = "G"; }
- else if(VALUE < 1099511627776) { VALUE /= 1073741824; UNIT = "T"; }
- else if(VALUE < 1125899906842624) { VALUE /= 1099511627776; UNIT = "P"; }
- else if(VALUE < 1152921504606846976) { VALUE /= 1125899906842624; UNIT = "E"; }
- else { VALUE /= 1152921504606846976; UNIT = "Z"; }
- return sprintf("%.1f%s", VALUE, UNIT)
- }
- function __get_bename(BENAME) {
- sub(BENAME_BEGINS_WITH "\/", "", BENAME)
- sub("/.*", "", BENAME)
- return BENAME
- }
- function __convert_date(DATE) {
- CMD_DATE = "date -j -f \"%a %b %d %H:%M %Y\" \"" DATE "\" +\"%Y-%m-%d %H:%M\""
- CMD_DATE | getline NEW
- close(CMD_DATE)
- return NEW
- }
- BEGIN {
- 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
- FSNAME_LENGTH = 2
- if(OPTION_a == 1)
- FSNAME_LENGTH = 19
- CMD_MOUNT="mount"
- while(CMD_MOUNT | getline)
- if($1 ~ "^" BENAME_BEGINS_WITH)
- MOUNTS[$1] = $3
- close(CMD_MOUNT)
- FS = "\\t"
- CMD_ZFS_LIST = "zfs list -H -t all -s creation -o name,used,usedds,usedbysnapshots,usedrefreserv,refer,creation,origin -r "
- while(CMD_ZFS_LIST BENAME_BEGINS_WITH | getline) {
- if($1 != BENAME_BEGINS_WITH) {
- FSNAME = $1
- FSNAMES[length(FSNAMES) + 1] = FSNAME
- USED = __normalize($2)
- USEDBYDATASET = __normalize($3)
- USEDBYSNAPSHOTS = __normalize($4)
- USEDREFRESERV = __normalize($5)
- REFER[FSNAME] = __normalize($6)
- CREATIONS[FSNAME] = $7
- ORIGINS[FSNAME] = $8
- if(FSNAME ~ /@/)
- SPACES[FSNAME] = USED
- else {
- SPACES[FSNAME] = USEDBYDATASET + USEDREFRESERV
- if(OPTION_D != 1)
- SPACES[FSNAME] += USEDBYSNAPSHOTS
- BE = " " __get_bename(FSNAME) " "
- if(index(BELIST, BE) == 0)
- BELIST = BELIST " " BE
- MOUNTPOINT = MOUNTS[FSNAME]
- if(MOUNTPOINT) {
- if((OPTION_a == 0 && FSNAME == (BENAME_BEGINS_WITH "/" __get_bename(FSNAME))) || (OPTION_a == 1)) {
- LM = length(MOUNTPOINT)
- if(LM > MOUNTPOINT_LENGTH)
- MOUNTPOINT_LENGTH = LM
- }
- }
- else
- MOUNTPOINT = "-"
- }
- if(OPTION_a == 1)
- LF = length(FSNAME)
- else if(FSNAME !~ /@/)
- LF = length(__get_bename(FSNAME))
- if(LF > FSNAME_LENGTH)
- FSNAME_LENGTH = LF
- }
- }
- close(CMD_ZFS_LIST)
- split(BELIST, BENAMES, " ")
- if(OPTION_a == 1) {
- BE_HEAD = "BE/Dataset/Snapshot"
- printf "%-" FSNAME_LENGTH + 2 "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", BE_HEAD, "Active", "Mountpoint", "Space", "Created"
- }
- else if(OPTION_H == 1)
- BE_HEAD = ""
- else {
- BE_HEAD = "BE"
- printf "%-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", BE_HEAD, "Active", "Mountpoint", "Space", "Created"
- }
- if(OPTION_s != 1)
- SNAPSHOT_FILTER = "(/[^@]*)?$"
- for(I = 1; I <= length(BENAMES); I++) {
- BENAME = BENAMES[I]
- if(OPTION_a == 1) {
- printf "\n"
- print BENAME
- for(J = 1; J <= length(FSNAMES); J++) {
- FSNAME = FSNAMES[J]
- if(FSNAME ~ "^" BENAME_BEGINS_WITH "/" BENAME SNAPSHOT_FILTER) {
- ACTIVE = ""
- if(FSNAME == ROOTFS)
- ACTIVE = ACTIVE "N"
- if(FSNAME == BOOTFS)
- ACTIVE = ACTIVE "R"
- if(! ACTIVE)
- ACTIVE = "-"
- MOUNTPOINT = MOUNTS[FSNAME]
- if(! MOUNTPOINT)
- MOUNTPOINT = "-"
- printf " %-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", FSNAME, ACTIVE, MOUNTPOINT, __show_units(SPACES[FSNAME]), __convert_date(CREATIONS[FSNAME])
- ORIGIN = ORIGINS[FSNAME]
- ORIGIN_DISPLAY = ORIGIN
- sub(BENAME_BEGINS_WITH "/", "", ORIGIN_DISPLAY)
- if(ORIGIN != "-") {
- if(OPTION_D == 1)
- SPACE = REFER[ORIGIN]
- else
- SPACE = SPACES[ORIGIN]
- printf " %-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", " " ORIGIN_DISPLAY, "-", "-", __show_units(SPACE), __convert_date(CREATIONS[ORIGIN])
- }
- }
- }
- }
- else {
- SPACE = 0
- ACTIVE = ""
- NAME = BENAME_BEGINS_WITH "/" BENAME
- if(NAME == ROOTFS)
- ACTIVE = ACTIVE "N"
- if(NAME == BOOTFS)
- ACTIVE = ACTIVE "R"
- if(! ACTIVE)
- ACTIVE = "-"
- for(J = 1; J <= length(FSNAMES); J++) {
- FSNAME = FSNAMES[J]
- if(FSNAME ~ "^" BENAME_BEGINS_WITH "/" BENAME "(/[^@]*)?$") {
- if((BENAME_BEGINS_WITH "/" BENAME) == FSNAME) {
- MOUNTPOINT = MOUNTS[FSNAME]
- if(! MOUNTPOINT)
- MOUNTPOINT = "-"
- CREATION = __convert_date(CREATIONS[FSNAME])
- }
- ORIGIN = ORIGINS[FSNAME]
- if(ORIGIN == "-")
- SPACE += SPACES[FSNAME]
- else {
- if(OPTION_D == 1)
- SPACE += REFER[FSNAME]
- else
- SPACE += SPACES[FSNAME] + SPACES[ORIGIN]
- }
- }
- }
- if(OPTION_H == 1)
- printf "%s\t%s\t%s\t%s\t%s\n", BENAME, ACTIVE, MOUNTPOINT, __show_units(SPACE), CREATION
- else
- printf "%-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", BENAME, ACTIVE, MOUNTPOINT, __show_units(SPACE), CREATION
- }
- }
- }'
- ;;
- (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}$" \
- | 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 -E "^${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 \
- | awk '{print $1}' \
- | grep -E "^${POOL}/ROOT/${2}(/|$)" \
- | 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
Latest version of FreeBSD's beadm with support for separate /boot.