Wątek przeniesiony 2021-08-07 19:33 z Inne języki programowania przez cerrato.

Prośba o ocenę skryptu

0

Witajcie. Napisałem ostatnio sobie skrypcik w bashu do tworzenia datasetów i pooli zfs. Mam wrażenie że mój skrypt to czysty mess. Uczę się dopiero pisać w bashu, czerpię wiedzę z kursów videopoint. Bardzo mi zależy na solidnym code review skryptu, nie boję się krytyki ;-)

Kod

#!/usr/bin/env bash

set -o errexit
# set -o pipefail
set -o nounset
# set -o xtrace

die() {
    local msg=$1
    printf "Error: %s. Exiting...\n" "$msg" >&2
    exit 1
}

log_info() {
    local msg=$1
    printf "[INFO] %s\n" "$msg"
}

require_binary() {
    local binaries=( "$@" )
    for binary in ${binaries[*]}; do
        if ! command -v "${binary}" &> /dev/null; then
            die "${binary} binary does not exist"
        fi
    done
}

require_binary "zpool" "zfs" "sgdisk"

readonly lockdir=/tmp/prepare_zfs.lock
if ! mkdir "$lockdir"; then
    die "Lock file already exists"
fi
trap 'stty echo; rm -rf "${lockdir}"; exit $?' EXIT

display_usage() {
    cat <<EOF
./prepare_zfs.sh - Setup ZFS layout
Usage:
  ./prepare_zfs.sh [ options ]
  ./prepare_zfs.sh --help
Options:

  Specific to the target zfs_on_root:
  --boot-mode legacy_bios|uefi (default: legacy_bios) - specify a boot mode
  --distro|--distribution debian|gentoo|linux (default: linux) - specify a target linux distribution
  --gnome - create gnome datasets
  --libvirt - create libvirt datasets
  --lxc - create lxc datasets
  --docker - create docker datasets
  --nfs - create nfs datasets
  --webserver - create datasets for webservers
  --mailserver - create datasets for mailservers
  --snap - create datasets for snap packages
  --systemd - create datasets for systemd
  --rpool-name - specify a rootfs pool name
  --bpool-name - specify a boot pool name
  --zfs-swap - enable zfs swap partition
  --swap-hibernate - enable if swap hibernation is used
  -u|--username value (default: user) - specify username
  
  Specific to the target home_dir:
  -u|--username value (defaukt: user) - specify username

  Target independent:
  --ashift value (default: optimal, calculated by script) - overrides default ashift value
  --raid none|raid0|raid1 (default: none) - specify raid type
  --device|--devices paths - specify target devices
  -m|--mnt path (default: /mnt) - specify ZFS pool mount path
  --encrypt-zfs - specify if you want to encrypt ZFS pool
  --key-type password|keyfile - encryption key type
  --key-path path - path to the encryption keyfile
  -c|--config path (default: ./zfs_layout.conf) - specify path to the config file
  -t|--target data|home_dir|zfs_on_root - specify the target
  -i|--interactive - enter into interactive mode
  -h|--help - displays help

EOF
    exit 0
}

while (( $# )); do
    opt=$1
    case $opt in
        --ashift)
            ZFS_POOL_ASHIFT=$2
            shift 2
            ;;
        --boot-mode)
            BOOT_MODE=$2
            shift 2
            ;;
        --distro|--distribution)
            TARGET_DISTRO=$2
            shift 2
            ;;
        --gnome)
            ZFS_DATASET_GNOME="y"
            shift 1
            ;;
        --libvirt)
            ZFS_DATASET_LIBVIRT="y"
            shift 1
            ;;
        --lxc)
            ZFS_DATASET_LXC="y"
            shift 1
            ;;
        --docker)
            ZFS_DATASET_DOCKER="y"
            shift 1
            ;;
        --nfs)
            ZFS_DATASET_NFS="y"
            shift 1
            ;;
        --webserver)
            ZFS_DATASET_WEBSERVER="y"
            shift 1
            ;;
        --mailserver)
            ZFS_DATASET_MAILSERVER="y"
            shift 1
            ;;
        --snap)
            ZFS_DATASET_SNAP="y"
            shift 1
            ;;
        --systemd)
            ZFS_DATASET_SYSTEMD="y"
            shift 1
            ;;
        --encrypt-zfs)
            ZFS_ENC_ENABLED="y"
            shift 1
            ;;
        --key-type)
            ZFS_ENC_TYPE="$2"
            shift 2
            ;;
        --key-path)
            ZFS_ENC_KEYPATH="$2"
            shift 2
            ;;
        --rpool-name)
            ZFS_RPOOL="$2"
            shift 2
            ;;
        --bpool-name)
            ZFS_BPOOL="$2"
            shift 2
            ;;
        --pool-name)
            ZFS_POOL="$2"
            shift 2
            ;;
        --zfs-swap)
            ZFS_CREATE_SWAP="y"
            shift 1
            ;;
        --swap-hibernate)
            SWAP_HIBERNATE="y"
            shift 1
            ;;
        -t|--target)
            ZFS_TARGET="$2"
            shift 2
            ;;
        --raid)
            ZFS_RAID_TYPE="$2"
            shift 2
            ;;
        -u|--username)
            ZFS_DATASET_USER="$2"
            shift 2
            ;;
        --device|--devices)
            TARGET_DEV="$2"
            shift 2
            ;;
        -m|--mnt)
            MNT_PATH="$2"
            shift 2
            ;;
        -c|--config)
            CONFIG_FILE="$2"
            shift 2
            ;;
        -i|--interactive)
            INTERACTIVE="y"
            shift 1
            ;;
        -h|--help)
            display_usage
            shift 1
            ;;
        *)
            printf "./prepare_zfs.sh: error: unrecognized arguments: %s" "$@"
            display_usage
            exit 1
            ;;
    esac
