Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mount/Umount fixes and improvements #778

Merged
merged 15 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions docs/chapters/subcommands/mount.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,40 @@ To mount storage within the container use `bastille mount`.

.. code-block:: shell

ishmael ~ # bastille mount azkaban /storage/foo /media/foo nullfs ro 0 0
ishmael ~ # bastille mount azkaban /storage/foo media/foo nullfs ro 0 0
[azkaban]:
Added: /media/foo /usr/local/bastille/jails/azkaban/root/media/foo nullfs ro 0 0
ishmael ~ # bastille mount azkaban /storage/bar /media/bar nullfs ro 0 0
[azkaban]:
Added: /media/bar /usr/local/bastille/jails/azkaban/root/media/bar nullfs ro 0 0

Notice the JAIL_PATH format can be /media/foo or simply media/bar. The leading slash / is optional. The HOST_PATH howerver, must be the full path including the leading slash /.

It is also possible to mount individual files into a jail as seen below.
Bastille will not mount if a file is already present at the specified mount point.
If you do not specify a file name, bastille will mount the file underneath the specified directory as seen in the second example below.

.. code-block:: shell

ishmael ~ # bastille mount azkaban /etc/rc.conf /mnt/etc/rc.conf nullfs ro 0 0
[azkaban]:
Added: /etc/rc.conf /usr/local/bastille/jails/azkaban/root/mnt/etc/rc.conf nullfs ro 0 0
ishmael ~ # bastille mount azkaban /etc/rc.conf /media/bar nullfs ro 0 0
[azkaban]:
Added: /etc/rc.conf usr/local/bastille/jails/azkaban/root/media/bar/rc.conf nullfs ro 0 0

It is also possible (but not recommended) to have spaces in the directories that are mounted.
It is necessary to escape each space with a backslash \ and enclose the mount point in quotes "" as seen below.
It is possible to do the same for the jail path, but again, not recommemded.

.. code-block:: shell

ishmael ~ # bastille mount azkaban "/storage/my\ directory\ with\ spaces" /media/foo nullfs ro 0 0
[azkaban]:
Added: /storage/my\040directory\040with\040spaces /usr/local/bastille/jails/azkaban/root/media/foo nullfs ro 0 0

Syntax follows standard `/etc/fstab` format:

.. code-block:: shell

Usage: bastille mount TARGET host_path container_path [filesystem_type options dump pass_number]
Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]
16 changes: 14 additions & 2 deletions docs/chapters/subcommands/umount.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,21 @@ To unmount storage from a container use `bastille umount`.

ishmael ~ # bastille umount azkaban /media/foo
[azkaban]:
Unmounted: /usr/local/bastille/jails/jail4/root/media/foo
ishmael ~ # bastille umount azkaban /mnt/etc/rc.conf
[azkaban]:
Unmounted: /usr/local/bastille/jails/jail4/root/mnt/etc/rc.conf

Syntax requires only the jail path to unmount.

.. code-block:: shell

Usage: bastille umount TARGET JAIL_PATH

Syntax requires only the container path to unmount:
If the directory you are unmounting has spaces, make sure to escape them with a backslash \, and enclode the mount point in quotes "".

.. code-block:: shell

