From 5583ab1d7888a2ef3c3fcb5d33cc6d885c73bacd Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 3 Feb 2021 18:31:41 -0400 Subject: [PATCH] Add support bastille RAW image exports/imports, also extended help usage --- usr/local/share/bastille/export.sh | 134 +++++++++++++++++++---------- usr/local/share/bastille/import.sh | 38 ++++++-- 2 files changed, 120 insertions(+), 52 deletions(-) diff --git a/usr/local/share/bastille/export.sh b/usr/local/share/bastille/export.sh index 03977857..ed168512 100644 --- a/usr/local/share/bastille/export.sh +++ b/usr/local/share/bastille/export.sh @@ -32,7 +32,13 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille export TARGET [option] | PATH" + error_exit "Usage: bastille export TARGET [options] | PATH + \n + \nOptions: + \n + -t|--txz -- Export to a standard .txz archive even if bastille is configured for zfs\n + -s|--safe -- Safely stop the jail to snapshot it then start it again to proceed exporting\n + -r|--raw -- Export the jail to an uncompressed raw image\n" } # Handle special-case commands first @@ -47,40 +53,47 @@ if [ "${TARGET}" = "ALL" ]; then error_exit "Batch export is unsupported." fi -if [ $# -gt 2 ] || [ $# -lt 0 ]; then +if [ $# -gt 4 ] || [ $# -lt 0 ]; then usage fi -OPTION="${1}" -EXPATH="${2}" SAFE_EXPORT= +RAW_EXPORT= +DIR_EXPORT= -# Handle some options -if [ -n "${OPTION}" ]; then - if [ "${OPTION}" = "-t" -o "${OPTION}" = "--txz" ]; then - if [ "${bastille_zfs_enable}" = "YES" ]; then - # Temporarily disable ZFS so we can create a standard backup archive - bastille_zfs_enable="NO" - fi - elif [ "${OPTION}" = "-s" -o "${OPTION}" = "--safe" ]; then - SAFE_EXPORT="1" - elif echo "${OPTION}" | grep -q "\/"; then - if [ -d "${OPTION}" ]; then - EXPATH="${OPTION}" - else - error_exit "Error: Path not found." - fi - else - error_notify "Invalid option!" - usage - fi -fi +# Handle and parse option args +while [ $# -gt 0 ]; do + case "${1}" in + -t|--txz) + if [ "${bastille_zfs_enable}" = "YES" ]; then + bastille_zfs_enable="NO" + fi + shift + ;; + -s|--safe) + SAFE_EXPORT="1" + shift + ;; + -r|--raw) + RAW_EXPORT="1" + shift + ;; + *) + if echo "${1}" | grep -q "\/"; then + DIR_EXPORT="${1}" + else + usage + fi + shift + ;; + esac +done # Export directory check -if [ -n "${EXPATH}" ]; then - if [ -d "${EXPATH}" ]; then +if [ -n "${DIR_EXPORT}" ]; then + if [ -d "${DIR_EXPORT}" ]; then # Set the user defined export directory - bastille_backupsdir="${EXPATH}" + bastille_backupsdir="${DIR_EXPORT}" else error_exit "Error: Path not found." fi @@ -92,36 +105,63 @@ create_zfs_snap(){ zfs snapshot -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" } +export_check(){ + # Inform the user about the exporting method + if [ -n "$(jls name | awk "/^${TARGET}$/")" ]; then + EXPORT_AS="Hot exporting" + else + EXPORT_AS="Exporting" + fi + if [ -n "${RAW_EXPORT}" ]; then + EXPORT_INFO="to a raw" + else + EXPORT_INFO="to a compressed ${FILE_EXT}" + fi + + # Safely stop and snapshot the jail + if [ -n "${SAFE_EXPORT}" ]; then + info "Safely exporting '${TARGET}' ${EXPORT_INFO} archive." + bastille stop ${TARGET} + create_zfs_snap + bastille start ${TARGET} + else + info "${EXPORT_AS} '${TARGET}' ${EXPORT_INFO} archive." + create_zfs_snap + fi + info "Sending ZFS data stream..." +} + jail_export() { # Attempt to export the container DATE=$(date +%F-%H%M%S) if [ "${bastille_zfs_enable}" = "YES" ]; then if [ -n "${bastille_zfs_zpool}" ]; then - FILE_EXT="xz" + if [ -n "${RAW_EXPORT}" ]; then + FILE_EXT="" + export_check - if [ -n "${SAFE_EXPORT}" ]; then - info "Safely exporting '${TARGET}' to a compressed .${FILE_EXT} archive." - bastille stop ${TARGET} - create_zfs_snap - bastille start ${TARGET} + # Export the raw container recursively and cleanup temporary snapshots + zfs send -R "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" \ + > "${bastille_backupsdir}/${TARGET}_${DATE}" + zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}/root@bastille_export_${DATE}" + zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" else - info "Hot exporting '${TARGET}' to a compressed .${FILE_EXT} archive." - create_zfs_snap - fi + FILE_EXT=".xz" + export_check - info "Sending ZFS data stream..." - # Export the container recursively and cleanup temporary snapshots - zfs send -R "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" | \ - xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}.${FILE_EXT}" - zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}/root@bastille_export_${DATE}" - zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" + # Export the container recursively and cleanup temporary snapshots + zfs send -R "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" | \ + xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}" + zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}/root@bastille_export_${DATE}" + zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" + fi fi else # Create standard backup archive - FILE_EXT="txz" - info "Exporting '${TARGET}' to a compressed .${FILE_EXT} archive..." - cd "${bastille_jailsdir}" && tar -cf - "${TARGET}" | xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}.${FILE_EXT}" + FILE_EXT=".txz" + info "Exporting '${TARGET}' to a compressed ${FILE_EXT} archive..." + cd "${bastille_jailsdir}" && tar -cf - "${TARGET}" | xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}" fi if [ "$?" -ne 0 ]; then @@ -129,8 +169,8 @@ jail_export() else # Generate container checksum file cd "${bastille_backupsdir}" - sha256 -q "${TARGET}_${DATE}.${FILE_EXT}" > "${TARGET}_${DATE}.sha256" - info "Exported '${bastille_backupsdir}/${TARGET}_${DATE}.${FILE_EXT}' successfully." + sha256 -q "${TARGET}_${DATE}${FILE_EXT}" > "${TARGET}_${DATE}.sha256" + info "Exported '${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}' successfully." exit 0 fi } diff --git a/usr/local/share/bastille/import.sh b/usr/local/share/bastille/import.sh index 4a773525..e0b56437 100644 --- a/usr/local/share/bastille/import.sh +++ b/usr/local/share/bastille/import.sh @@ -32,7 +32,11 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille import file [option]" + error_exit "Usage: bastille import FILE [option] + \n + \nOptions: + \n + -f|--force -- Force an archive import even if the checksum file is missing or don't match\n" } # Handle special-case commands first @@ -47,8 +51,21 @@ if [ $# -gt 2 ] || [ $# -lt 1 ]; then fi TARGET="${1}" -OPTION="${2}" shift +OPT_FORCE= + +# Handle and parse option args +while [ $# -gt 0 ]; do + case "${1}" in + -f|force|--force) + OPT_FORCE="1" + shift + ;; + *) + usage + ;; + esac +done validate_archive() { # Compare checksums on the target archive @@ -66,7 +83,7 @@ validate_archive() { fi else # Check if user opt to force import - if [ "${OPTION}" = "-f" -o "${OPTION}" = "force" ]; then + if [ -n "${OPT_FORCE}" ]; then warn "Warning: Skipping archive validation!" else error_exit "Checksum file not found. See 'bastille import TARGET -f'." @@ -403,6 +420,17 @@ jail_import() { else update_config fi + elif [ -z "${FILE_EXT}" ]; then + if echo "${TARGET}" | grep -q '_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}$'; then + # Based on the file name, looks like we are importing a raw bastille image + # Import from uncompressed image file + info "Importing '${TARGET_TRIM}' from uncompressed image archive." + info "Receiving ZFS data stream..." + zfs receive -u "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" < "${bastille_backupsdir}/${TARGET}" + + # Update ZFS mountpoint property if required + update_zfsmount + fi else error_exit "Unknown archive format." fi @@ -465,9 +493,9 @@ fi # Check if archive exist then trim archive name if [ -f "${bastille_backupsdir}/${TARGET}" ]; then # Filter unsupported/unknown archives - if echo "${TARGET}" | grep -q '_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.xz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.txz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}.zip$\|-[0-9]\{12\}.[0-9]\{2\}.tar.gz$\|@[0-9]\{12\}.[0-9]\{2\}.tar$'; then + if echo "${TARGET}" | grep -q '_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.xz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.txz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}.zip$\|-[0-9]\{12\}.[0-9]\{2\}.tar.gz$\|@[0-9]\{12\}.[0-9]\{2\}.tar$'; then if ls "${bastille_backupsdir}" | awk "/^${TARGET}$/" >/dev/null; then - TARGET_TRIM=$(echo "${TARGET}" | sed "s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.xz//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.txz//;s/_[0-9]*-[0-9]*-[0-9]*.zip//;s/-[0-9]\{12\}.[0-9]\{2\}.tar.gz//;s/@[0-9]\{12\}.[0-9]\{2\}.tar//") + TARGET_TRIM=$(echo "${TARGET}" | sed "s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.xz//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.txz//;s/_[0-9]*-[0-9]*-[0-9]*.zip//;s/-[0-9]\{12\}.[0-9]\{2\}.tar.gz//;s/@[0-9]\{12\}.[0-9]\{2\}.tar//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*//") fi else error_exit "Unrecognized archive name."