done

ZFS_POOL_ASHIFT=${ZFS_POOL_ASHIFT:-}
BOOT_MODE=${BOOT_MODE:-"legacy_bios"}
TARGET_DISTRO=${TARGET_DISTRO:-"linux"}
ZFS_DATASET_GNOME=${ZFS_DATASET_GNOME:-}
ZFS_DATASET_LIBVIRT=${ZFS_DATASET_LIBVIRT:-}
ZFS_DATASET_LXC=${ZFS_DATASET_LXC:-}
ZFS_DATASET_DOCKER=${ZFS_DATASET_DOCKER:-}
ZFS_DATASET_NFS=${ZFS_DATASET_NFS:-}
ZFS_DATASET_WEBSERVER=${ZFS_DATASET_WEBSERVER:-}
ZFS_DATASET_MAILSERVER=${ZFS_DATASET_MAILSERVER:-}
ZFS_DATASET_SNAP=${ZFS_DATASET_SNAP:-}
ZFS_DATASET_SYSTEMD=${ZFS_DATASET_SYSTEMD:-}
ZFS_ENC_ENABLED=${ZFS_ENC_ENABLED:-}
ZFS_ENC_TYPE=${ZFS_ENC_TYPE:-}
ZFS_ENC_KEYPATH=${ZFS_ENC_KEYPATH:-}
ZFS_RPOOL=${ZFS_RPOOL:-"rpool"}
ZFS_BPOOL=${ZFS_BPOOL:-"bpool"}
ZFS_POOL=${ZFS_POOL:-}
ZFS_CREATE_SWAP=${ZFS_CREATE_SWAP:-}
SWAP_HIBERNATE=${SWAP_HIBERNATE:-}
ZFS_TARGET=${ZFS_TARGET:-}
ZFS_RAID_TYPE=${ZFS_RAID_TYPE:-"none"}
TARGET_DEV=${TARGET_DEV:-}
MNT_PATH=${MNT_PATH:-"/mnt"}
ZFS_DATASET_USER=${ZFS_DATASET_USER:-"user"}
CONFIG_FILE=${CONFIG_FILE:-"./zfs_layout.conf"}
INTERACTIVE=${INTERACTIVE:-}

# shellcheck disable=SC1090
[[ -f "${CONFIG_FILE}" ]] && source "${CONFIG_FILE}"

yesno() {
    local question=$1
    local -n answer=$2
    while true; do
        read -rp "$question (answer y or n)? " answer
        case $answer in
            y) answer=1; return;;
            n) answer=""; return;;
            *) printf "Error: invalid answer, valid choices are y and n.\n" >&2 ;;
        esac
    done
}

read_string() {
    local question=$1
    local -n answer=$2
    local default=${3:-}
    if [[ $default ]]; then
        read -rp "$question (leave blank for $default): " answer
    else
        read -rp "$question: " answer
    fi
    answer=${answer:-$default}
}

read_num() {
    local question=$1
    local -n answer=$2
    local default=${3:-}
    if [[ $default ]]; then
        question="$question (leave blank for $default): "
    else
        question="$question: "
    fi
    local re='^[0-9]+$'
    until
        read -rp "$question" answer
        answer=${answer:-$default}
        [[ "$answer" =~ $re ]]
    do
        printf "%s: not a number.\n" "$answer" >&2
    done
}

read_device() {
    local question=$1
    local -n answer=$2
    local default=${3:-}
    if [[ $default ]]; then
        question="$question (leave blank for $default): "
    else
        question="$question: "
    fi
    until
        read -rp "$question" answer
        answer=${answer:-$default}
        [ -b "$answer" ]
    do
        printf "%s: not a device.\n" "$answer" >&2
    done
}