Usage: bastille umount TARGET container_path
ishmael ~ # bastille umount azkaban "/media/foo\ with\ spaces"
[azkaban]:
Unmounted: /usr/local/bastille/jails/jail4/root/media/foo with spaces
4 changes: 2 additions & 2 deletions usr/local/bin/bastille
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@ version|-v|--version)
help|-h|--help)
usage
;;
bootstrap|create|destroy|export|import|list|rdr|restart|setup|start|update|upgrade|verify)
bootstrap|create|destroy|export|import|list|mount|rdr|restart|setup|start|umount|update|upgrade|verify)
# Nothing "extra" to do for these commands. -- cwells
;;
clone|config|cmd|console|convert|cp|edit|htop|limits|mount|pkg|rcp|rename|service|stop|sysrc|tags|template|top|umount|zfs)
clone|config|cmd|console|convert|cp|edit|htop|limits|pkg|rcp|rename|service|stop|sysrc|tags|template|top|zfs)
# Parse the target and ensure it exists. -- cwells
if [ $# -eq 0 ]; then # No target was given, so show the command's help. -- cwells
PARAMS='help'
Expand Down
29 changes: 29 additions & 0 deletions usr/local/share/bastille/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ error_notify() {
echo -e "${COLOR_RED}$*${COLOR_RESET}" 1>&2
}

error_continue() {
error_notify "$@"
# Disabling this shellcheck as we only ever call it inside of a loop
# shellcheck disable=SC2104
continue
}

# Notify message on error and exit
error_exit() {
error_notify "$@"
Expand All @@ -70,6 +77,15 @@ warn() {
echo -e "${COLOR_YELLOW}$*${COLOR_RESET}"
}

check_target_exists() {
local _TARGET="${1}"
if [ ! -d "${bastille_jailsdir}"/"${_TARGET}" ]; then
return 1
else
return 0
fi
}

generate_static_mac() {
local jail_name="${1}"
local external_interface="${2}"
Expand Down Expand Up @@ -131,6 +147,19 @@ EOF
fi
}

set_target() {
local _TARGET="${1}"
if [ "${_TARGET}" = ALL ] || [ "${_TARGET}" = all ]; then
target_all_jails
else
check_target_exists "${_TARGET}" || error_exit "Jail not found \"${_TARGET}\""
JAILS="${_TARGET}"
TARGET="${_TARGET}"
export JAILS
export TARGET
fi
}

checkyesno() {
## copied from /etc/rc.subr -- cedwards (20231125)
## issue #368 (lowercase values should be parsed)
Expand Down
123 changes: 77 additions & 46 deletions usr/local/share/bastille/mount.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,96 +32,127 @@
. /usr/local/etc/bastille/bastille.conf

usage() {
error_exit "Usage: bastille mount TARGET host_path container_path [filesystem_type options dump pass_number]"
error_exit "Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]"
}

# Handle special-case commands first.
case "$1" in
help|-h|--help)
usage
;;
case "${1}" in
help|-h|--help)
usage
;;
esac

if [ $# -lt 2 ]; then
if [ "$#" -lt 3 ] || [ "$#" -gt 6 ]; then
usage
elif [ $# -eq 2 ]; then
_fstab="$@ nullfs ro 0 0"
fi

TARGET="${1}"
shift

if [ "$#" -eq 2 ]; then
_fstab="$(echo "$* nullfs ro 0 0" | sed 's#\\ #\\040#g')"
else
_fstab="$@"
_fstab="$(echo "$*" | sed 's#\\ #\\040#g')"
fi

bastille_root_check
set_target "${TARGET}"

## assign needed variables
_hostpath=$(echo "${_fstab}" | awk '{print $1}')
_jailpath=$(echo "${_fstab}" | awk '{print $2}')
# Assign variables
_hostpath_fstab=$(echo "${_fstab}" | awk '{print $1}')
_hostpath="$(echo "${_hostpath_fstab}" 2>/dev/null | sed 's#\\040# #g')"
_jailpath_fstab=$(echo "${_fstab}" | awk '{print $2}')
_jailpath="$(echo "${_jailpath_fstab}" 2>/dev/null | sed 's#\\040# #g')"
_type=$(echo "${_fstab}" | awk '{print $3}')
_perms=$(echo "${_fstab}" | awk '{print $4}')
_checks=$(echo "${_fstab}" | awk '{print $5" "$6}')

## if any variables are empty, bail out
# Exit if any variables are empty
if [ -z "${_hostpath}" ] || [ -z "${_jailpath}" ] || [ -z "${_type}" ] || [ -z "${_perms}" ] || [ -z "${_checks}" ]; then
error_notify "FSTAB format not recognized."
warn "Format: /host/path jail/path nullfs ro 0 0"
warn "Format: /host/path /jail/path nullfs ro 0 0"
warn "Read: ${_fstab}"
exit 1
usage
fi

# if host path doesn't exist, type is not "nullfs" or are using advanced mount type "tmpfs,linprocfs,linsysfs, fdescfs,
# procfs"
# Exit if host path doesn't exist, type is not "nullfs", or mount is an advanced mount type "tmpfs,linprocfs,linsysfs,fdescfs,procfs"
if { [ "${_hostpath}" = "tmpfs" ] && [ "$_type" = "tmpfs" ]; } || \
{ [ "${_hostpath}" = "linprocfs" ] && [ "${_type}" = "linprocfs" ]; } || \
{ [ "${_hostpath}" = "linsysfs" ] && [ "${_type}" = "linsysfs" ]; } || \
{ [ "${_hostpath}" = "proc" ] && [ "${_type}" = "procfs" ]; } || \
{ [ "${_hostpath}" = "fdesc" ] && [ "${_type}" = "fdescfs" ]; } then
warn "Detected advanced mount type ${_hostpath}"
elif [ ! -d "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then
error_notify "Detected invalid host path or incorrect mount type in FSTAB."
warn "Format: /host/path jail/path nullfs ro 0 0"
elif [ ! -e "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then
error_notify "Invalid host path or incorrect mount type in FSTAB."
warn "Format: /host/path /jail/path nullfs ro 0 0"
warn "Read: ${_fstab}"
exit 1
usage
fi

## if mount permissions are not "ro" or "rw"
# Mount permissions need to be "ro" or "rw"
if [ "${_perms}" != "ro" ] && [ "${_perms}" != "rw" ]; then
error_notify "Detected invalid mount permissions in FSTAB."
warn "Format: /host/path jail/path nullfs ro 0 0"
warn "Format: /host/path /jail/path nullfs ro 0 0"
warn "Read: ${_fstab}"
exit 1
usage
fi

## if check & pass are not "0 0 - 1 1"; bail out
# Dump and pass need to be "0 0 - 1 1"
if [ "${_checks}" != "0 0" ] && [ "${_checks}" != "1 0" ] && [ "${_checks}" != "0 1" ] && [ "${_checks}" != "1 1" ]; then
error_notify "Detected invalid fstab options in FSTAB."
warn "Format: /host/path jail/path nullfs ro 0 0"
warn "Format: /host/path /jail/path nullfs ro 0 0"
warn "Read: ${_fstab}"
exit 1
usage
fi

for _jail in ${JAILS}; do

info "[${_jail}]:"

## aggregate variables into FSTAB entry
_fullpath="${bastille_jailsdir}/${_jail}/root/${_jailpath}"
_fstab_entry="${_hostpath} ${_fullpath} ${_type} ${_perms} ${_checks}"
_fullpath_fstab="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath_fstab}" 2>/dev/null | sed 's#//#/#' )"
_fullpath="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath}" 2>/dev/null | sed 's#//#/#' )"
_fstab_entry="${_hostpath_fstab} ${_fullpath_fstab} ${_type} ${_perms} ${_checks}"

## Create mount point if it does not exist. -- cwells
if [ ! -d "${_fullpath}" ]; then
if ! mkdir -p "${_fullpath}"; then
error_exit "Failed to create mount point inside jail."
fi
# Check if mount point has already been added
_existing_mount="$(echo ${_fullpath_fstab} 2>/dev/null | sed 's#\\#\\\\#g')"
if grep -Eq "[[:blank:]]${_existing_mount}.*[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab"; then
warn "Mountpoint already present in ${bastille_jailsdir}/${_jail}/fstab"
grep -E "[[:blank:]]${_existing_mount}" "${bastille_jailsdir}/${_jail}/fstab"
continue
fi

## if entry doesn't exist, add; else show existing entry
if ! egrep -q "[[:blank:]]${_fullpath}[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab" 2> /dev/null; then
if ! echo "${_fstab_entry}" >> "${bastille_jailsdir}/${_jail}/fstab"; then
error_exit "Failed to create fstab entry: ${_fstab_entry}"

# Create mount point if it does not exist
if [ -d "${_hostpath}" ] && [ ! -d "${_fullpath}" ]; then
mkdir -p "${_fullpath}" || error_continue "Failed to create mount point."
elif [ -f "${_hostpath}" ] ; then
_filename="$( basename ${_hostpath} )"
if echo "${_fullpath}" 2>/dev/null | grep -qow "${_filename}"; then
mkdir -p "$( dirname "${_fullpath}" )" || error_continue "Failed to create mount point."
if [ ! -f "${_fullpath}" ]; then
touch "${_fullpath}" || error_continue "Failed to create mount point."
else
error_notify "Failed. File exists at mount point."
warn "${_fullpath}"
continue
fi
else
_fullpath_fstab="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath_fstab}/${_filename}" 2>/dev/null | sed 's#//#/#' )"
_fullpath="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath}/${_filename}" 2>/dev/null | sed 's#//#/#' )"
_fstab_entry="${_hostpath_fstab} ${_fullpath_fstab} ${_type} ${_perms} ${_checks}"
mkdir -p "$( dirname "${_fullpath}" )" || error_continue "Failed to create mount point."
if [ ! -f "${_fullpath}" ]; then
touch "${_fullpath}" || error_continue "Failed to create mount point."
else
error_notify "Failed. File exists at mount point."
warn "${_fullpath}"
continue
fi
fi
echo "Added: ${_fstab_entry}"
else
warn "Mountpoint already present in ${bastille_jailsdir}/${_jail}/fstab"
egrep "[[:blank:]]${_fullpath}[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab"
fi
mount -F "${bastille_jailsdir}/${_jail}/fstab" -a
echo
fi

# Add entry to fstab and mount
echo "${_fstab_entry}" >> "${bastille_jailsdir}/${_jail}/fstab" || error_continue "Failed to create fstab entry: ${_fstab_entry}"
mount -F "${bastille_jailsdir}/${_jail}/fstab" -a || error_continue "Failed to mount volume: ${_fullpath}"
echo "Added: ${_fstab_entry}"
done
52 changes: 33 additions & 19 deletions usr/local/share/bastille/umount.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,43 +32,57 @@
. /usr/local/etc/bastille/bastille.conf

usage() {
error_exit "Usage: bastille umount TARGET container_path"
error_exit "Usage: bastille umount TARGET JAIL_PATH"
}

# Handle special-case commands first.
case "$1" in
help|-h|--help)
usage
;;
case "${1}" in
help|-h|--help)
usage
;;
esac

