1#!/bin/sh
2#
3# Copyright (c) 2018-2021, Christer Edwards <christer.edwards@gmail.com>
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are met:
8#
9# * Redistributions of source code must retain the above copyright notice, this
10#   list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright notice,
13#   this list of conditions and the following disclaimer in the documentation
14#   and/or other materials provided with the distribution.
15#
16# * Neither the name of the copyright holder nor the names of its
17#   contributors may be used to endorse or promote products derived from
18#   this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31. /usr/local/share/bastille/common.sh
32. /usr/local/etc/bastille/bastille.conf
33
34usage() {
35    # Build an independent usage for the import command
36    # If no file/extension specified, will import from standard input
37    error_notify "Usage: bastille import [option(s)] FILE"
38
39    cat << EOF
40    Options:
41
42    -f | --force    -- Force an archive import regardless if the checksum file does not match or missing.
43    -v | --verbose  -- Be more verbose during the ZFS receive operation.
44
45Tip: If no option specified, container should be imported from standard input.
46
47EOF
48    exit 1
49}
50
51# Handle special-case commands first
52case "$1" in
53help|-h|--help)
54    usage
55    ;;
56esac
57
58if [ $# -gt 3 ] || [ $# -lt 1 ]; then
59    usage
60fi
61
62TARGET="${1}"
63OPT_FORCE=
64USER_IMPORT=
65OPT_ZRECV="-u"
66
67# Handle and parse option args
68while [ $# -gt 0 ]; do
69    case "${1}" in
70        -f|--force)
71            OPT_FORCE="1"
72            TARGET="${2}"
73            shift
74            ;;
75        -v|--verbose)
76            OPT_ZRECV="-u -v"
77            TARGET="${2}"
78            shift
79            ;;
80        -*|--*)
81            error_notify "Unknown Option."
82            usage
83            ;;
84        *)
85            if [ $# -gt 1 ] || [ $# -lt 1 ]; then
86                usage
87            fi
88            shift
89            ;;
90    esac
91done
92
93# Fallback to default if missing config parameters
94if [ -z "${bastille_decompress_xz_options}" ]; then
95    bastille_decompress_xz_options="-c -d -v"
96fi
97if [ -z "${bastille_decompress_gz_options}" ]; then
98    bastille_decompress_gz_options="-k -d -c -v"
99fi
100
101validate_archive() {
102    # Compare checksums on the target archive
103    # Skip validation for unsupported archive
104    if [ -f "${bastille_backupsdir}/${TARGET}" ]; then
105        if [ -f "${bastille_backupsdir}/${FILE_TRIM}.sha256" ]; then
106            info "Validating file: ${TARGET}..."
107            SHA256_DIST=$(cat "${bastille_backupsdir}/${FILE_TRIM}.sha256")
108            SHA256_FILE=$(sha256 -q "${bastille_backupsdir}/${TARGET}")
109            if [ "${SHA256_FILE}" != "${SHA256_DIST}" ]; then
110                error_exit "Failed validation for ${TARGET}."
111            else
112                info "File validation successful!"
113            fi
114        else
115            # Check if user opt to force import
116            if [ -n "${OPT_FORCE}" ]; then
117                warn "Warning: Skipping archive validation!"
118            else
119                error_exit "Checksum file not found. See 'bastille import [option(s)] FILE'."
120            fi
121        fi
122    fi
123}
124
125update_zfsmount() {
126    # Update the mountpoint property on the received ZFS data stream
127    OLD_ZFS_MOUNTPOINT=$(zfs get -H mountpoint "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}/root" | awk '{print $3}')
128    NEW_ZFS_MOUNTPOINT="${bastille_jailsdir}/${TARGET_TRIM}/root"
129    if [ "${NEW_ZFS_MOUNTPOINT}" != "${OLD_ZFS_MOUNTPOINT}" ]; then
130        info "Updating ZFS mountpoint..."
131        zfs set mountpoint="${bastille_jailsdir}/${TARGET_TRIM}/root" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}/root"
132    fi
133
134    # Mount new container ZFS datasets
135    if ! zfs mount | grep -qw "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}$"; then
136        zfs mount "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}"
137    fi
138    if ! zfs mount | grep -qw "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}/root$"; then
139        zfs mount "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}/root"
140    fi
141}
142
143update_jailconf() {
144    # Update jail.conf paths
145    JAIL_CONFIG="${bastille_jailsdir}/${TARGET_TRIM}/jail.conf"
146    if [ -f "${JAIL_CONFIG}" ]; then
147        if ! grep -qw "path = ${bastille_jailsdir}/${TARGET_TRIM}/root;" "${JAIL_CONFIG}"; then
148            info "Updating jail.conf..."
149            sed -i '' "s|exec.consolelog.*=.*;|exec.consolelog = ${bastille_logsdir}/${TARGET_TRIM}_console.log;|" "${JAIL_CONFIG}"
150            sed -i '' "s|path.*=.*;|path = ${bastille_jailsdir}/${TARGET_TRIM}/root;|" "${JAIL_CONFIG}"
151            sed -i '' "s|mount.fstab.*=.*;|mount.fstab = ${bastille_jailsdir}/${TARGET_TRIM}/fstab;|" "${JAIL_CONFIG}"
152        fi
153    fi
154}
155
156update_fstab() {
157    # Update fstab .bastille mountpoint on thin containers only
158    # Set some variables
159    FSTAB_CONFIG="${bastille_jailsdir}/${TARGET_TRIM}/fstab"
160    FSTAB_RELEASE=$(grep -owE '([1-9]{2,2})\.[0-9](-RELEASE|-RELEASE-i386|-RC[1-2])|([0-9]{1,2}-stable-build-[0-9]{1,3})|(current-build)-([0-9]{1,3})|(current-BUILD-LATEST)|([0-9]{1,2}-stable-BUILD-LATEST)|(current-BUILD-LATEST)' "${FSTAB_CONFIG}")
161    FSTAB_CURRENT=$(grep -w ".*/releases/.*/jails/${TARGET_TRIM}/root/.bastille" "${FSTAB_CONFIG}")
162    FSTAB_NEWCONF="${bastille_releasesdir}/${FSTAB_RELEASE} ${bastille_jailsdir}/${TARGET_TRIM}/root/.bastille nullfs ro 0 0"
163    if [ -n "${FSTAB_CURRENT}" ] && [ -n "${FSTAB_NEWCONF}" ]; then
164        # If both variables are set, compare and update as needed
165        if ! grep -qw "${bastille_releasesdir}/${FSTAB_RELEASE}.*${bastille_jailsdir}/${TARGET_TRIM}/root/.bastille" "${FSTAB_CONFIG}"; then
166            info "Updating fstab..."
167            sed -i '' "s|${FSTAB_CURRENT}|${FSTAB_NEWCONF}|" "${FSTAB_CONFIG}"
168        fi
169    fi
170}
171
172generate_config() {
173    # Attempt to read previous config file and set required variables accordingly
174    # If we can't get a valid interface, fallback to lo1 and warn user
175    info "Generating jail.conf..."
176    DEVFS_RULESET=4
177
178    if [ "${FILE_EXT}" = ".zip" ]; then
179        # Gather some bits from foreign/iocage config files
180        JSON_CONFIG="${bastille_jailsdir}/${TARGET_TRIM}/config.json"
181        if [ -n "${JSON_CONFIG}" ]; then
182            IPV4_CONFIG=$(grep -wo '\"ip4_addr\": \".*\"' "${JSON_CONFIG}" | tr -d '" ' | sed 's/ip4_addr://')
183            IPV6_CONFIG=$(grep -wo '\"ip6_addr\": \".*\"' "${JSON_CONFIG}" | tr -d '" ' | sed 's/ip6_addr://')
184	    DEVFS_RULESET=$(grep -wo '\"devfs_ruleset\": \".*\"' "${JSON_CONFIG}" | tr -d '" ' | sed 's/devfs_ruleset://')
185	    DEVFS_RULESET=${DEVFS_RULESET:-4}
186        fi
187    elif [ "${FILE_EXT}" = ".tar.gz" ]; then
188        # Gather some bits from foreign/ezjail config files
189        PROP_CONFIG="${bastille_jailsdir}/${TARGET_TRIM}/prop.ezjail-${FILE_TRIM}-*"
190        if [ -n "${PROP_CONFIG}" ]; then
191            IPVX_CONFIG=$(grep -wo "jail_${TARGET_TRIM}_ip=.*" ${PROP_CONFIG} | tr -d '" ' | sed "s/jail_${TARGET_TRIM}_ip=//")
192        fi
193    fi
194
195    # If there are multiple IP/NIC let the user configure network
196    if [ -n "${IPV4_CONFIG}" ]; then
197        if ! echo "${IPV4_CONFIG}" | grep -q '.*,.*'; then
198            NETIF_CONFIG=$(echo "${IPV4_CONFIG}" | grep '.*|' | sed 's/|.*//g')
199            if [ -z "${NETIF_CONFIG}" ]; then
200                config_netif
201            fi
202            IPX_ADDR="ip4.addr"
203            IP_CONFIG="${IPV4_CONFIG}"
204            IP6_MODE="disable"
205        fi
206    elif [ -n "${IPV6_CONFIG}" ]; then
207        if ! echo "${IPV6_CONFIG}" | grep -q '.*,.*'; then
208            NETIF_CONFIG=$(echo "${IPV6_CONFIG}" | grep '.*|' | sed 's/|.*//g')
209            if [ -z "${NETIF_CONFIG}" ]; then
210                config_netif
211            fi
212            IPX_ADDR="ip6.addr"
213            IP_CONFIG="${IPV6_CONFIG}"
214            IP6_MODE="new"
215        fi
216    elif [ -n "${IPVX_CONFIG}" ]; then
217        if ! echo "${IPVX_CONFIG}" | grep -q '.*,.*'; then
218            NETIF_CONFIG=$(echo "${IPVX_CONFIG}" | grep '.*|' | sed 's/|.*//g')
219            if [ -z "${NETIF_CONFIG}" ]; then
220                config_netif
221            fi
222            IPX_ADDR="ip4.addr"
223            IP_CONFIG="${IPVX_CONFIG}"
224            IP6_MODE="disable"
225            if echo "${IPVX_CONFIG}" | sed 's/.*|//' | grep -Eq '^(([a-fA-F0-9:]+$)|([a-fA-F0-9:]+\/[0-9]{1,3}$))'; then
226                IPX_ADDR="ip6.addr"
227                IP6_MODE="new"
228            fi
229        fi
230    fi
231
232    # Let the user configure network manually
233    if [ -z "${NETIF_CONFIG}" ]; then
234        NETIF_CONFIG="lo1"
235        IPX_ADDR="ip4.addr"
236        IP_CONFIG="-"
237        IP6_MODE="disable"
238        warn "Warning: See 'bastille edit ${TARGET_TRIM} jail.conf' for manual network configuration."
239    fi
240
241    if [ "${FILE_EXT}" = ".tar.gz" ]; then
242        CONFIG_RELEASE=$(echo ${PROP_CONFIG} | grep -o '[0-9]\{2\}\.[0-9]_RELEASE' | sed 's/_/-/g')
243        if [ -z "${CONFIG_RELEASE}" ]; then
244            # Fallback to host version
245            CONFIG_RELEASE=$(freebsd-version | sed 's/\-[pP].*//')
246            warn "Warning: ${CONFIG_RELEASE} was set by default!"
247        fi
248        mkdir "${bastille_jailsdir}/${TARGET_TRIM}/root/.bastille"
249        echo "${bastille_releasesdir}/${CONFIG_RELEASE} ${bastille_jailsdir}/${TARGET_TRIM}/root/.bastille nullfs ro 0 0" \
250        >> "${bastille_jailsdir}/${TARGET_TRIM}/fstab"
251
252        # Work with the symlinks
253        cd "${bastille_jailsdir}/${TARGET_TRIM}/root"
254        update_symlinks
255    else
256        # Generate new empty fstab file
257        touch "${bastille_jailsdir}/${TARGET_TRIM}/fstab"
258    fi
259
260    # Generate a basic jail configuration file on foreign imports
261    cat << EOF > "${bastille_jailsdir}/${TARGET_TRIM}/jail.conf"
262${TARGET_TRIM} {
263  devfs_ruleset = ${DEVFS_RULESET};
264  enforce_statfs = 2;
265  exec.clean;
266  exec.consolelog = ${bastille_logsdir}/${TARGET_TRIM}_console.log;
267  exec.start = '/bin/sh /etc/rc';
268  exec.stop = '/bin/sh /etc/rc.shutdown';
269  host.hostname = ${TARGET_TRIM};
270  mount.devfs;
271  mount.fstab = ${bastille_jailsdir}/${TARGET_TRIM}/fstab;
272  path = ${bastille_jailsdir}/${TARGET_TRIM}/root;
273  securelevel = 2;
274
275  interface = ${NETIF_CONFIG};
276  ${IPX_ADDR} = ${IP_CONFIG};
277  ip6 = ${IP6_MODE};
278}
279EOF
280}
281
282update_config() {
283    # Update an existing jail configuration
284    # The config on select archives does not provide a clear way to determine
285    # the base release, so lets try to get it from the base/COPYRIGHT file,
286    # otherwise warn user and fallback to host system release
287    CONFIG_RELEASE=$(grep -wo 'releng/[0-9]\{2\}.[0-9]/COPYRIGHT' "${bastille_jailsdir}/${TARGET_TRIM}/root/COPYRIGHT" | sed 's|releng/||;s|/COPYRIGHT|-RELEASE|')
288    if [ -z "${CONFIG_RELEASE}" ]; then
289        # Fallback to host version
290        CONFIG_RELEASE=$(freebsd-version | sed 's/\-[pP].*//')
291        warn "Warning: ${CONFIG_RELEASE} was set by default!"
292    fi
293    mkdir "${bastille_jailsdir}/${TARGET_TRIM}/root/.bastille"
294    echo "${bastille_releasesdir}/${CONFIG_RELEASE} ${bastille_jailsdir}/${TARGET_TRIM}/root/.bastille nullfs ro 0 0" \
295    >> "${bastille_jailsdir}/${TARGET_TRIM}/fstab"
296
297    # Work with the symlinks
298    cd "${bastille_jailsdir}/${TARGET_TRIM}/root"
299    update_symlinks
300}
301
302workout_components() {
303    if [ "${FILE_EXT}" = ".tar" ]; then
304        # Workaround to determine the tarball path/components before extract(assumes path/jails/target)
305        JAIL_PATH=$(tar -tvf ${bastille_backupsdir}/${TARGET} | grep -wo "/.*/jails/${TARGET_TRIM}" | tail -n1)
306        JAIL_DIRS=$(echo ${JAIL_PATH} | grep -o '/' | wc -l)
307        DIRS_PLUS=$(expr ${JAIL_DIRS} + 1)
308
309        # Workaround to determine the jail.conf path before extract(assumes path/qjail.config/target)
310        JAIL_CONF=$(tar -tvf ${bastille_backupsdir}/${TARGET} | grep -wo "/.*/qjail.config/${TARGET_TRIM}")
311        CONF_TRIM=$(echo ${JAIL_CONF} | grep -o '/' | wc -l)
312    fi
313}
314
315config_netif() {
316    # Get interface from bastille configuration
317    if [ -n "${bastille_network_loopback}" ]; then
318        NETIF_CONFIG="${bastille_network_loopback}"
319    elif [ -n "${bastille_network_shared}" ]; then
320        NETIF_CONFIG="${bastille_network_shared}"
321    else
322        NETIF_CONFIG=
323    fi
324}
325
326update_symlinks() {
327    # Work with the symlinks
328    SYMLINKS="bin boot lib libexec rescue sbin usr/bin usr/include usr/lib usr/lib32 usr/libdata usr/libexec usr/ports usr/sbin usr/share usr/src"
329
330    # Just warn user to bootstrap the release if missing
331    if [ ! -d "${bastille_releasesdir}/${CONFIG_RELEASE}" ]; then
332        warn "Warning: ${CONFIG_RELEASE} must be bootstrapped. See 'bastille bootstrap'."
333    fi
334
335    # Update old symlinks
336    info "Updating symlinks..."
337    for _link in ${SYMLINKS}; do
338        if [ -L "${_link}" ]; then
339            ln -sf /.bastille/${_link} ${_link}
340        fi
341    done
342}
343
344create_zfs_datasets() {
345    # Prepare the ZFS environment and restore from file
346    info "Importing '${TARGET_TRIM}' from foreign compressed ${FILE_EXT} archive."
347    info "Preparing ZFS environment..."
348
349    # Create required ZFS datasets, mountpoint inherited from system
350    zfs create ${bastille_zfs_options} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}"
351    zfs create ${bastille_zfs_options} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}/root"
352}
353
354remove_zfs_datasets() {
355    # Perform cleanup on failure
356    zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}/root"
357    zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}"
358    error_exit "Failed to extract files from '${TARGET}' archive."
359}
360
361jail_import() {
362    # Attempt to import container from file
363    FILE_TRIM=$(echo "${TARGET}" | sed 's/\.xz//g;s/\.gz//g;s/\.tgz//g;s/\.txz//g;s/\.zip//g;s/\.tar\.gz//g;s/\.tar//g')
364    FILE_EXT=$(echo "${TARGET}" | sed "s/${FILE_TRIM}//g")
365    if [ -d "${bastille_jailsdir}" ]; then
366        if [ "${bastille_zfs_enable}" = "YES" ]; then
367            if [ -n "${bastille_zfs_zpool}" ]; then
368                if [ "${FILE_EXT}" = ".xz" ]; then
369                    validate_archive
370                    # Import from compressed xz on ZFS systems
371                    info "Importing '${TARGET_TRIM}' from compressed ${FILE_EXT} image."
372                    info "Receiving ZFS data stream..."
373                    xz ${bastille_decompress_xz_options} "${bastille_backupsdir}/${TARGET}" | \
374                    zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}"
375
376                    # Update ZFS mountpoint property if required
377                    update_zfsmount
378                elif [ "${FILE_EXT}" = ".gz" ]; then
379                    validate_archive
380                    # Import from compressed xz on ZFS systems
381                    info "Importing '${TARGET_TRIM}' from compressed ${FILE_EXT} image."
382                    info "Receiving ZFS data stream..."
383                    gzip ${bastille_decompress_gz_options} "${bastille_backupsdir}/${TARGET}" | \
384                    zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}"
385
386                    # Update ZFS mountpoint property if required
387                    update_zfsmount
388
389                elif [ "${FILE_EXT}" = ".txz" ]; then
390                    validate_archive
391                    # Prepare the ZFS environment and restore from existing .txz file
392                    create_zfs_datasets
393
394                    # Extract required files to the new datasets
395                    info "Extracting files from '${TARGET}' archive..."
396                    tar --exclude='root' -Jxf "${bastille_backupsdir}/${TARGET}" --strip-components 1 -C "${bastille_jailsdir}/${TARGET_TRIM}"
397                    tar -Jxf "${bastille_backupsdir}/${TARGET}" --strip-components 2 -C "${bastille_jailsdir}/${TARGET_TRIM}/root" "${TARGET_TRIM}/root"
398                    if [ "$?" -ne 0 ]; then
399                        remove_zfs_datasets
400                    fi
401                elif [ "${FILE_EXT}" = ".tgz" ]; then
402                    validate_archive
403                    # Prepare the ZFS environment and restore from existing .tgz file
404                    create_zfs_datasets
405
406                    # Extract required files to the new datasets
407                    info "Extracting files from '${TARGET}' archive..."
408                    tar --exclude='root' -xf "${bastille_backupsdir}/${TARGET}" --strip-components 1 -C "${bastille_jailsdir}/${TARGET_TRIM}"
409                    tar -xf "${bastille_backupsdir}/${TARGET}" --strip-components 2 -C "${bastille_jailsdir}/${TARGET_TRIM}/root" "${TARGET_TRIM}/root"
410                    if [ "$?" -ne 0 ]; then
411                        remove_zfs_datasets
412                    fi
413                elif [ "${FILE_EXT}" = ".zip" ]; then
414                    validate_archive
415                    # Attempt to import a foreign/iocage container
416                    info "Importing '${TARGET_TRIM}' from foreign compressed ${FILE_EXT} archive."
417                    # Sane bastille ZFS options
418                    ZFS_OPTIONS=$(echo ${bastille_zfs_options} | sed 's/-o//g')
419
420                    # Extract required files from the zip archive
421                    cd "${bastille_backupsdir}" && unzip -j "${TARGET}"
422                    if [ "$?" -ne 0 ]; then
423                        error_exit "Failed to extract files from '${TARGET}' archive."
424                        rm -f "${FILE_TRIM}" "${FILE_TRIM}_root"
425                    fi
426                    info "Receiving ZFS data stream..."
427                    zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" < "${FILE_TRIM}"
428                    zfs set ${ZFS_OPTIONS} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}"
429                    zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}/root" < "${FILE_TRIM}_root"
430
431                    # Update ZFS mountpoint property if required
432                    update_zfsmount
433
434                    # Keep old configuration files for user reference
435                    if [ -f "${bastille_jailsdir}/${TARGET_TRIM}/fstab" ]; then
436                        mv "${bastille_jailsdir}/${TARGET_TRIM}/fstab" "${bastille_jailsdir}/${TARGET_TRIM}/fstab.old"
437                    fi
438
439                    # Cleanup unwanted files
440                    rm -f "${FILE_TRIM}" "${FILE_TRIM}_root"
441
442                    # Generate fstab and jail.conf files
443                    generate_config
444                elif [ "${FILE_EXT}" = ".tar.gz" ]; then
445                    # Attempt to import a foreign/ezjail container
446                    # Prepare the ZFS environment and restore from existing .tar.gz file
447                    create_zfs_datasets
448
449                    # Extract required files to the new datasets
450                    info "Extracting files from '${TARGET}' archive..."
451                    tar --exclude='ezjail/' -xf "${bastille_backupsdir}/${TARGET}" -C "${bastille_jailsdir}/${TARGET_TRIM}"
452                    tar -xf "${bastille_backupsdir}/${TARGET}" --strip-components 1 -C "${bastille_jailsdir}/${TARGET_TRIM}/root"
453                    if [ "$?" -ne 0 ]; then
454                        remove_zfs_datasets
455                    else
456                        generate_config
457                    fi
458                elif [ "${FILE_EXT}" = ".tar" ]; then
459                    # Attempt to import a foreign/qjail container
460                    # Prepare the ZFS environment and restore from existing .tar file
461                    create_zfs_datasets
462                    workout_components
463
464                    # Extract required files to the new datasets
465                    info "Extracting files from '${TARGET}' archive..."
466                    tar -xf "${bastille_backupsdir}/${TARGET}" --strip-components "${CONF_TRIM}" -C "${bastille_jailsdir}/${TARGET_TRIM}" "${JAIL_CONF}"
467                    tar -xf "${bastille_backupsdir}/${TARGET}" --strip-components "${DIRS_PLUS}" -C "${bastille_jailsdir}/${TARGET_TRIM}/root" "${JAIL_PATH}"
468                    if [ -f "${bastille_jailsdir}/${TARGET_TRIM}/${TARGET_TRIM}" ]; then
469                        mv "${bastille_jailsdir}/${TARGET_TRIM}/${TARGET_TRIM}" "${bastille_jailsdir}/${TARGET_TRIM}/jail.conf"
470                    fi
471
472                    if [ "$?" -ne 0 ]; then
473                        remove_zfs_datasets
474                    else
475                        update_config
476                    fi
477                elif [ -z "${FILE_EXT}" ]; then
478                    if echo "${TARGET}" | grep -q '_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}$'; then
479                        validate_archive
480                        # Based on the file name, looks like we are importing a raw bastille image
481                        # Import from uncompressed image file
482                        info "Importing '${TARGET_TRIM}' from uncompressed image archive."
483                        info "Receiving ZFS data stream..."
484                        zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" < "${bastille_backupsdir}/${TARGET}"
485
486                        # Update ZFS mountpoint property if required
487                        update_zfsmount
488                    else
489                        # Based on the file name, looks like we are importing from previous redirected bastille image
490                        # Quietly import from previous redirected bastille image
491                        if ! zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}"; then
492                            exit 1
493                        else
494                            # Update ZFS mountpoint property if required
495                            update_zfsmount
496                        fi
497                    fi
498                else
499                    error_exit "Unknown archive format."
500                fi
501            fi
502        else
503            # Import from standard supported archives on UFS systems
504            if [ "${FILE_EXT}" = ".txz" ]; then
505                info "Extracting files from '${TARGET}' archive..."
506                tar -Jxf  "${bastille_backupsdir}/${TARGET}" -C "${bastille_jailsdir}"
507            elif [ "${FILE_EXT}" = ".tgz" ]; then
508                info "Extracting files from '${TARGET}' archive..."
509                tar -xf  "${bastille_backupsdir}/${TARGET}" -C "${bastille_jailsdir}"
510            elif [ "${FILE_EXT}" = ".tar.gz" ]; then
511                # Attempt to import/configure foreign/ezjail container
512                info "Extracting files from '${TARGET}' archive..."
513                mkdir "${bastille_jailsdir}/${TARGET_TRIM}"
514                tar -xf "${bastille_backupsdir}/${TARGET}" -C "${bastille_jailsdir}/${TARGET_TRIM}"
515                mv "${bastille_jailsdir}/${TARGET_TRIM}/ezjail" "${bastille_jailsdir}/${TARGET_TRIM}/root"
516                generate_config
517            elif [ "${FILE_EXT}" = ".tar" ]; then
518                # Attempt to import/configure foreign/qjail container
519                info "Extracting files from '${TARGET}' archive..."
520                mkdir -p "${bastille_jailsdir}/${TARGET_TRIM}/root"
521                workout_components
522                tar -xf "${bastille_backupsdir}/${TARGET}" --strip-components "${CONF_TRIM}" -C "${bastille_jailsdir}/${TARGET_TRIM}" "${JAIL_CONF}"
523                tar -xf "${bastille_backupsdir}/${TARGET}" --strip-components "${DIRS_PLUS}" -C "${bastille_jailsdir}/${TARGET_TRIM}/root" "${JAIL_PATH}"
524                if [ -f "${bastille_jailsdir}/${TARGET_TRIM}/${TARGET_TRIM}" ]; then
525                    mv "${bastille_jailsdir}/${TARGET_TRIM}/${TARGET_TRIM}" "${bastille_jailsdir}/${TARGET_TRIM}/jail.conf"
526                fi
527                update_config
528            else
529                error_exit "Unsupported archive format."
530            fi
531        fi
532
533        if [ "$?" -ne 0 ]; then
534            error_exit "Failed to import from '${TARGET}' archive."
535        else
536            # Update the jail.conf and fstab if required
537            # This is required on foreign imports only
538            update_jailconf
539            update_fstab
540            if [ -z "${USER_IMPORT}" ]; then
541                info "Container '${TARGET_TRIM}' imported successfully."
542            fi
543            exit 0
544        fi
545    else
546        error_exit "Jails directory/dataset does not exist. See 'bastille bootstrap'."
547    fi
548}
549
550# Check for user specified file location
551if echo "${TARGET}" | grep -q '\/'; then
552    GETDIR="${TARGET}"
553    TARGET=$(echo ${TARGET} | awk -F '\/' '{print $NF}')
554    bastille_backupsdir=$(echo ${GETDIR} | sed "s/${TARGET}//")
555fi
556
557# Check if backups directory/dataset exist
558if [ ! -d "${bastille_backupsdir}" ]; then
559    error_exit "Backups directory/dataset does not exist. See 'bastille bootstrap'."
560fi
561
562# Check if archive exist then trim archive name
563if [ -f "${bastille_backupsdir}/${TARGET}" ]; then
564    # Filter unsupported/unknown archives
565    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\}$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.gz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.tgz$\|_[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
566        if ls "${bastille_backupsdir}" | awk "/^${TARGET}$/" >/dev/null; then
567            TARGET_TRIM=$(echo "${TARGET}" | sed "s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.xz//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.gz//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.tgz//;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]*//")
568        fi
569    else
570        error_exit "Unrecognized archive name."
571    fi
572else
573    if echo "${TARGET}" | grep -q '_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.*$'; then
574        error_exit "Archive '${TARGET}' not found."
575    else
576        # Assume user will import from standard input
577        TARGET_TRIM=${TARGET}
578        USER_IMPORT="1"
579    fi
580fi
581
582# Check if a running jail matches name or already exist
583if [ -n "$(/usr/sbin/jls name | awk "/^${TARGET_TRIM}$/")" ]; then
584    error_exit "A running jail matches name."
585elif [ -n "${TARGET_TRIM}" ]; then
586    if [ -d "${bastille_jailsdir}/${TARGET_TRIM}" ]; then
587        error_exit "Container: ${TARGET_TRIM} already exists."
588    fi
589fi
590
591if [ -n "${TARGET}" ]; then
592    jail_import
593fi
594