read_devices() {
    local question=$1
    local -n devices=$2
    local quit=false
    until $quit; do
        read_string "$question" devices
        for device in $devices; do
            if [ ! -b "$device" ]; then
                printf "%s: not a device.\n" "$device" >&2
            else
                quit=true
            fi
        done
    done
}

read_dir() {
    local question=$1
    local -n answer=$2
    local default=${3:-}
    if [[ $default ]]; then
        question="$question (leave blank for $default): "
    else
        question="$question: "
    fi
    until
        read -rp "$question" answer
        answer=${answer:-$default}
        [ -b "$answer}" ]
    do
        printf "%s: not a directory.\n" "$answer" >&2
    done
}

read_mnt() {
    local question=$1
    local -n answer=$2
    local default=${3:-}
    if [[ $default ]]; then
        question="$question (leave blank for $default): "
    else
        question="$question: "
    fi
    while
        read -rp "$question" answer
        answer=${answer:-$default}
        [ "$(findmnt -M "$answer")" ]
    do
        printf "%s: already mounted.\n" "$answer" >&2
    done
}

read_choice() {
    local question=$1
    local -n ans=$2
    local choices=( "$@" )
    choices=( "${choices[@]:2}" )
    while true; do
        printf "%s\n" "$question"
        for (( i = 0; i < ${#choices[@]}; ++i )); do
            printf "%d) %s\n" "$i" "${choices[$i]}"
        done
        read_num "Your choice" ans
        if [ "$ans" -ge 0 ] && [ "$ans" -lt ${#choices[@]} ]; then
            ans=${choices[$ans]}
            break
        else
            printf "Invalid choice!\n\n"
        fi
    done
}

read_password() {
    local -n answer=$1
    local passwd confirm_passwd
    until
        stty -echo
        read -rp "Password: " passwd
        printf "\n"
        read -rp "Confirm password: " confirm_passwd
        printf "\n"
        stty echo
        [ "$passwd" == "$confirm_passwd" ]
    do
        printf "Error: passwords don't match.\n" >&2
    done
    answer="${passwd}"
}

find_device_id() {
    local device=$1
    local result
    [[ ! -b ${device} ]] && die "${device}: not a block device"
    result=$(find /dev/disk/by-id -lname "*$(lsblk --noheadings --output NAME -nl ${device} | head -n1 | awk '{print $1;}')" -printf "%p\n" | grep -v "wwn")
    [[ -z "${result}" ]] && printf "%s" "${device}" || printf "%s" "${result}"
}

if [[ ${INTERACTIVE} ]]; then
    if [[ -z ${TARGET_DEV[*]} ]]; then
        if [[ ${ZFS_RAID_TYPE} != "none" ]]; then
            read_devices "Enter target devices separated by spaces" TARGET_DEV
        else
            read_device "Enter target device path" TARGET_DEV
        fi
    fi
    if [[ -z ${ZFS_TARGET} ]]; then
        choices=( "zfs_on_root" "home_dir" "data" )
        read_choice "Select target" ZFS_TARGET "${choices[@]}"
    fi
    [[ -z "${ZFS_ENC_ENABLED}" ]] && yesno "Do you want to use encryption" ZFS_ENC_ENABLED
    if [[ ${ZFS_TARGET} == "zfs_on_root" ]]; then
        if [[ -z ${BOOT_MODE} ]]; then
            choices=( "legacy_bios" "uefi" )
            read_choice "Select boot mode" BOOT_MODE "${choices[@]}"
        fi
        [[ -z "${MNT_PATH}" ]] && read_mnt "Enter mount path for rootfs" MNT_PATH "/mnt"
        [[ -z "${ZFS_BPOOL}" ]] && read_string "Enter ZFS boot pool name" ZFS_BPOOL "bpool"
        [[ -z "${ZFS_RPOOL}" ]] && read_string "Enter ZFS root pool name" ZFS_RPOOL "rpool"
        if [[ -z "${TARGET_DISTRO}" ]]; then
            choices=( "gentoo" "debian" "linux" )
            read_choice "Choose target operating system" TARGET_DISTRO "${choices[@]}"
        fi
        [[ -z "${ZFS_DATASET_GNOME}" ]] && yesno "Will you be using GNOME" ZFS_DATASET_GNOME
        [[ -z "${ZFS_DATASET_LIBVIRT}" ]] && yesno "Will you be using libvirt" ZFS_DATASET_LIBVIRT
        [[ -z "${ZFS_DATASET_LXC}" ]] && yesno "Will you be using LXC" ZFS_DATASET_LXC
        [[ -z "${ZFS_DATASET_DOCKER}" ]] && yesno "Will you be using Docker" ZFS_DATASET_DOCKER
        [[ -z "${ZFS_DATASET_NFS}" ]] && yesno "Will you be using NFS" ZFS_DATASET_NFS
        [[ -z "${ZFS_DATASET_WEBSERVER}" ]] && yesno "Will you be using web servers" ZFS_DATASET_WEBSERVER
        [[ -z "${ZFS_DATASET_MAILSERVER}" ]] && yesno "Will you be using mail servers" ZFS_DATASET_MAILSERVER
        [[ -z "${ZFS_DATASET_SNAP}" ]] && yesno "Will you be using snap packages" ZFS_DATASET_SNAP
        [[ -z "${ZFS_DATASET_SYSTEMD}" ]] && yesno "Will you be using systemd" ZFS_DATASET_SYSTEMD
        [[ -z "${ZFS_CREATE_SWAP}" ]] && yesno "Do you want to create swap dataset" ZFS_CREATE_SWAP
        [[ $ZFS_CREATE_SWAP ]] && yesno "Will you hibernate device" SWAP_HIBERNATE
        [[ -z "${ZFS_DATASET_USER}" ]] && read_string "Enter username" username "user"
        [[ -z "${ZFS_ENC_ENABLED}" ]] && yesno "Do you want to use encryption" ZFS_ENC_ENABLED
    fi
fi

IFS=" " read -r -a TARGET_DEV <<< "${TARGET_DEV}"

case ${BOOT_MODE} in
    legacy_bios|uefi)
        ;;
    *)
        die "unsupported boot mode ${BOOT_MODE}"
        ;;
esac

case ${TARGET_DISTRO} in
    gentoo|debian|linux)
        ;;
    *)
        die "unsupported operating system '${TARGET_DISTRO}'"
        ;;
esac

case ${ZFS_TARGET} in
    data|home_dir|zfs_on_root)
        ;;
    "")
        die "target not specified"
        ;;
    *)
        die "invalid target '${ZFS_TARGET}'"
        ;;