if [ $# -ne 1 ]; then
if [ "$#" -ne 2 ]; then
usage
fi

bastille_root_check
TARGET="${1}"
MOUNT_PATH="${2}"

MOUNT_PATH=$1
bastille_root_check
set_target "${TARGET}"

for _jail in ${JAILS}; do

info "[${_jail}]:"

_jailpath="${bastille_jailsdir}/${_jail}/root/${MOUNT_PATH}"
_jailpath="$( echo "${bastille_jailsdir}/${_jail}/root/${MOUNT_PATH}" 2>/dev/null | sed 's#//#/#' | sed 's#\\##g')"
_mount="$( mount | grep -Eo "[[:blank:]]${_jailpath}[[:blank:]]" )"
_jailpath_fstab="$(echo "${bastille_jailsdir}/${_jail}/root/${MOUNT_PATH}" | sed 's#//#/#g' | sed 's# #\\#g' | sed 's#\\#\\\\040#g')"
_fstab_entry="$(grep -Eo "[[:blank:]]${_jailpath_fstab}[[:blank:]]" ${bastille_jailsdir}/${_jail}/fstab)"

if [ ! -d "${_jailpath}" ]; then
error_exit "The specified mount point does not exist inside the jail."
# Exit if mount point non-existent
if [ -z "${_mount}" ] && [ -z "${_fstab_entry}" ]; then
error_continue "The specified mount point does not exist."
fi

# Unmount the volume. -- cwells
if ! umount "${_jailpath}"; then
error_exit "Failed to unmount volume: ${MOUNT_PATH}"
# Unmount
if [ -n "${_mount}" ]; then
umount "${_jailpath}" || error_continue "Failed to unmount volume: ${MOUNT_PATH}"
fi

# Remove the entry from fstab so it is not automounted in the future. -- cwells
if ! sed -E -i '' "\, +${_jailpath} +,d" "${bastille_jailsdir}/${_jail}/fstab"; then
error_exit "Failed to delete fstab entry: ${_fstab_entry}"
# Remove entry from fstab
if [ -n "${_fstab_entry}" ]; then
if ! sed -E -i '' "\, +${_jailpath_fstab} +,d" "${bastille_jailsdir}/${_jail}/fstab"; then
error_continue "Failed to delete fstab entry: ${MOUNT_PATH}"
fi
fi

echo "Unmounted: ${MOUNT_PATH}"
echo
# Delete if mount point was a file
if [ -f "${_jailpath}" ]; then
rm -f "${_jailpath}" || error_continue "Failed to unmount volume: ${MOUNT_PATH}"
fi

echo "Unmounted: ${_jailpath}"

done