automount/beadm

754 lines
26 KiB
Bash
Executable File

#!/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} 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 sys/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