esac

case ${ZFS_RAID_TYPE} in
    none)
        [[ ${#TARGET_DEV[@]} -gt 1 ]] && die "too many devices, specified raid type is 'none'"
        ;;
    raid0|raid1)
        [[ ${#TARGET_DEV[@]} -lt 2 ]] && die "required at least 2 devices, not a raid"
        ;;
    *)
        die "invalid raid type $2"
        ;;
esac

# if [[ ${ZFS_TARGET} != "zfs_on_root" ]]; then
#     if [[ ${ZFS_DATASET_GNOME} || ${ZFS_DATASET_LIBVIRT} || ${ZFS_DATASET_LXC} || ${ZFS_DATASET_DOCKER} || ${ZFS_DATASET_NFS}\
#         || ${ZFS_DATASET_WEBSERVER} || ${ZFS_DATASET_MAILSERVER} || ${ZFS_DATASET_SNAP} || ${ZFS_DATASET_SYSTEMD} || ${ZFS_RPOOL}\
#         || ${ZFS_BPOOL} || ${ZFS_CREATE_SWAP} || ${SWAP_HIBERNATE} || ${ZFS_RAID_TYPE} ]]; then
#         die "${ZFS_TARGET} target contains zfs_on_root specific argument"
#     fi
# fi

if [[ ${ZFS_TARGET} == "data" && -n "${ZFS_DATASET_USER}" ]]; then
    die "${ZFS_TARGET} target cannot contain username argument"
fi

# if [[ ${ZFS_POOL} ]]; then
#     [[ ${ZFS_RPOOL} || ${ZFS_BPOOL} ]] && die "--bpool-name and --rpool-name arguments cannot be specified when --pool-name argument is specified"
# fi

for dev in "${TARGET_DEV[@]}"; do
    if [ ! -b "${dev}" ]; then
        die "${dev}: not a device"
    fi
done

if [[ ! -d ${MNT_PATH} ]]; then
    die "${MNT_PATH}: not a directory"
fi

if [[ "$(findmnt -M "${MNT_PATH}")" ]]; then
    die "${MNT_PATH}: already mounted"
fi

PASSWORD=""
if [[ ${ZFS_ENC_ENABLED} ]]; then
    choices=( "password" "keyfile" )
    [[ -z ${ZFS_ENC_TYPE} ]] && read_choice "Do you want to use password or generate keyfile?" ZFS_ENC_TYPE "${choices[@]}"
    case ${ZFS_ENC_TYPE} in
        "password")
            read_password PASSWORD
            ;;
        "keyfile")
            if [[ -z ${ZFS_ENC_KEYPATH} ]]; then
                read_string "Enter path for generated keyfile" ZFS_ENC_KEYPATH "/usr/local/etc/keys/${ZFS_POOL}.key"
            fi
            ;;
        **)
            die "invalid encryption type '${ZFS_ENC_TYPE}'"
            ;;
    esac
fi

for (( i = 0; i < ${#TARGET_DEV[@]}; ++i )); do
    TARGET_DEV[$i]=$(find_device_id "${TARGET_DEV[$i]}")
done

calculate_ashift() {
    local device=$1
    local zpool_ashift=""
    local sector_size
    sector_size=$(blockdev --getpbsz "${device}")
    case $sector_size in
        "512")
            zpool_ashift=9
            ;;
        "4096")
            zpool_ashift=12
            ;;
        "8192")
            zpool_ashift=13
            ;;
    esac
    [[ -z ${ZFS_POOL_ASHIFT} ]] && printf "%d" $zpool_ashift || printf "%d" "${ZFS_POOL_ASHIFT}"
}

calculate_swap_size() {
    local swap_size
    local total_memory
    total_memory=$(free -m | awk '/^Mem:/{print $2}')
    if [[ $total_memory -le 2048 ]]; then
        if [[ ${SWAP_HIBERNATE} ]]; then
            swap_size=$(( 3 * total_memory ))
        else
            swap_size=$(( 2 * total_memory ))
        fi
    elif [[ $total_memory -gt 2048 && $total_memory -le 8192 ]]; then
        if [[ ${SWAP_HIBERNATE} ]]; then
            swap_size=$(( 2 * total_memory ))
        else
            swap_size=$total_memory
        fi
    elif [[ $total_memory -gt 8192 && $total_memory -le 65536 ]]; then
        if [[ ${SWAP_HIBERNATE} ]]; then
            swap_size=$(echo "1.5 * $total_memory" | bc -l)
        else
            swap_size=4096
        fi
    else
        if [[ ${SWAP_HIBERNATE} ]]; then
            die "operation not supported"
        else
            swap_size=4096
        fi
    fi
    printf "%sM" $swap_size
}

create_partitions() {
    log_info "Creating partitions"
    for device in ${TARGET_DEV[*]}; do
        sgdisk --zap-all "$device"
        sgdisk -n 0:0:+1MiB -t 0:ef02 -c 0:grub "$device"
        sgdisk -n 0:0:+1GiB -t 0:be00 -c 0:boot "$device"
        sgdisk -n 0:0:0 -t 0:bf00 -c 0:root "$device"
    done
    sleep 3
}

create_swap() {
    log_info "Creating swap dataset"
    local swap_size
    swap_size=$(calculate_swap_size "${SWAP_HIBERNATE}" "${ZFS_RPOOL}")
    zfs create -V "$swap_size" -b 4096 -o logbias=throughput -o sync=always -o primarycache=metadata\
        -o com.sun:auto-snapshot=false "${ZFS_RPOOL}/swap"
    mkswap -f "/dev/zvol/${ZFS_RPOOL}/swap"
}

check_ashift() {
    local partitions=$*
    local zpool_ashift=""
    local prev=""
    for part in ${partitions[*]}; do
        zpool_ashift=$(calculate_ashift "${part}")
        if [[ ${prev} && ${zpool_ashift} != $(calculate_ashift "${prev}") ]]; then
            die "device ashifts don't match"
        fi
        prev=${part}
    done
    printf "%d" "${zpool_ashift}"
}

generate_keyfile() {
    local target=$1
    tr -d '\n' < /dev/urandom | head -c 512 > "$target"
    log_info "Successfully created encryption key"
}

create_bpool() {
    log_info "Creating bpool"
    local devices=( "$@" )
    local bpool_ashift
    bpool_ashift=$(check_ashift "${devices[@]}")
    local bpool_args=(
            "-d"
            "-f"
            "-o feature@allocation_classes=enabled"
            "-o feature@async_destroy=enabled"
            "-o feature@bookmarks=enabled"
            "-o feature@embedded_data=enabled"
            "-o feature@empty_bpobj=enabled"
            "-o feature@enabled_txg=enabled"
            "-o feature@extensible_dataset=enabled"
            "-o feature@filesystem_limits=enabled"
            "-o feature@hole_birth=enabled"
            "-o feature@large_blocks=enabled"
            "-o feature@lz4_compress=enabled"
            "-o feature@project_quota=enabled"
            "-o feature@resilver_defer=enabled"
            "-o feature@spacemap_histogram=enabled"
            "-o feature@spacemap_v2=enabled"
            "-o feature@userobj_accounting=enabled"
            "-o feature@zpool_checkpoint=enabled"
            "-o ashift=${bpool_ashift}"
            "-o cachefile=/etc/zfs/zpool.cache"
            "-o autotrim=on"
            "-O acltype=posixacl"
            "-O canmount=off"
            "-O compression=off"
            "-O devices=off"
            "-O normalization=formD"
            "-O relatime=on"
            "-O xattr=sa"
            "-O mountpoint=/boot"
            "-R ${MNT_PATH}"
        )
    case ${ZFS_RAID_TYPE} in
        none)
            zpool create ${bpool_args[*]} ${ZFS_BPOOL} ${devices[*]}
            ;;
        raid0)
            
            zpool create ${bpool_args[*]} ${ZFS_BPOOL} ${devices[*]}
            ;;
        raid1)
            zpool create ${bpool_args[*]} ${ZFS_BPOOL} mirror ${devices[*]}
            ;;
    esac
    zfs create -o canmount=off -o mountpoint=none ${ZFS_BPOOL}/BOOT
    zfs create -o mountpoint=/boot ${ZFS_BPOOL}/BOOT/default
    zpool export ${ZFS_BPOOL}
    rmdir ${MNT_PATH}/boot || true
}

create_rpool() {
    log_info "Creating rpool"
    local devices=( "$@" )
    local rpool_args=(
            "-f"
            "-o ashift=$(check_ashift "${devices[@]}")"
            "-o cachefile=/etc/zfs/zpool.cache"
            "-O acltype=posixacl"
            "-O relatime=on"
            "-O xattr=sa"
            "-O dnodesize=legacy"
            "-O normalization=formD"
            "-O mountpoint=none"
            "-O canmount=off"
            "-O devices=off"
            "-O compression=lz4"
            "-m none"
            "-R ${MNT_PATH}"
        )
    
    if [[ ${ZFS_ENC_ENABLED} ]]; then
        rpool_args+=( "-O encryption=aes-256-gcm" "-O keyformat=passphrase" )
        case ${ZFS_ENC_TYPE} in
            "password")
                rpool_args+=( "-O keylocation=prompt" )
                ;;
            "keyfile")
                mkdir -p "$(dirname "${ZFS_ENC_KEYPATH}")"
                generate_keyfile "${ZFS_ENC_KEYPATH}"
                rpool_args+=( "-O keylocation=file://${ZFS_ENC_KEYPATH}" )
                ;;
        esac
    fi

    case ${ZFS_RAID_TYPE} in
        none)
            zpool create ${rpool_args[*]} ${ZFS_RPOOL} ${devices[*]} <<< "${PASSWORD}"
            ;;
        raid0)
            zpool create ${rpool_args[*]} ${ZFS_RPOOL} ${devices[*]} <<< "${PASSWORD}"
            ;;
        raid1)
            zpool create ${rpool_args[*]} ${ZFS_RPOOL} mirror ${devices[*]} <<< "${PASSWORD}"
            ;;
    esac
    zfs create -o mountpoint=none -o compression=lz4 ${ZFS_RPOOL}/ROOT
    zfs create -o mountpoint=/ ${ZFS_RPOOL}/ROOT/default
    zfs create -o canmount=off -o mountpoint=/var -o xattr=sa ${ZFS_RPOOL}/ROOT/var
    zfs create -o canmount=off -o mountpoint=/var/lib ${ZFS_RPOOL}/ROOT/var/lib
    if [[ ${TARGET_DISTRO} == "gentoo" ]]; then
        # Create portage directories
        zfs create -o mountpoint=/var/cache/distfiles ${ZFS_RPOOL}/ROOT/distfiles

        # Create portage build directory
        zfs create -o mountpoint=/var/tmp/portage -o compression=lz4 -o sync=disabled ${ZFS_RPOOL}/ROOT/build_dir

        # Create optional packages directory
        zfs create -o mountpoint=/var/cache/binpkgs ${ZFS_RPOOL}/ROOT/binpkgs

        # Create optional ccache directory
        zfs create -o mountpoint=/var/tmp/ccache -o compression=lz4 ${ZFS_RPOOL}/ROOT/ccache
    fi
    [[ ${ZFS_DATASET_GNOME} ]] && zfs create -o canmount=on -o mountpoint=/var/lib/AccountsService ${ZFS_RPOOL}/ROOT/var/lib/AccountsService
    [[ ${ZFS_DATASET_LIBVIRT} ]] && zfs create -o canmount=on -o mountpoint=/var/lib/libvirt ${ZFS_RPOOL}/ROOT/var/lib/libvirt
    [[ ${ZFS_DATASET_LXC} ]] && zfs create -o canmount=on -o mountpoint=/var/lib/lxc ${ZFS_RPOOL}/ROOT/var/lib/lxc
    [[ ${ZFS_DATASET_DOCKER} ]] && zfs create -o canmount=on -o mountpoint=/var/lib/docker ${ZFS_RPOOL}/ROOT/var/lib/docker
    [[ ${ZFS_DATASET_NFS} ]] && zfs create -o canmount=on -o mountpoint=/var/lib/nfs ${ZFS_RPOOL}/ROOT/var/lib/nfs
    [[ ${ZFS_DATASET_WEBSERVER} ]] && zfs create -o canmount=on -o mountpoint=/var/www ${ZFS_RPOOL}/ROOT/var/www
    [[ ${ZFS_DATASET_MAILSERVER} ]] && zfs create -o canmount=on -o mountpoint=/var/mail ${ZFS_RPOOL}/ROOT/var/mail
    [[ ${ZFS_DATASET_SNAP} ]] && zfs create -o canmount=on -o mountpoint=/var/snap ${ZFS_RPOOL}/ROOT/var/snap
    [[ ${ZFS_DATASET_SYSTEMD} ]] && zfs create -o canmount=off -o mountpoint=/var/lib/systemd ${ZFS_RPOOL}/ROOT/var/lib/systemd
    zfs create -o canmount=off -o mountpoint=/usr ${ZFS_RPOOL}/ROOT/usr
    zfs create -o mountpoint=/usr/local ${ZFS_RPOOL}/ROOT/usr/local
    zfs create -o mountpoint=/opt ${ZFS_RPOOL}/ROOT/opt
    [[ ${ZFS_DATASET_SYSTEMD} ]] && zfs create -o mountpoint=/var/lib/systemd/coredump ${ZFS_RPOOL}/ROOT/var/lib/systemd/coredump
    zfs create -o mountpoint=/var/log ${ZFS_RPOOL}/ROOT/var/log
    [[ ${ZFS_DATASET_SYSTEMD} ]] && zfs create -o acltype=posixacl -o mountpoint=/var/log/journal ${ZFS_RPOOL}/ROOT/var/log/journal
    zfs create -o mountpoint=/home ${ZFS_RPOOL}/home
    zfs create -o mountpoint=/root ${ZFS_RPOOL}/home/root
    zfs create -o mountpoint=/home/${ZFS_DATASET_USER} ${ZFS_RPOOL}/home/${ZFS_DATASET_USER}
    zpool set bootfs=${ZFS_RPOOL} ${ZFS_RPOOL}
    zfs set relatime=on ${ZFS_RPOOL}
    zfs set compression=lz4 ${ZFS_RPOOL}
    [[ ${ZFS_CREATE_SWAP} ]] && create_swap ${SWAP_HIBERNATE} ${ZFS_RPOOL}
    zpool export ${ZFS_RPOOL}
}

mount_rootfs() {
    zpool import -R ${MNT_PATH} ${ZFS_RPOOL}
    if [[ ${ZFS_ENC_ENABLED} ]]; then
        zfs load-key ${ZFS_RPOOL} <<< "${PASSWORD}"
        zfs mount -a
    fi
    zpool import -o cachefile=/etc/zfs/zpool.cache -R ${MNT_PATH} ${ZFS_BPOOL}
    mkdir -p ${MNT_PATH}/etc/zfs
    cp /etc/zfs/zpool.cache ${MNT_PATH}/etc/zfs/zpool.cache
    log_info "Successfully mounted rootfs"
}

create_rootfs() {
    local partitions=( )
    for device in ${TARGET_DEV[*]}; do
        if [[ ${device} == /dev/disk/by-id* ]]; then
            partitions+=( "${device}-part2" )
        else
            partitions+=( "${device}2" )
        fi
    done
    create_bpool "${partitions[@]}"
    partitions=( )
    for device in ${TARGET_DEV[*]}; do
        if [[ ${device} == /dev/disk/by-id* ]]; then
            partitions+=( "${device}-part3" )
        else
            partitions+=( "${device}3" )
        fi
    done
    create_rpool "${partitions[@]}"
    mount_rootfs
}

create_data_pool() {
    local devices=( "$@" )
    local zpool_args=(
            "-f"
            "-o ashift=$(check_ashift "${TARGET_DEV[@]}")"
            "-o cachefile=/etc/zfs/zpool.cache"
            "-O compression=lz4"
            "-R ${MNT_PATH}"
        )

    if [[ ${ZFS_ENC_ENABLED} ]]; then
        rpool_args+=( "-O encryption=aes-256-gcm" "-O keyformat=passphrase" )
        case ${ZFS_ENC_TYPE} in
            "password")
                rpool_args+=( "-O keylocation=prompt" )
                ;;
            "keyfile")
                mkdir -p "$(dirname "${ZFS_ENC_KEYPATH}")"
                generate_keyfile "${ZFS_ENC_KEYPATH}"
                zpool_args+=( "-O keylocation=file://${ZFS_ENC_KEYPATH}" )
                ;;
        esac
    fi

    case ${ZFS_RAID_TYPE} in
        none)
            zpool create ${rpool_args[*]} ${ZFS_POOL} ${devices[*]} <<< "${PASSWORD}"
            ;;
        raid0)
            zpool create ${rpool_args[*]} ${ZFS_POOL} ${devices[*]} <<< "${PASSWORD}"
            ;;
        raid1)
            zpool create ${rpool_args[*]} ${ZFS_POOL} mirror ${devices[*]} <<< "${PASSWORD}"
            ;;
    esac

    zfs create ${ZFS_POOL}/documents
    zfs create ${ZFS_POOL}/downloads
    zfs create ${ZFS_POOL}/ebooks
    zfs create ${ZFS_POOL}/images
    zfs create ${ZFS_POOL}/movies
    zfs create ${ZFS_POOL}/music
    zfs create ${ZFS_POOL}/operating_systems
    zfs create ${ZFS_POOL}/websites
    zfs create ${ZFS_POOL}/random_stuff

    zpool export ${ZFS_POOL}
    zpool import -R ${MNT_PATH} ${ZFS_POOL}
    if [[ ${ZFS_ENC_ENABLED} ]]; then
        zfs load-key ${ZFS_POOL} <<< "${PASSWORD}"
        zfs mount -a
    fi
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/documents
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/downloads
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/ebooks
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/images
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/movies
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/music
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/operating_systems
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/websites
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/random_stuff
    chown -R ${ZFS_DATASET_USER}:users ${MNT_PATH}
}

create_home_dir_pool() {
    local devices=( "$@" )
    local pool_args=(
            "-f"
            "-o ashift=$(check_ashift "${devices[@]}")"
            "-o cachefile=/etc/zfs/zpool.cache"
            "-O compression=lz4"
            "-O mountpoint=none"
            "-R ${MNT_PATH}"
        )

    if [[ ${ZFS_ENC_ENABLED} ]]; then
        pool_args+=( "-O encryption=aes-256-gcm" "-O keyformat=passphrase" )
        case ${ZFS_ENC_TYPE} in
            "password")
                pool_args+=( "-O keylocation=prompt" )
                ;;
            "keyfile")
                mkdir -p "$(dirname "${ZFS_ENC_KEYPATH}")"
                generate_keyfile "${ZFS_ENC_KEYPATH}"
                pool_args+=( "-O keylocation=file://${ZFS_ENC_KEYPATH}" )
                ;;
        esac
    fi

    case ${ZFS_RAID_TYPE} in
        none)
            zpool create ${pool_args[*]} ${ZFS_POOL} ${devices[*]} <<< "${PASSWORD}"
            ;;
        raid0)
            zpool create ${pool_args[*]} ${ZFS_POOL} ${devices[*]} <<< "${PASSWORD}"
            ;;
        raid1)
            zpool create ${pool_args[*]} ${ZFS_POOL} mirror ${devices[*]} <<< "${PASSWORD}"
            ;;
    esac

    zfs create -o mountpoint=/home/${ZFS_DATASET_USER}/Documents ${ZFS_POOL}/documents
    zfs create -o mountpoint=/home/${ZFS_DATASET_USER}/Downloads ${ZFS_POOL}/downloads
    zfs create -o mountpoint=/home/${ZFS_DATASET_USER}/Calibre\ Library ${ZFS_POOL}/ebooks
    zfs create -o mountpoint=/home/${ZFS_DATASET_USER}/Pictures ${ZFS_POOL}/images
    zfs create -o mountpoint=/home/${ZFS_DATASET_USER}/Videos ${ZFS_POOL}/movies
    zfs create -o mountpoint=/home/${ZFS_DATASET_USER}/Music ${ZFS_POOL}/music
    zfs create -o mountpoint=/home/${ZFS_DATASET_USER}/Operating\ systems ${ZFS_POOL}/operating_systems
    zfs create -o mountpoint=/home/${ZFS_DATASET_USER}/websites ${ZFS_POOL}/websites

    zpool export ${ZFS_POOL}
    zpool import ${ZFS_POOL}
    if [[ ${ZFS_ENC_ENABLED} ]]; then
        zfs load-key ${ZFS_POOL} <<< "${PASSWORD}"
        zfs mount -a
    fi
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/documents
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/downloads
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/ebooks
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/images
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/movies
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/music
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/operating_systems
    zfs allow ${ZFS_DATASET_USER} create,mount,mountpoint,snapshot ${ZFS_POOL}/websites
    chown -R ${ZFS_DATASET_USER}:users /home/${ZFS_DATASET_USER}
}

case ${ZFS_TARGET} in
    "data")
        create_data_pool "${TARGET_DEV[@]}"
        ;;
    "home_dir")
        create_home_dir_pool "${TARGET_DEV[@]}"
        ;;
    "zfs_on_root")
        create_partitions
        create_rootfs
        ;;
esac
0

Do linijki 394 jest całkiem spoko, kiedy cała logika jest pochowana w funkjach. Potem zaczyna się już global-masacre.

1 użytkowników online, w tym zalogowanych: 0, gości: 1