# !/bin/bash # # Architect Installation Framework (2016-2017) # # Written by Carl Duff and @mandog for Archlinux # Heavily modified and re-written by @Chrysostomus to install Manjaro instead # Contributors: @papajoker, @oberon and the Manjaro-Community. # # This program is free software, provided under the GNU General Public License # as published by the Free Software Foundation. So feel free to copy, distribute, # or modify it as you wish. # Unmount partitions. umount_partitions() { MOUNTED="" MOUNTED=$(mount | grep "${MOUNTPOINT}" | awk '{print $3}' | sort -r) swapoff -a for i in ${MOUNTED[@]}; do umount $i >/dev/null 2>$ERR check_for_error "unmount $i" $? # local err=$(umount $i >/dev/null 2>$ERR) # (( err !=0 )) && check_for_error "$FUNCNAME $i" $err done } # This function does not assume that the formatted device is the Root installation device as # more than one device may be formatted. Root is set in the mount_partitions function. select_device() { DEVICE="" devices_list=$(lsblk -lno NAME,SIZE,TYPE | grep 'disk' | awk '{print "/dev/" $1 " " $2}' | sort -u); for i in ${devices_list[@]}; do DEVICE="${DEVICE} ${i}" done DIALOG " $_DevSelTitle " --menu "\n$_DevSelBody\n " 20 60 4 ${DEVICE} 2>${ANSWER} || return 1 DEVICE=$(cat ${ANSWER}) } create_partitions() { # Partitioning Menu DIALOG " $_PrepPartDisk " --menu "\n$_PartToolBody\n " 0 0 7 \ "$_PartOptWipe" "BIOS & UEFI" \ "$_PartOptAuto" "BIOS & UEFI" \ "cfdisk" "BIOS" \ "cgdisk" "UEFI" \ "fdisk" "BIOS & UEFI" \ "gdisk" "UEFI" \ "parted" "BIOS & UEFI" 2>${ANSWER} || return 0 clear # If something selected if [[ $(cat ${ANSWER}) != "" ]]; then if ([[ $(cat ${ANSWER}) != "$_PartOptWipe" ]] && [[ $(cat ${ANSWER}) != "$_PartOptAuto" ]]); then $(cat ${ANSWER}) ${DEVICE} else [[ $(cat ${ANSWER}) == "$_PartOptWipe" ]] && secure_wipe && create_partitions [[ $(cat ${ANSWER}) == "$_PartOptAuto" ]] && auto_partition fi fi } # Securely destroy all data on a given device. secure_wipe() { # Warn the user. If they proceed, wipe the selected device. DIALOG " $_PartOptWipe " --yesno "\n$_AutoPartWipeBody1 ${DEVICE} $_AutoPartWipeBody2\n " 0 0 if [[ $? -eq 0 ]]; then # Install wipe where not already installed. Much faster than dd inst_needed wipe wipe -Ifre ${DEVICE} 2>$ERR check_for_error "wipe ${DEVICE}" $? # Alternate dd command - requires pv to be installed #dd if=/dev/zero | pv | dd of=${DEVICE} iflag=nocache oflag=direct bs=4096 2>$ERR fi } # BIOS and UEFI auto_partition() { # Provide warning to user DIALOG " $_PrepPartDisk " --yesno "\n$_AutoPartBody1 $DEVICE $_AutoPartBody2 $_AutoPartBody3\n " 0 0 if [[ $? -eq 0 ]]; then # Find existing partitions (if any) to remove parted -s ${DEVICE} print | awk '/^ / {print $1}' > /tmp/.del_parts for del_part in $(tac /tmp/.del_parts); do parted -s ${DEVICE} rm ${del_part} 2>$ERR check_for_error "rm ${del_part} on ${DEVICE}" $? done # Identify the partition table part_table=$(parted -s ${DEVICE} print | grep -i 'partition table' | awk '{print $3}' >/dev/null 2>&1) check_for_error "${DEVICE} is $part_table" # Create partition table if one does not already exist if [[ $SYSTEM == "BIOS" ]] && [[ $part_table != "msdos" ]] ; then parted -s ${DEVICE} mklabel msdos 2>$ERR check_for_error "${DEVICE} mklabel msdos" $? fi if [[ $SYSTEM == "UEFI" ]] && [[ $part_table != "gpt" ]] ; then parted -s ${DEVICE} mklabel gpt 2>$ERR check_for_error "${DEVICE} mklabel gpt" $? fi # Create partitions (same basic partitioning scheme for BIOS and UEFI) if [[ $SYSTEM == "BIOS" ]]; then parted -s ${DEVICE} mkpart primary ext3 1MiB 513MiB 2>$ERR check_for_error "create ext3 513MiB on ${DEVICE}" $? else parted -s ${DEVICE} mkpart ESP fat32 1MiB 513MiB 2>$ERR check_for_error "create ESP on ${DEVICE}" $? fi parted -s ${DEVICE} set 1 boot on 2>$ERR check_for_error "set boot flag for ${DEVICE}" $? parted -s ${DEVICE} mkpart primary ext3 513MiB 100% 2>$ERR check_for_error "create ext3 100% on ${DEVICE}" $? # Show created partitions lsblk ${DEVICE} -o NAME,TYPE,FSTYPE,SIZE > /tmp/.devlist DIALOG "" --textbox /tmp/.devlist 0 0 fi } # Finds all available partitions according to type(s) specified and generates a list # of them. This also includes partitions on different devices. find_partitions() { PARTITIONS="" NUMBER_PARTITIONS=0 # get the list of partitions and also include the zvols since it is common to mount filesystems directly on them. It should be safe to include them here since they present as block devices. partition_list=$(lsblk -lno NAME,SIZE,TYPE | grep $INCLUDE_PART | sed 's/part$/\/dev\//g' | sed 's/lvm$\|crypt$/\/dev\/mapper\//g' | \ awk '{print $3$1 " " $2}' | awk '!/mapper/{a[++i]=$0;next}1;END{while(x/dev/null | awk '{printf "/dev/zvol/%s %s\n", $1, $2}') # create a raid partition list old_ifs="$IFS" IFS=$'\n' raid_partitions=($(lsblk -lno NAME,SIZE,TYPE | grep raid | awk '{print $1,$2}' | uniq)) IFS="$old_ifs" # add raid partitions to partition_list for i in "${raid_partitions[@]}" do partition_list="${partition_list} /dev/md/${i}" done for i in ${partition_list}; do PARTITIONS="${PARTITIONS} ${i}" NUMBER_PARTITIONS=$(( NUMBER_PARTITIONS + 1 )) done # Double-partitions will be counted due to counting sizes, so fix NUMBER_PARTITIONS=$(( NUMBER_PARTITIONS / 2 )) check_for_error "--------- [lsblk] ------------" local parts=($PARTITIONS) for i in ${!parts[@]}; do (( $i % 2 == 0 )) || continue local j=$((i+1)) check_for_error "${parts[i]} ${parts[j]}" done #for test delete /dev:sda8 #delete_partition_in_list "/dev/sda8" # Deal with partitioning schemes appropriate to mounting, lvm, and/or luks. case $INCLUDE_PART in 'part\|lvm\|crypt') # Deal with incorrect partitioning for main mounting function if ([[ $SYSTEM == "UEFI" ]] && [[ $NUMBER_PARTITIONS -lt 2 ]]) || ([[ $SYSTEM == "BIOS" ]] && [[ $NUMBER_PARTITIONS -eq 0 ]]); then DIALOG " $_ErrTitle " --msgbox "\n$_PartErrBody\n " 0 0 create_partitions fi ;; 'part\|crypt') # Ensure there is at least one partition for LVM if [[ $NUMBER_PARTITIONS -eq 0 ]]; then DIALOG " $_ErrTitle " --msgbox "\n$_LvmPartErrBody\n " 0 0 create_partitions fi ;; 'part\|lvm') # Ensure there are at least two partitions for LUKS if [[ $NUMBER_PARTITIONS -lt 2 ]]; then DIALOG " $_ErrTitle " --msgbox "\n$_LuksPartErrBody\n " 0 0 create_partitions fi ;; esac } ## List partitions to be hidden from the mounting menu list_mounted() { lsblk -l | awk '$7 ~ /mnt/ {print $1}' > /tmp/.mounted check_for_error "already mounted: $(cat /tmp/.mounted)" echo /dev/* /dev/mapper/* | xargs -n1 2>/dev/null | grep -f /tmp/.mounted } list_containing_crypt() { blkid | awk '/TYPE="crypto_LUKS"/{print $1}' | sed 's/.$//' } list_non_crypt() { blkid | awk '!/TYPE="crypto_LUKS"/{print $1}' | sed 's/.$//' } # delete partition in list $PARTITIONS # param: partition to delete delete_partition_in_list() { [ -z "$1" ] && return 127 local parts=($PARTITIONS) for i in ${!parts[@]}; do (( $i % 2 == 0 )) || continue if [[ "${parts[i]}" = "$1" ]]; then local j=$((i+1)) unset parts[$j] unset parts[$i] check_for_error "in partitions delete item $1 no: $i / $j" PARTITIONS="${parts[*]}" check_for_error "partitions: $PARTITIONS" NUMBER_PARTITIONS=$(( "${#parts[*]}" / 2 )) return 0 fi done return 0 } # Revised to deal with partion sizes now being displayed to the user confirm_mount() { if [[ $(mount | grep $1) ]]; then DIALOG " $_MntStatusTitle " --infobox "\n$_MntStatusSucc\n " 0 0 sleep 2 PARTITIONS=$(echo $PARTITIONS | sed "s~${PARTITION} [0-9]*[G-M]~~" | sed "s~${PARTITION} [0-9]*\.[0-9]*[G-M]~~" | sed s~${PARTITION}$' -'~~) NUMBER_PARTITIONS=$(( NUMBER_PARTITIONS - 1 )) else DIALOG " $_MntStatusTitle " --infobox "\n$_MntStatusFail\n " 0 0 sleep 2 return 1 fi } # Set static list of filesystems rather than on-the-fly. Partially as most require additional flags, and # partially because some don't seem to be viable. # Set static list of filesystems rather than on-the-fly. select_filesystem() { # prep variables fs_opts="" CHK_NUM=0 # zfs legacy filesystems can't be formatted, always have a zfs filesystem and store their mount options in metadata [[ $(zfs_list_datasets legacy | grep "^${PARTITION}$") ]] && return DIALOG " $_FSTitle " --menu "\n$_FSBody\n " 0 0 10 \ "$_FSSkip" "-" \ "btrfs" "mkfs.btrfs -f" \ "ext2" "mkfs.ext2 -q" \ "ext3" "mkfs.ext3 -q" \ "ext4" "mkfs.ext4 -q" \ "f2fs" "mkfs.f2fs -q" \ "jfs" "mkfs.jfs -q" \ "nilfs2" "mkfs.nilfs2 -fq" \ "ntfs" "mkfs.ntfs -q" \ "reiserfs" "mkfs.reiserfs -q" \ "vfat" "mkfs.vfat -F32" \ "xfs" "mkfs.xfs -f" 2>${ANSWER} || return 1 case $(cat ${ANSWER}) in "$_FSSkip") FILESYSTEM="$_FSSkip" ;; "btrfs") FILESYSTEM="mkfs.btrfs -f" CHK_NUM=16 fs_opts="autodefrag compress=zlib compress=lzo compress=zstd compress=no compress-force=zlib compress-force=lzo compress-force=zstd discard \ noacl noatime nodatasum nospace_cache recovery skip_balance space_cache nossd ssd ssd_spread commit=120" modprobe btrfs ;; "ext2") FILESYSTEM="mkfs.ext2 -q" ;; "ext3") FILESYSTEM="mkfs.ext3 -q" ;; "ext4") FILESYSTEM="mkfs.ext4 -q" CHK_NUM=8 fs_opts="data=journal data=writeback dealloc discard noacl noatime nobarrier nodelalloc" ;; "f2fs") FILESYSTEM="mkfs.f2fs -q" fs_opts="data_flush disable_roll_forward disable_ext_identify discard fastboot flush_merge \ inline_xattr inline_data inline_dentry no_heap noacl nobarrier noextent_cache noinline_data norecovery" CHK_NUM=16 modprobe f2fs ;; "jfs") FILESYSTEM="mkfs.jfs -q" CHK_NUM=4 fs_opts="discard errors=continue errors=panic nointegrity" ;; "nilfs2") FILESYSTEM="mkfs.nilfs2 -fq" CHK_NUM=7 fs_opts="discard nobarrier errors=continue errors=panic order=relaxed order=strict norecovery" ;; "ntfs") FILESYSTEM="mkfs.ntfs -q" ;; "reiserfs") FILESYSTEM="mkfs.reiserfs -q" CHK_NUM=5 fs_opts="acl nolog notail replayonly user_xattr" ;; "vfat") FILESYSTEM="mkfs.vfat -F32" ;; "xfs") FILESYSTEM="mkfs.xfs -f" CHK_NUM=9 fs_opts="discard filestreams ikeep largeio noalign nobarrier norecovery noquota wsync" ;; *) return 1 ;; esac # Warn about formatting! if [[ $FILESYSTEM != $_FSSkip ]]; then DIALOG " $_FSTitle " --yesno "\n$_FSMount $FILESYSTEM\n\n! $_FSWarn1 $PARTITION $_FSWarn2 !\n " 0 0 if (( $? != 1 )); then ${FILESYSTEM} ${PARTITION} >/dev/null 2>$ERR check_for_error "mount ${PARTITION} as ${FILESYSTEM}." $? || return 1 ini "mount.${PARTITION}" $(echo "${FILESYSTEM:5}|cut -d' ' -f1") fi fi } # This subfunction allows for special mounting options to be applied for relevant fs's. # Seperate subfunction for neatness. mount_opts() { FS_OPTS="" for i in ${fs_opts}; do FS_OPTS="${FS_OPTS} ${i} - off" done echo ${FS_OPTS} > /tmp/.fs_options format_name=$(echo ${PARTITION} | rev | cut -d/ -f1 | rev) format_device=$(lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e "/$format_name/,/disk/p" | awk '/disk/ {print $1}') if [[ "$(cat /sys/block/${format_device}/queue/rotational)" == 1 ]]; then sed -i 's/autodefrag - off/autodefrag - on/' /tmp/.fs_options sed -i 's/compress=zlip - off/compress=zlip - on/' /tmp/.fs_options sed -i 's/nossd - off/nossd - on/' /tmp/.fs_options else sed -i 's/compress=lzo - off/compress=lzo - on/' /tmp/.fs_options sed -i 's/ space_cache - off/ space_cache - on/' /tmp/.fs_options sed -i 's/commit=120 - off/commit=120 - on/' /tmp/.fs_options sed -i 's/ ssd - off/ ssd - on/' /tmp/.fs_options fi sed -i 's/noatime - off/noatime - on/' /tmp/.fs_options FS_OPTS=$(cat /tmp/.fs_options) DIALOG " $(echo $FILESYSTEM | sed "s/.*\.//g;s/-.*//g") " --checklist "\n$_btrfsMntBody\n " 0 0 \ $CHK_NUM $FS_OPTS 2>${MOUNT_OPTS} # Now clean up the file sed -i 's/ /,/g' ${MOUNT_OPTS} sed -i '$s/,$//' ${MOUNT_OPTS} # If mount options selected, confirm choice if [[ $(cat ${MOUNT_OPTS}) != "" ]]; then DIALOG " $_MntStatusTitle " --yesno "\n${_btrfsMntConfBody}$(cat ${MOUNT_OPTS})\n " 10 75 [[ $? -eq 1 ]] && echo "" > ${MOUNT_OPTS} fi } mount_current_partition() { # Make the mount directory mkdir -p ${MOUNTPOINT}${MOUNT} 2>$ERR check_for_error "create mountpoint ${MOUNTPOINT}${MOUNT}" "$?" echo "" > ${MOUNT_OPTS} # Get mounting options for appropriate filesystems [[ $fs_opts != "" ]] && mount_opts # Check for zfs, use special mounting options if selected, else standard mount if [[ $(zfs_list_datasets legacy | grep "^${PARTITION}$") ]]; then check_for_error "mount ${PARTITION}" mount -t zfs ${PARTITION} ${MOUNTPOINT}${MOUNT} 2>>$LOGFILE elif [[ $(cat ${MOUNT_OPTS}) != "" ]]; then check_for_error "mount ${PARTITION} $(cat ${MOUNT_OPTS})" mount -o $(cat ${MOUNT_OPTS}) ${PARTITION} ${MOUNTPOINT}${MOUNT} 2>>$LOGFILE else check_for_error "mount ${PARTITION}" mount ${PARTITION} ${MOUNTPOINT}${MOUNT} 2>>$LOGFILE fi confirm_mount ${MOUNTPOINT}${MOUNT} # Identify if mounted partition is type "crypt" (LUKS on LVM, or LUKS alone) if [[ $(lsblk -lno TYPE ${PARTITION} | grep "crypt") != "" ]]; then # cryptname for bootloader configuration either way LUKS=1 LUKS_NAME=$(echo ${PARTITION} | sed "s~^/dev/mapper/~~g") # Check if LUKS on LVM (parent = lvm /dev/mapper/...) cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE | grep "lvm" | grep -i "crypto_luks" | uniq | awk '{print "/dev/mapper/"$1}') for i in ${cryptparts}; do if [[ $(lsblk -lno NAME ${i} | grep $LUKS_NAME) != "" ]]; then LUKS_DEV="$LUKS_DEV cryptdevice=${i}:$LUKS_NAME" LVM=1 return 0; fi done # Check if LVM on LUKS cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE | grep " crypt$" | grep -i "LVM2_member" | uniq | awk '{print "/dev/mapper/"$1}') for i in ${cryptparts}; do if [[ $(lsblk -lno NAME ${i} | grep $LUKS_NAME) != "" ]]; then LUKS_DEV="$LUKS_DEV cryptdevice=${i}:$LUKS_NAME" LVM=1 return 0; fi done # Check if LUKS alone (parent = part /dev/...) cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE | grep "part" | grep -i "crypto_luks" | uniq | awk '{print "/dev/"$1}') for i in ${cryptparts}; do if [[ $(lsblk -lno NAME ${i} | grep $LUKS_NAME) != "" ]]; then LUKS_UUID=$(lsblk -lno UUID,TYPE,FSTYPE ${i} | grep "part" | grep -i "crypto_luks" | awk '{print $1}') LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME" return 0; fi done # If LVM logical volume.... elif [[ $(lsblk -lno TYPE ${PARTITION} | grep "lvm") != "" ]]; then LVM=1 # First get crypt name (code above would get lv name) cryptparts=$(lsblk -lno NAME,TYPE,FSTYPE | grep "crypt" | grep -i "lvm2_member" | uniq | awk '{print "/dev/mapper/"$1}') for i in ${cryptparts}; do if [[ $(lsblk -lno NAME ${i} | grep $(echo $PARTITION | sed "s~^/dev/mapper/~~g")) != "" ]]; then LUKS_NAME=$(echo ${i} | sed s~/dev/mapper/~~g) return 0; fi done # Now get the device (/dev/...) for the crypt name cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE | grep "part" | grep -i "crypto_luks" | uniq | awk '{print "/dev/"$1}') for i in ${cryptparts}; do if [[ $(lsblk -lno NAME ${i} | grep $LUKS_NAME) != "" ]]; then # Create UUID for comparison LUKS_UUID=$(lsblk -lno UUID,TYPE,FSTYPE ${i} | grep "part" | grep -i "crypto_luks" | awk '{print $1}') # Check if not already added as a LUKS DEVICE (i.e. multiple LVs on one crypt). If not, add. if [[ $(echo $LUKS_DEV | grep $LUKS_UUID) == "" ]]; then LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME" LUKS=1 fi return 0; fi done fi } make_swap() { # Ask user to select partition or create swapfile if swapfiles are valid for the root filesystem if [[ $(findmnt -ln -o FSTYPE ${MOUNTPOINT}) == "zfs" || $(findmnt -ln -o FSTYPE ${MOUNTPOINT}) == "btrfs" ]]; then DIALOG " $_PrepMntPart " --menu "\n$_SelSwpBody\n " 0 0 12 "$_SelSwpNone" $"-" ${PARTITIONS} 2>${ANSWER} || return 0 else DIALOG " $_PrepMntPart " --menu "\n$_SelSwpBody\n " 0 0 12 "$_SelSwpNone" $"-" "$_SelSwpFile" $"-" ${PARTITIONS} 2>${ANSWER} || return 0 fi if [[ $(cat ${ANSWER}) != "$_SelSwpNone" ]]; then PARTITION=$(cat ${ANSWER}) if [[ $PARTITION == "$_SelSwpFile" ]]; then total_memory=$(grep MemTotal /proc/meminfo | awk '{print $2/1024}' | sed 's/\..*//') DIALOG " $_SelSwpFile " --inputbox "\nM = MB, G = GB\n " 9 30 "${total_memory}M" 2>${ANSWER} || return 0 m_or_g=$(cat ${ANSWER}) while [[ $(echo ${m_or_g: -1} | grep "M\|G") == "" ]]; do DIALOG " $_SelSwpFile " --msgbox "\n$_SelSwpFile $_ErrTitle: M = MB, G = GB\n " 0 0 DIALOG " $_SelSwpFile " --inputbox "\nM = MB, G = GB\n " 9 30 "${total_memory}M" 2>${ANSWER} || return 0 m_or_g=$(cat ${ANSWER}) done fallocate -l ${m_or_g} ${MOUNTPOINT}/swapfile 2>$ERR check_for_error "Swapfile fallocate" "$?" chmod 600 ${MOUNTPOINT}/swapfile 2>$ERR check_for_error "Swapfile chmod" "$?" mkswap ${MOUNTPOINT}/swapfile 2>$ERR check_for_error "Swapfile mkswap" "$?" swapon ${MOUNTPOINT}/swapfile 2>$ERR check_for_error "Swapfile swapon" "$?" else # Swap Partition # Warn user if creating a new swap if [[ $(lsblk -o FSTYPE ${PARTITION} | grep -i "swap") != "swap" ]]; then DIALOG " $_PrepMntPart " --yesno "\nmkswap ${PARTITION}\n " 0 0 if [[ $? -eq 0 ]]; then mkswap ${PARTITION} >/dev/null 2>$ERR check_for_error "Swap partition: mkswap" "$?" else return 0 fi fi # Whether existing to newly created, activate swap swapon ${PARTITION} >/dev/null 2>$ERR check_for_error "Swap partition: swapon" "$?" # Since a partition was used, remove that partition from the list PARTITIONS=$(echo $PARTITIONS | sed "s~${PARTITION} [0-9]*[G-M]~~" | sed "s~${PARTITION} [0-9]*\.[0-9]*[G-M]~~" | sed s~${PARTITION}$' -'~~) NUMBER_PARTITIONS=$(( NUMBER_PARTITIONS - 1 )) fi fi ini mount.swap "${PARTITION}" } raid_level_menu() { declare -i loopmenu=1 while ((loopmenu)); do RAID_OPT="" DIALOG "RAID" --menu "\n$_RAIDLevelTitle\n" 20 75 6 \ "0" "$_RAIDLevel0" \ "1" "$_RAIDLevel1" \ "5" "$_RAIDLevel5" \ "6" "$_RAIDLevel6" \ "10" "$_RAIDLevel10" \ "$_Back" "-" 2>${ANSWER} case $(cat ${ANSWER}) in "0") raid_array_menu 0 ;; "1") raid_array_menu 1 ;; "5") raid_array_menu 5 ;; "6") raid_array_menu 6 ;; "10") raid_array_menu 10 ;; *) loopmenu=0 return 0 ;; esac done } raid_create() { RAID_DEVICES=${1} RAID_DEVICE_NUMBER=$(echo ${1} | wc -w) RAID_LEVEL=${2} RAID_DEVICE_NAME=${3} # creates the array mdadm --create --level=${RAID_LEVEL} --metadata=1.2 --raid-devices=${RAID_DEVICE_NUMBER} /dev/md/${RAID_DEVICE_NAME} ${RAID_DEVICES} # array is disassembled and reassembled to prevent the array from being named /dev/md/md127 # the check of /etc/mdadm.conf is preformed to prevent the user from adding duplicate entries if [[ $(cat /etc/mdadm.conf | grep "/dev/md/${RAID_DEVICE_NAME}" | wc -l) == 0 ]]; then mdadm --detail --scan | grep -e "/dev/md/${RAID_DEVICE_NAME}" -e "/dev/md/md127" >> /etc/mdadm.conf mdadm --stop /dev/md/${RAID_DEVICE_NAME} mdadm --assemble --scan fi DIALOG "$__ArrayCreatedTitle" --msgbox "\n$_ArrayCreatedDescription\n\nmdadm --create --level=${RAID_LEVEL} --metadata=1.2 --raid-devices=${RAID_DEVICE_NUMBER} /dev/md/${RAID_DEVICE_NAME} ${RAID_DEVICES}\n" 0 0 } raid_get_array_name() { DIALOG "$_DeviceNameTitle" --inputbox "\n$_DeviceNameDescription\n\n$_DeviceNamePrefixWarning\n" 0 0 2>${ANSWER} raid_device_name=$(cat ${ANSWER}) if [[ ${raid_device_name} != "" ]]; then raid_create "${1}" ${2} ${raid_device_name} fi } raid_array_menu() { # find raid partitions. INCLUDE_PART='part\|crypt' umount_partitions find_partitions # Amend partition(s) found for use in check list PARTITIONS=$(echo $PARTITIONS | sed 's/M\|G\|T/& off/g') RAID_LEVEL=${1} # select partitions for the array echo "" > $ANSWER while [[ $(cat ${ANSWER}) == "" ]]; do DIALOG "$_PartitionSelectTitle" --checklist "\n$__PartitionSelectDescription\n\n$_UseSpaceBar\n " 0 0 12 ${PARTITIONS} 2> ${ANSWER} done ANSWERS=$(cat ${ANSWER}) raid_get_array_name "${ANSWERS[@]}" ${RAID_LEVEL} } luks_menu() { declare -i loopmenu=1 while ((loopmenu)); do LUKS_OPT="" DIALOG " $_PrepLUKS " --menu "\n$_LuksMenuBody\n$_LuksMenuBody2\n$_LuksMenuBody3\n " 0 0 0 \ "$_LuksOpen" "cryptsetup open --type luks" \ "$_LuksEncrypt" "cryptsetup -q luksFormat" \ "$_LuksEncryptAdv" "cryptsetup -q -s -c luksFormat" \ "Express LUKS" "cryptsetup -q -s --pbkdf-force-iterations 200000 -c luksFormat" \ "$_Back" "-" 2>${ANSWER} case $(cat ${ANSWER}) in "$_LuksOpen") luks_open ;; "$_LuksEncrypt") luks_setup && luks_default && luks_show ;; "$_LuksEncryptAdv") luks_setup && luks_key_define && luks_show ;; "Express LUKS") luks_setup && luks_express && luks_show ;; *) loopmenu=0 return 0 ;; esac done } luks_open() { LUKS_ROOT_NAME="" INCLUDE_PART='part\|crypt\|lvm' umount_partitions find_partitions # Filter out partitions that don't contain crypt device list_non_crypt > /tmp/.ignore_part for part in $(cat /tmp/.ignore_part); do delete_partition_in_list $part done # stop if no encrypted partition found if [[ $PARTITIONS == "" ]]; then DIALOG " $_ErrTitle " --msgbox "\n$_LuksErr\n " 0 0 return 1 fi # Select encrypted partition to open DIALOG " $_LuksOpen " --menu "\n$_LuksMenuBody\n " 0 0 12 ${PARTITIONS} 2>${ANSWER} || return 0 PARTITION=$(cat ${ANSWER}) # Enter name of the Luks partition and get password to open it DIALOG " $_LuksOpen " --inputbox "\n$_LuksOpenBody\n " 0 0 "cryptroot" 2>${ANSWER} || return 0 LUKS_ROOT_NAME=$(cat ${ANSWER}) DIALOG " $_PrepLUKS " --clear --insecure --passwordbox "\n$_LuksPassBody\n " 0 0 2> ${ANSWER} || return 0 PASSWD=$(cat ${ANSWER}) # Try to open the luks partition with the credentials given. If successful show this, otherwise # show the error DIALOG " $_LuksOpen " --infobox "\n$_PlsWaitBody\n " 0 0 echo $PASSWD | cryptsetup open --type luks ${PARTITION} ${LUKS_ROOT_NAME} 2>$ERR check_for_error "luks pwd ${PARTITION} ${LUKS_ROOT_NAME}" "$?" echo "" > /tmp/.devlist lsblk -o NAME,TYPE,FSTYPE,SIZE,MOUNTPOINT ${PARTITION} | grep "crypt\|NAME\|MODEL\|TYPE\|FSTYPE\|SIZE" >> /tmp/.devlist DIALOG " $_DevShowOpt " --textbox /tmp/.devlist 0 0 } luks_password() { luks_get_password while [[ "$PASSWD" != "$PASSWD2" ]]; do DIALOG " $_ErrTitle " --msgbox "\n$_PassErrBody\n " 0 0 luks_get_password done } luks_get_password() { DIALOG " $_PrepLUKS " --clear --insecure --passwordbox "\n$_LuksPassBody\n " 0 0 2> ${ANSWER} || return 0 PASSWD=$(cat ${ANSWER}) DIALOG " $_PrepLUKS " --clear --insecure --passwordbox "\n$_PassReEntBody\n " 0 0 2> ${ANSWER} || return 0 PASSWD2=$(cat ${ANSWER}) } luks_setup() { modprobe -a dm-mod dm_crypt INCLUDE_PART='part\|lvm' umount_partitions find_partitions # Select partition to encrypt DIALOG " $_LuksEncrypt " --menu "\n$_LuksCreateBody\n " 0 0 12 ${PARTITIONS} 2>${ANSWER} || return 1 PARTITION=$(cat ${ANSWER}) # Enter name of the Luks partition and get password to create it DIALOG " $_LuksEncrypt " --inputbox "\n$_LuksOpenBody\n " 0 0 "cryptroot" 2>${ANSWER} || return 1 LUKS_ROOT_NAME=$(cat ${ANSWER}) luks_password } luks_default() { # Encrypt selected partition or LV with credentials given DIALOG " $_LuksEncrypt " --infobox "\n$_PlsWaitBody\n " 0 0 sleep 2 echo $PASSWD | cryptsetup -q --type luks1 luksFormat ${PARTITION} 2>$ERR check_for_error "luksFormat ${PARTITION}" $? # Now open the encrypted partition or LV echo $PASSWD | cryptsetup open ${PARTITION} ${LUKS_ROOT_NAME} 2>$ERR check_for_error "open ${PARTITION} ${LUKS_ROOT_NAME}" $? } luks_express() { # Encrypt selected partition or LV with credentials given DIALOG " $_LuksEncrypt " --infobox "\n$_PlsWaitBody\n " 0 0 sleep 2 echo $PASSWD | cryptsetup -q --pbkdf-force-iterations 200000 --type luks1 luksFormat ${PARTITION} 2>$ERR check_for_error "luksFormat ${PARTITION}" $? # Now open the encrypted partition or LV echo $PASSWD | cryptsetup open ${PARTITION} ${LUKS_ROOT_NAME} 2>$ERR check_for_error "open ${PARTITION} ${LUKS_ROOT_NAME}" $? } luks_key_define() { DIALOG " $_PrepLUKS " --inputbox "\n$_LuksCipherKey\n " 0 0 "-s 512 -c aes-xts-plain64" 2>${ANSWER} || return 1 # Encrypt selected partition or LV with credentials given DIALOG " $_LuksEncryptAdv " --infobox "\n$_PlsWaitBody\n " 0 0 sleep 2 echo $PASSWD | cryptsetup -q $(cat ${ANSWER}) luksFormat ${PARTITION} 2>$ERR check_for_error "encrypt ${PARTITION}" "$?" # Now open the encrypted partition or LV echo $PASSWD | cryptsetup open ${PARTITION} ${LUKS_ROOT_NAME} 2>$ERR check_for_error "open ${PARTITION} ${LUKS_ROOT_NAME}" "$?" } luks_show() { printf "\n${_LuksEncruptSucc}\n\n" > /tmp/.devlist lsblk -o NAME,TYPE,FSTYPE,SIZE ${PARTITION} | grep "part\|crypt\|NAME\|TYPE\|FSTYPE\|SIZE" >> /tmp/.devlist DIALOG " $_LuksEncrypt " --textbox /tmp/.devlist 0 0 } lvm_menu() { declare -i loopmenu=1 while ((loopmenu)); do DIALOG " $_PrepLVM $_PrepLVM2 " --infobox "\n$_PlsWaitBody\n " 0 0 sleep 1 lvm_detect DIALOG " $_PrepLVM $_PrepLVM2 " --menu "\n$_LvmMenu\n " 22 60 4 \ "$_LvmCreateVG" "vgcreate -f, lvcreate -L -n" \ "$_LvmDelVG" "vgremove -f" \ "$_LvMDelAll" "lvremove, vgremove, pvremove -f" \ "$_Back" "-" 2>${ANSWER} case $(cat ${ANSWER}) in "$_LvmCreateVG") lvm_create ;; "$_LvmDelVG") lvm_del_vg ;; "$_LvMDelAll") lvm_del_all ;; *) loopmenu=0 return 0 ;; esac done } lvm_detect() { LVM_PV=$(pvs -o pv_name --noheading 2>/dev/null) LVM_VG=$(vgs -o vg_name --noheading 2>/dev/null) LVM_LV=$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null) if [[ $LVM_LV != "" ]] && [[ $LVM_VG != "" ]] && [[ $LVM_PV != "" ]]; then DIALOG " $_PrepLVM " --infobox "\n$_LvmDetBody\n " 0 0 sleep 2 modprobe dm-mod 2>$ERR check_for_error "modprobe dm-mod" "$?" vgscan >/dev/null 2>&1 vgchange -ay >/dev/null 2>&1 fi } # Create Volume Group and Logical Volumes lvm_create() { # Find LVM appropriate partitions. INCLUDE_PART='part\|crypt' umount_partitions find_partitions # Amend partition(s) found for use in check list PARTITIONS=$(echo $PARTITIONS | sed 's/M\|G\|T/& off/g') # Name the Volume Group DIALOG " $_LvmCreateVG " --inputbox "\n$_LvmNameVgBody\n " 0 0 2>${ANSWER} || return 0 LVM_VG=$(cat ${ANSWER}) # Loop while the Volume Group name starts with a "/", is blank, has spaces, or is already being used while [[ ${LVM_VG:0:1} == "/" ]] || [[ ${#LVM_VG} -eq 0 ]] || [[ $LVM_VG =~ \ |\' ]] || [[ $(lsblk | grep ${LVM_VG}) != "" ]]; do DIALOG " $_ErrTitle " --msgbox "\n$_LvmNameVgErr\n " 0 0 DIALOG " $_LvmCreateVG " --inputbox "\n$_LvmNameVgBody\n " 0 0 "" 2>${ANSWER} || return 0 LVM_VG=$(cat ${ANSWER}) done # Select the partition(s) for the Volume Group echo "" > $ANSWER while [[ $(cat ${ANSWER}) == "" ]]; do DIALOG " $_LvmCreateVG " --checklist "\n$_LvmPvSelBody\n\n$_UseSpaceBar\n " 0 0 12 ${PARTITIONS} 2>${ANSWER} || return 0 done VG_PARTS=$(cat ${ANSWER}) # Once all the partitions have been selected, show user. On confirmation, use it/them in 'vgcreate' command. # Also determine the size of the VG, to use for creating LVs for it. DIALOG " $_LvmCreateVG " --yesno "\n$_LvmPvConfBody1 [${LVM_VG}] $_LvmPvConfBody2\n${VG_PARTS}\n " 0 0 if [[ $? -eq 0 ]]; then DIALOG " $_LvmCreateVG " --infobox "\n$_LvmPvActBody1 [${LVM_VG}].\n$_PlsWaitBody\n " 0 0 sleep 1 vgcreate -f ${LVM_VG} ${VG_PARTS} >/dev/null check_for_error "vgcreate -f ${LVM_VG} ${VG_PARTS}" # Once created, get size and size type for display and later number-crunching for lv creation VG_SIZE=$(vgdisplay $LVM_VG | awk '/VG Size/ {print $3}' | sed -e 's/${ANSWER} # repeat if answer is not a number while [[ $(cat ${ANSWER}) != ?(-)+([0-9]) ]]; do DIALOG " $_ErrTitle " --inputbox "\n$_LvmLvNumBody1 [${LVM_VG}]. $_LvmLvNumBody2\n " 0 0 2>${ANSWER} done NUMBER_LOGICAL_VOLUMES=$(cat ${ANSWER}) # Loop while the number of LVs is greater than 1. This is because the size of the last LV is automatic. while [[ $NUMBER_LOGICAL_VOLUMES -gt 1 ]]; do DIALOG " $_LvmCreateVG (LV:$NUMBER_LOGICAL_VOLUMES) " --inputbox "\n$_LvmLvNameBody1\n " 0 0 "lvol" 2>${ANSWER} || return 0 LVM_LV_NAME=$(cat ${ANSWER}) # Loop if preceeded with a "/", if nothing is entered, if there is a space, or if that name already exists. while [[ ${LVM_LV_NAME:0:1} == "/" ]] || [[ ${#LVM_LV_NAME} -eq 0 ]] || [[ ${LVM_LV_NAME} =~ \ |\' ]] || [[ $(lsblk | grep ${LVM_LV_NAME}) != "" ]]; do DIALOG " $_ErrTitle " --msgbox "\n$_LvmLvNameErrBody\n " 0 0 DIALOG " $_LvmCreateVG (LV:$NUMBER_LOGICAL_VOLUMES) " --inputbox "\n$_LvmLvNameBody1\n " 0 0 "lvol" 2>${ANSWER} || return 0 LVM_LV_NAME=$(cat ${ANSWER}) done DIALOG " $_LvmCreateVG (LV:$NUMBER_LOGICAL_VOLUMES) " --inputbox \ "\n[${LVM_VG}]: ${VG_SIZE}${VG_SIZE_TYPE} - (${LVM_VG_MB}MB $_LvmLvSizeBody1).\n\n$_LvmLvSizeBody2\n " 0 0 "" 2>${ANSWER} || return 0 LVM_LV_SIZE=$(cat ${ANSWER}) check_lv_size # Loop while an invalid value is entered. while [[ $LV_SIZE_INVALID -eq 1 ]]; do DIALOG " $_ErrTitle " --msgbox "\n$_LvmLvSizeErrBody\n " 0 0 DIALOG " $_LvmCreateVG (LV:$NUMBER_LOGICAL_VOLUMES) " --inputbox \ "\n[${LVM_VG}]: ${VG_SIZE}${VG_SIZE_TYPE} - (${LVM_VG_MB}MB $_LvmLvSizeBody1).\n\n$_LvmLvSizeBody2\n " 0 0 "" 2>${ANSWER} || return 0 LVM_LV_SIZE=$(cat ${ANSWER}) check_lv_size done # Create the LV lvcreate -W y -qq -L ${LVM_LV_SIZE} ${LVM_VG} -n ${LVM_LV_NAME} 2>$ERR check_for_error "lvcreate -W y -qq -L ${LVM_LV_SIZE} ${LVM_VG} -n ${LVM_LV_NAME}" "$?" DIALOG " $_LvmCreateVG (LV:$NUMBER_LOGICAL_VOLUMES) " --msgbox "\nLV ${LVM_LV_NAME} (${LVM_LV_SIZE}) $_LvmPvDoneBody2.\n " 0 0 NUMBER_LOGICAL_VOLUMES=$(( NUMBER_LOGICAL_VOLUMES - 1 )) done # Now the final LV. Size is automatic. DIALOG " $_LvmCreateVG (LV:$NUMBER_LOGICAL_VOLUMES) " --inputbox "\n$_LvmLvNameBody1 $_LvmLvNameBody2 (${LVM_VG_MB}MB).\n " 0 0 "lvol" 2>${ANSWER} || return 0 LVM_LV_NAME=$(cat ${ANSWER}) # Loop if preceeded with a "/", if nothing is entered, if there is a space, or if that name already exists. while [[ ${LVM_LV_NAME:0:1} == "/" ]] || [[ ${#LVM_LV_NAME} -eq 0 ]] || [[ ${LVM_LV_NAME} =~ \ |\' ]] || [[ $(lsblk | grep ${LVM_LV_NAME}) != "" ]]; do DIALOG " $_ErrTitle " --msgbox "\n$_LvmLvNameErrBody\n " 0 0 DIALOG " $_LvmCreateVG (LV:$NUMBER_LOGICAL_VOLUMES) " --inputbox "\n$_LvmLvNameBody1 $_LvmLvNameBody2 (${LVM_VG_MB}MB).\n " 0 0 "lvol" 2>${ANSWER} || return 0 LVM_LV_NAME=$(cat ${ANSWER}) done # Create the final LV lvcreate -W y -qq -l +100%FREE ${LVM_VG} -n ${LVM_LV_NAME} 2>$ERR check_for_error "lvcreate -W y -qq -l +100%FREE ${LVM_VG} -n ${LVM_LV_NAME}" "$?" NUMBER_LOGICAL_VOLUMES=$(( NUMBER_LOGICAL_VOLUMES - 1 )) LVM=1 DIALOG " $_LvmCreateVG " --yesno "\n$_LvmCompBody\n " 0 0 && show_devices || return 0 } check_lv_size() { LV_SIZE_INVALID=0 chars=0 # Check to see if anything was actually entered and if first character is '0' ([[ ${#LVM_LV_SIZE} -eq 0 ]] || [[ ${LVM_LV_SIZE:0:1} -eq "0" ]]) && LV_SIZE_INVALID=1 # If not invalid so far, check for non numberic characters other than the last character if [[ $LV_SIZE_INVALID -eq 0 ]]; then while [[ $chars -lt $(( ${#LVM_LV_SIZE} - 1 )) ]]; do [[ ${LVM_LV_SIZE:chars:1} != [0-9] ]] && LV_SIZE_INVALID=1 && return 0; chars=$(( chars + 1 )) done fi # If not invalid so far, check that last character is a M/m or G/g if [[ $LV_SIZE_INVALID -eq 0 ]]; then LV_SIZE_TYPE=$(echo ${LVM_LV_SIZE:$(( ${#LVM_LV_SIZE} - 1 )):1}) case $LV_SIZE_TYPE in "m"|"M"|"g"|"G") LV_SIZE_INVALID=0 ;; *) LV_SIZE_INVALID=1 ;; esac fi # If not invalid so far, check whether the value is greater than or equal to the LV remaining Size. # If not, convert into MB for VG space remaining. if [[ ${LV_SIZE_INVALID} -eq 0 ]]; then case ${LV_SIZE_TYPE} in "G"|"g") if [[ $(( $(echo ${LVM_LV_SIZE:0:$(( ${#LVM_LV_SIZE} - 1 ))}) * 1000 )) -ge ${LVM_VG_MB} ]]; then LV_SIZE_INVALID=1 else LVM_VG_MB=$(( LVM_VG_MB - $(( $(echo ${LVM_LV_SIZE:0:$(( ${#LVM_LV_SIZE} - 1 ))}) * 1000 )) )) fi ;; "M"|"m") if [[ $(echo ${LVM_LV_SIZE:0:$(( ${#LVM_LV_SIZE} - 1 ))}) -ge ${LVM_VG_MB} ]]; then LV_SIZE_INVALID=1 else LVM_VG_MB=$(( LVM_VG_MB - $(echo ${LVM_LV_SIZE:0:$(( ${#LVM_LV_SIZE} - 1 ))}) )) fi ;; *) LV_SIZE_INVALID=1 ;; esac fi } lvm_del_vg() { # Generate list of VGs for selection lvm_show_vg # If no VGs, no point in continuing if [[ $VG_LIST == "" ]]; then DIALOG " $_ErrTitle " --msgbox "\n$_LvmVGErr\n " 0 0 return 0 fi # Select VG DIALOG " $_PrepLVM " --menu "\n$_LvmSelVGBody\n " 0 0 5 ${VG_LIST} 2>${ANSWER} || return 0 # Ask for confirmation DIALOG " $_LvmDelVG " --yesno "\n$_LvmDelQ\n " 0 0 # if confirmation given, delete if [[ $? -eq 0 ]]; then vgremove -f $(cat ${ANSWER}) 2>/dev/null check_for_error "delete lvm-VG $(cat ${ANSWER})" fi } lvm_show_vg() { VG_LIST="" vg_list=$(lvs --noheadings | awk '{print $2}' | uniq) for i in ${vg_list}; do VG_LIST="${VG_LIST} ${i} $(vgdisplay ${i} | grep -i "vg size" | awk '{print $3$4}')" done } lvm_del_all() { # check if VG exist at all if [[ $(lvs) == "" ]]; then DIALOG " $_ErrTitle " --msgbox "\n$_LvmVGErr\n " 0 0 return 0 fi LVM_PV=$(pvs -o pv_name --noheading 2>/dev/null) LVM_VG=$(vgs -o vg_name --noheading 2>/dev/null) LVM_LV=$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null) # Ask for confirmation DIALOG " $_LvmDelLV " --yesno "\n$_LvmDelQ\n " 0 0 # if confirmation given, delete if [[ $? -eq 0 ]]; then for i in ${LVM_LV}; do lvremove -f /dev/mapper/${i} 2>/dev/null check_for_error "remove LV ${i}" done for i in ${LVM_VG}; do vgremove -f ${i} 2>/dev/null check_for_error "remove VG ${i}" done for i in ${LV_PV}; do pvremove -f ${i} 2>/dev/null check_for_error "remove LV-PV ${i}" done fi } # returns a list of devices containing zfs members zfs_list_devs() { # get a list of devices with zpools on them for device in $(zpool status -PL 2>/dev/null | awk '{print $1}' | grep "^/"); do # add the device echo $device # now lets add any other forms of those devices find -L /dev/ -xtype l -samefile ${device} 2>/dev/null done } zfs_list_datasets() { case $1 in "zvol") zfs list -Ht volume -o name,volsize 2>/dev/null ;; "legacy") zfs list -Ht filesystem -o name,mountpoint 2>/dev/null | grep "^.*/.*legacy$" | awk '{print $1}' ;; *) zfs list -H -o name 2>/dev/null | grep "/" esac } # creates a new zpool on an existing partition zfs_create_zpool() { # LVM Detection. If detected, activate. lvm_detect INCLUDE_PART='part\|lvm\|crypt' umount_partitions find_partitions list_mounted > /tmp/.ignore_part zfs_list_devs >> /tmp/.ignore_part list_containing_crypt >> /tmp/.ignore_part check_for_error "ignore crypted: $(list_containing_crypt)" for part in $(cat /tmp/.ignore_part); do delete_partition_in_list $part done # Identify the partition for the zpool DIALOG " $_zfsZpoolPartMenuTitle " --menu "\n$_zfsZpoolPartMenuBody\n " 0 0 12 ${PARTITIONS} 2>${ANSWER} || return 1 PARTITION=$(cat ${ANSWER}) # We need to get a name for the zpool local -i loopmenu=1 ZFSMENUTEXT=$_zfsZpoolCBody while ((loopmenu)); do DIALOG " $_zfsZpoolCTitle " --inputbox "\n$ZFSMENUTEXT\n " 0 0 "zpmanjaro" 2>${ANSWER} || return 1 ZFSMENUTEXT=$_zfsZpoolCBody # validation [[ ! $(cat ${ANSWER}) =~ ^[a-zA-Z][a-zA-Z0-9.:_-]*$ ]] && ZFSMENUTEXT=$_zfsZpoolCValidation1 [[ $(cat ${ANSWER}) =~ ^(log|mirror|raidz|raidz1|raidz2|raidz3|spare).*$ ]] && ZFSMENUTEXT=$_zfsZpoolCValidation2 [[ $ZFSMENUTEXT == $_zfsZpoolCBody ]] && loopmenu=0 done ZFS_ZPOOL_NAME=$(cat ${ANSWER}) # Find the UUID of the partition PARTUUID=$(lsblk -lno PATH,PARTUUID | grep "^${PARTITION}" | awk '{print $2}') # See if the partition has a partuuid, if not use the device name if [[ -n ${PARTUUID} ]]; then zpool create -m none ${ZFS_ZPOOL_NAME} ${PARTUUID} 2>$ERR check_for_error "Creating zpool ${ZFS_ZPOOL_NAME} on device ${PARTITION} using partuuid ${PARTUUID}" else zpool create -m none ${ZFS_ZPOOL_NAME} ${PARTITION} 2>$ERR check_for_error "Creating zpool ${ZFS_ZPOOL_NAME} on device ${PARTITION}" fi ZFS=1 # Since zfs manages mountpoints, we export it and then import with a root of $MOUNTPOINT zpool export ${ZFS_ZPOOL_NAME} 2>$ERR zpool import -R ${MOUNTPOINT} ${ZFS_ZPOOL_NAME} 2>>$ERR check_for_error "Export and importing ${ZFS_POOL_NAME}" return 0 } # Creates a zfs filesystem, the first parameter is the ZFS path and the second is the mount path zfs_create_dataset() { local zpath=$1 local zmount=$2 zfs create -o mountpoint=$zmount $zpath 2>$ERR check_for_error "Creating zfs dataset ${zpath} with mountpoint ${zmount}" } # Automated configuration of zfs. Creates a new zpool and a default set of filesystems zfs_auto() { # first we need to create a zpool to hold the datasets/zvols zfs_create_zpool if [ $? != 0 ]; then DIALOG " $_zfsZpoolCTitle " --infobox "\n$_zfsCancelled\n " 0 0 sleep 3 return 0 fi # next create the datasets including their parents zfs_create_dataset "${ZFS_ZPOOL_NAME}/data" "none" zfs_create_dataset "${ZFS_ZPOOL_NAME}/ROOT" "none" zfs_create_dataset "${ZFS_ZPOOL_NAME}/ROOT/manjaro" "none" zfs_create_dataset "${ZFS_ZPOOL_NAME}/ROOT/manjaro/root" "/" zfs_create_dataset "${ZFS_ZPOOL_NAME}/data/home" "/home" zfs_create_dataset "${ZFS_ZPOOL_NAME}/ROOT/manjaro/paccache" "/var/cache/pacman" # set the rootfs zpool set bootfs=${ZFS_ZPOOL_NAME}/ROOT/manjaro/root ${ZFS_ZPOOL_NAME} 2>$ERR check_for_error "Setting zfs bootfs" # provide confirmation to the user DIALOG " $_zfsZpoolCTitle " --infobox "\n$_zfsAutoComplete\n " 0 0 sleep 3 } zfs_import_pool() { local zplist local zpoolitem for zpoolitem in $(zpool import 2>/dev/null | grep "^[[:space:]]*pool" | awk -F : '{print $2}' | awk '{$1=$1};1'); do zplist="${zplist} ${zpoolitem} -" done if [[ ${zplist} ]]; then DIALOG " $_zfsZpoolImportMenuTitle " --menu "\n$_zfsZpoolImportMenuBody\n " 0 0 12 ${zplist} 2>${ANSWER} || return 0 zpool import -R ${MOUNTPOINT} $(cat ${ANSWER}) 2>$ERR check_for_error "Import zpool $(cat ${ANSWER})" ZFS=1 else DIALOG " $_zfsZpoolImportMenuTitle " --infobox "\n$_zfsZpoolNoPool\n " 0 0 sleep 3 fi } # return a list of imported zpools zfs_list_pools() { zpool list -H 2>/dev/null | awk '{print $1}' } zfs_new_ds() { local zplist local zmount=$1 local zpoolitem for zpoolitem in `zfs_list_pools`; do zplist="${zplist} ${zpoolitem} -" done if [[ ${zplist} ]]; then # select a zpool DIALOG " $_zfsSelectZpoolMenuTitle " --menu "\n$_zfsSelectZpoolMenuBody\n " 0 0 12 ${zplist} 2>${ANSWER} || return 0 local zpool=$(cat ${ANSWER}) else # no imported zpools DIALOG " $_zfsSelectZpoolMenuTitle " --infobox "\n$_zfsZpoolNoPool\n " 0 0 sleep 3 return 0 fi # enter a name for the dataset local -i loopmenu=1 local zfsmenubody=$_zfsDSMenuNameBody while ((loopmenu)); do DIALOG " $_zfsDSMenuNameTitle " --inputbox "\n$zfsmenubody\n " 0 0 "" 2>${ANSWER} || return 1 # validation [[ ! $(cat ${ANSWER}) =~ ^[a-zA-Z][a-zA-Z0-9.:/_-]*$ ]] && zfsmenubody=$_zfsZpoolCValidation1 || loopmenu=0 done local zname=$(cat ${ANSWER}) case $zmount in "legacy") zfs_create_dataset ${zpool}/${zname} ${zmount} 2>$ERR ;; "zvol") # get the size of the zvol loopmenu=1 zfsmenubody=$_zfsZvolSizeMenuBody while ((loopmenu)); do DIALOG " $_zfsZvolSizeMenuTitle " --inputbox "\n$zfsmenubody\n " 0 0 "" 2>${ANSWER} || return 1 # validation [[ $(cat ${ANSWER}) =~ ^[0-9]*$ ]] && loopmenu=0 || zfsmenubody=$_zfsZvolSizeMenuValidation done local zsize=$(cat ${ANSWER}) zfs create -V ${zsize}M ${zpool}/${zname} 2>$ERR ;; *) # select a mount point loopmenu=1 zfsmenubody=$_zfsMountMenuBody while ((loopmenu)); do DIALOG " $_zfsMountMenuTitle " --inputbox "\n$zfsmenubody\n " 0 0 "" 2>${ANSWER} || return 0 zmount=$(cat ${ANSWER}) zfsmenubody=$_zfsMountMenuBody # validation [[ $(findmnt -n ${MOUNTPOINT}/${zmount}) ]] && zfsmenubody=$_zfsMountMenuInUse [[ ! ($zmount =~ ^/ || $zmount == none) ]] && zfsmenubody=$_zfsMountMenuNotValid [[ $zfsmenubody == $_zfsMountMenuBody ]] && loopmenu=0 done zfs_create_dataset ${zpool}/${zname} ${zmount} 2>$ERR ;; esac check_for_error "new zfs dataset ${zpool}/${zname} on ${zmount}" } zfs_destroy_dataset() { local zlist # get dataset list and format for the menu local zds for zds in $(zfs_list_datasets); do zlist="${zlist} ${zds} -" done # select the dataset to destroy if [[ ${zlist} ]]; then DIALOG " $_zfsDestroyMenuTitle " --menu "\n$_zfsDestroyMenuBody\n " 0 0 12 ${zlist} 2>${ANSWER} || return 0 local zdataset=$(cat ${ANSWER}) else # no available datasets DIALOG " $_zfsDestroyMenuTitle " --infobox "\n$_zfsDatasetNotFound\n " 0 0 sleep 3 return 0 fi # better confirm this one DIALOG --defaultno --yesno "$_zfsDestroyMenuConfirm1 ${zdataset} $_zfsDestroyMenuConfirm2" 0 0 if [ $? ]; then zfs destroy -r ${zdataset} check_for_error "zfs destroy ${zdataset}" fi } zfs_set_property () { local zlist # get dataset list and format for the menu local zds for zds in $(zfs_list_datasets); do zlist="${zlist} ${zds} -" done # select the dataset if [[ ${zlist} ]]; then DIALOG " $_zfsSetMenuTitle " --menu "\n$_zfsSetMenuBody\n " 0 0 12 ${zlist} 2>${ANSWER} || return 0 local zdataset=$(cat ${ANSWER}) else # no available datasets DIALOG " $_zfsSetMenuTitle " --infobox "\n$_zfsDatasetNotFound\n " 0 0 sleep 3 return 0 fi # get property/value input local -i loopmenu=1 zfsmenubody=$_zfsSetMenuBody local zsetstmt while ((loopmenu)); do DIALOG " $_zfsSetMenuTitle " --inputbox "\n$zfsmenubody\n " 0 0 "" 2>${ANSWER} || return 0 zsetstmt=$(cat ${ANSWER}) # validation [[ ! $zsetstmt =~ ^[a-zA-Z@]*=[a-zA-Z0-9@-]*$ ]] && zfsmenubody=$_zfsMountMenuNotValid || loopmenu=0 done #set the property zfs set ${zsetstmt} ${zdataset} 2>$ERR check_for_error "zfs set ${zsetstmt} on ${zdataset}" } zfs_menu_manual() { local -i loopmenu=1 while ((loopmenu)); do DIALOG " $_zfsManualMenuTitle " --menu "\n$_zfsManualMenuBody\n " 22 60 8 \ "$_zfsManualMenuOptCreate" "" \ "$_zfsManualMenuOptImport" "" \ "$_zfsManualMenuOptNewFile" "" \ "$_zfsManualMenuOptNewLegacy" "" \ "$_zfsManualMenuOptNewZvol" "" \ "$_zfsManualMenuOptSet" "" \ "$_zfsManualMenuOptDestroy" "" \ "$_Back" "" 2>${ANSWER} case $(cat ${ANSWER}) in "$_zfsManualMenuOptCreate") zfs_create_zpool ;; "$_zfsManualMenuOptImport") zfs_import_pool ;; "$_zfsManualMenuOptNewFile") zfs_new_ds ;; "$_zfsManualMenuOptNewLegacy") zfs_new_ds "legacy" ;; "$_zfsManualMenuOptNewZvol") zfs_new_ds "zvol" ;; "$_zfsManualMenuOptSet") zfs_set_property ;; "$_zfsManualMenuOptDestroy") zfs_destroy_dataset ;; *) loopmenu=0 return 0 ;; esac done } # The main ZFS menu zfs_menu() { # check for zfs support modprobe zfs 2>$ERR if [[ $(cat $ERR) ]]; then DIALOG " $_zfsZpoolCTitle " --infobox "\n$_zfsNotSupported\n " 0 0 sleep 3 return 0 fi declare -i loopmenu=1 while ((loopmenu)); do DIALOG " $_PrepZFS " --menu "\n$_zfsMainMenuBody\n " 22 60 3 \ "$_zfsMainMenuOptAutomatic" "" \ "$_zfsMainMenuOptManual" "" \ "$_Back" "" 2>${ANSWER} case $(cat ${ANSWER}) in "$_zfsMainMenuOptAutomatic") zfs_auto ;; "$_zfsMainMenuOptManual") zfs_menu_manual ;; *) loopmenu=0 return 0 ;; esac done } make_esp() { # Extra Step for VFAT UEFI Partition. This cannot be in an LVM container. if [[ $SYSTEM == "UEFI" ]]; then if DIALOG " $_PrepMntPart " --menu "\n$_SelUefiBody\n " 0 0 12 ${PARTITIONS} 2>${ANSWER}; then PARTITION=$(cat ${ANSWER}) UEFI_PART=${PARTITION} # If it is already a fat/vfat partition... if [[ $(fsck -N $PARTITION | grep fat) ]]; then DIALOG " $_PrepMntPart " --defaultno --yesno "\n$_FormUefiBody $PARTITION $_FormUefiBody2\n " 0 0 && { mkfs.vfat -F32 ${PARTITION} >/dev/null 2>$ERR check_for_error "mkfs.vfat -F32 ${PARTITION}" "$?" } # || return 0 else mkfs.vfat -F32 ${PARTITION} >/dev/null 2>$ERR check_for_error "mkfs.vfat -F32 ${PARTITION}" "$?" fi if [[ "$LUKS" == 0 ]]; then _MntUefiMessage="$_MntUefiBody" else _MntUefiMessage="$_MntUefiCrypt" fi DIALOG " $_PrepMntPart " --radiolist "\n$_MntUefiMessage\n " 0 0 2 \ "/boot/efi" "" on \ "/boot" "" off 2>${ANSWER} if [[ $(cat ${ANSWER}) != "" ]]; then UEFI_MOUNT=$(cat ${ANSWER}) mkdir -p ${MOUNTPOINT}${UEFI_MOUNT} 2>$ERR check_for_error "create ${MOUNTPOINT}${UEFI_MOUNT}" $? mount ${PARTITION} ${MOUNTPOINT}${UEFI_MOUNT} 2>$ERR check_for_error "mount ${PARTITION} ${MOUNTPOINT}${UEFI_MOUNT}" $? if confirm_mount ${MOUNTPOINT}${UEFI_MOUNT}; then ini mount.efi "${UEFI_MOUNT}" delete_partition_in_list "$PARTITION" fi fi fi fi } mount_partitions() { # Warn users that they CAN mount partitions without formatting them! DIALOG " $_PrepMntPart " --msgbox "\n$_WarnMount1 '$_FSSkip' $_WarnMount2\n " 15 65 # LVM Detection. If detected, activate. lvm_detect # Ensure partitions are unmounted (i.e. where mounted previously) INCLUDE_PART='part\|lvm\|crypt' umount_partitions # We need to remount the zfs filesystems that have defined mountpoints already zfs mount -aO 2>/dev/null # Get list of available partitions find_partitions # Add legacy zfs filesystems to the list - these can be mounted but not formatted for i in $(zfs_list_datasets "legacy"); do PARTITIONS="${PARTITIONS} ${i}" PARTITIONS="${PARTITIONS} zfs" NUMBER_PARTITIONS=$(( NUMBER_PARTITIONS + 1 )) done # Filter out partitions that have already been mounted and partitions that just contain crypt or zfs devices list_mounted > /tmp/.ignore_part zfs_list_devs >> /tmp/.ignore_part list_containing_crypt >> /tmp/.ignore_part check_for_error "ignore crypted: $(list_containing_crypt)" for part in $(cat /tmp/.ignore_part); do delete_partition_in_list $part done # check to see if we already have a zfs root mounted if [ $(findmnt -ln -o FSTYPE ${MOUNTPOINT}) == "zfs" ]; then DIALOG " $_PrepMntPart " --infobox "\n$_zfsFoundRoot\n " 0 0 sleep 3 else # Identify and mount root DIALOG " $_PrepMntPart " --menu "\n$_SelRootBody\n " 0 0 12 ${PARTITIONS} 2>${ANSWER} || return 0 PARTITION=$(cat ${ANSWER}) ROOT_PART=${PARTITION} echo ${ROOT_PART} > /tmp/.root_partitioni echo ${ROOT_PART} > /tmp/.root_partition # Reset the mountpoint variable, in case this is the second time through this menu and old state is still around MOUNT="" # Format with FS (or skip) -> # Make the directory and mount. Also identify LUKS and/or LVM select_filesystem && mount_current_partition || return 0 ini mount.root "${PARTITION}" delete_partition_in_list "${ROOT_PART}" # Extra check if root is on LUKS or lvm get_cryptroot echo "$LUKS_DEV" > /tmp/.luks_dev # If the root partition is btrfs, offer to create subvolumus if [[ $(findmnt -no FSTYPE ${MOUNTPOINT}) == btrfs ]]; then # Check if there are subvolumes already on the btrfs partition if [[ $(btrfs subvolume list ${MOUNTPOINT} | wc -l) -gt 1 ]] && DIALOG " The volume has already subvolumes " --yesno "\nFound subvolumes $(btrfs subvolume list ${MOUNTPOUNT} | cut -d" " -f9)\n\nWould you like to mount them? \n " 0 0; then # Pre-existing subvolumes and user wants to mount them mount_existing_subvols else # No subvolumes present. Make some new ones DIALOG " Your root volume is formatted in btrfs " --yesno "\nWould you like to create subvolumes in it? \n " 0 0 && btrfs_subvolumes fi fi fi # We need to remove legacy zfs partitions before make_swap since they can't hold swap local zlegacy for zlegacy in $(zfs_list_datasets "legacy"); do delete_partition_in_list ${zlegacy} done # Identify and create swap, if applicable make_swap # Now that swap is done we put the legacy partitions back, unless they are already mounted for i in $(zfs_list_datasets "legacy"); do PARTITIONS="${PARTITIONS} ${i}" PARTITIONS="${PARTITIONS} zfs" NUMBER_PARTITIONS=$(( NUMBER_PARTITIONS + 1 )) done for part in $(list_mounted); do delete_partition_in_list $part done # All other partitions while [[ $NUMBER_PARTITIONS > 0 ]]; do DIALOG " $_PrepMntPart " --menu "\n$_ExtPartBody\n " 0 0 12 "$_Done" $"-" ${PARTITIONS} 2>${ANSWER} || return 0 PARTITION=$(cat ${ANSWER}) if [[ $PARTITION == $_Done ]]; then make_esp get_cryptroot get_cryptboot echo "$LUKS_DEV" > /tmp/.luks_dev return 0; else MOUNT="" select_filesystem # Ask user for mountpoint. Don't give /boot as an example for UEFI systems! [[ $SYSTEM == "UEFI" ]] && MNT_EXAMPLES="/home\n/var" || MNT_EXAMPLES="/boot\n/home\n/var" DIALOG " $_PrepMntPart $PARTITON " --inputbox "\n$_ExtPartBody1$MNT_EXAMPLES\n " 0 0 "/" 2>${ANSWER} || return 0 MOUNT=$(cat ${ANSWER}) # loop while the mountpoint specified is incorrect (is only '/', is blank, or has spaces). while [[ ${MOUNT:0:1} != "/" ]] || [[ ${#MOUNT} -le 1 ]] || [[ $MOUNT =~ \ |\' ]]; do # Warn user about naming convention DIALOG " $_ErrTitle " --msgbox "\n$_ExtErrBody\n " 0 0 # Ask user for mountpoint again DIALOG " $_PrepMntPart $PARTITON " --inputbox "\n$_ExtPartBody1$MNT_EXAMPLES\n " 0 0 "/" 2>${ANSWER} || return 0 MOUNT=$(cat ${ANSWER}) done # Create directory and mount. mount_current_partition delete_partition_in_list "$PARTITION" # Determine if a seperate /boot is used. 0 = no seperate boot, 1 = seperate non-lvm boot, # 2 = seperate lvm boot. For Grub configuration if [[ $MOUNT == "/boot" ]]; then [[ $(lsblk -lno TYPE ${PARTITION} | grep "lvm") != "" ]] && LVM_SEP_BOOT=2 || LVM_SEP_BOOT=1 fi fi done } get_cryptroot() { # Identify if /mnt or partition is type "crypt" (LUKS on LVM, or LUKS alone) if $(lsblk | sed -r 's/^[^[:alnum:]]+//' | awk '/\/mnt$/ {print $6}' | grep -q crypt) || $(lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e "/\/mnt$/,/part/p" | awk '{print $6}' | grep -q crypt); then LUKS=1 root_name=$(mount | awk '/\/mnt / {print $1}' | sed s~/dev/mapper/~~g | sed s~/dev/~~g) #Get the name of the Luks device if $(lsblk -i | grep -q -e "crypt /mnt"); then # Mountpoint is directly on the LUKS device, so LUKS deivece is the same as root name LUKS_ROOT_NAME="$root_name" else # Mountpoint is not directly on LUKS device, so we need to get the crypt device above the mountpoint LUKS_ROOT_NAME="$(lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e "/\/mnt$/,/crypt/p" | awk '/crypt/ {print $1}')" fi # Check if LUKS on LVM if [[ $(lsblk -lno NAME,FSTYPE,TYPE,MOUNTPOINT | grep "lvm" | grep "/mnt$" | grep -i "crypto_luks" | uniq | awk '{print "/dev/mapper/"$1}') != "" ]]; then cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE,MOUNTPOINT | grep "lvm" | grep "/mnt$" | grep -i "crypto_luks" | uniq | awk '{print "/dev/mapper/"$1}') for i in ${cryptparts}; do if [[ $(lsblk -lno NAME ${i} | grep $LUKS_ROOT_NAME) != "" ]]; then LUKS_DEV="cryptdevice=${i}:$LUKS_ROOT_NAME" LVM=1 fi done fi # Check if LVM on LUKS if [[ $(lsblk -lno NAME,FSTYPE,TYPE | grep " crypt$" | grep -i "LVM2_member" | uniq | awk '{print "/dev/mapper/"$1}') != "" ]]; then cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE | grep " crypt$" | grep -i "LVM2_member" | uniq | awk '{print "/dev/mapper/"$1}') for i in ${cryptparts}; do if [[ $(lsblk -lno NAME ${i} | grep $LUKS_ROOT_NAME) != "" ]]; then LUKS_UUID=$(lsblk -ino NAME,FSTYPE,TYPE,MOUNTPOINT,UUID | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e "/\/mnt /,/part/p" | awk '/crypto_LUKS/ {print $4}') LUKS_DEV="cryptdevice=UUID=$LUKS_UUID:$LUKS_ROOT_NAME" LVM=1 fi done fi # Check if LUKS alone if [[ $(lsblk -lno NAME,FSTYPE,TYPE | grep "part" | grep -i "crypto_luks" | uniq | awk '{print "/dev/"$1}') != "" ]]; then cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE,MOUNTPOINT | grep "/mnt$" | grep "part" | grep -i "crypto_luks" | uniq | awk '{print "/dev/"$1}') for i in ${cryptparts}; do if [[ $(lsblk -lno NAME ${i} | grep $LUKS_ROOT_NAME) != "" ]]; then LUKS_UUID=$(lsblk -lno UUID,TYPE,FSTYPE ${i} | grep "part" | grep -i "crypto_luks" | awk '{print $1}') LUKS_DEV="cryptdevice=UUID=$LUKS_UUID:$LUKS_ROOT_NAME" fi done fi echo "$LUKS_DEV" > /tmp/.luks_dev fi } get_cryptboot(){ # If /boot is encrypted if $(lsblk | sed -r 's/^[^[:alnum:]]+//' | awk '/\/mnt\/boot$/ {print $6}' | grep -q crypt) || $(lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e "/\/mnt\/boot$/,/part/p" | awk '{print $6}' | grep -q crypt); then LUKS=1 boot_name=$(mount | awk '/\/mnt\/boot / {print $1}' | sed s~/dev/mapper/~~g | sed s~/dev/~~g) #Get the name of the Luks device if $(lsblk -i | grep -q -e "crypt /mnt"); then # Mountpoint is directly on the LUKS device, so LUKS deivece is the same as root name LUKS_BOOT_NAME="$boot_name" # Get UUID of the encrypted /boot LUKS_BOOT_UUID=$(lsblk -lno UUID,MOUNTPOINT | awk '/\mnt\/boot$/ {print $1}') else # Mountpoint is not directly on LUKS device, so we need to get the crypt device above the mountpoint LUKS_BOOT_NAME="$(lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e "/\/mnt\/boot$/,/crypt/p" | awk '/crypt/ {print $1}')" # Get UUID of the encrypted /boot LUKS_BOOT_UUID=$(lsblk -ino NAME,FSTYPE,TYPE,MOUNTPOINT,UUID | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e "/\/mnt\/boot /,/part/p" | awk '/crypto_LUKS/ {print $4}') fi # Check if LVM on LUKS if $(lsblk -lno TYPE,MOUNTPOINT | grep "/mnt/boot$" | grep -q lvm); then LVM=1 fi # Add Cryptdevice to LUKS_DEV, if not already present (if on same LVM on LUKS as /) if [[ $(echo $LUKS_DEV | grep $LUKS_BOOT_UUID) == "" ]]; then LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_BOOT_UUID:$LUKS_BOOT_NAME" fi echo "$LUKS_DEV" > /tmp/.luks_dev fi } btrfs_subvolumes() { #1) save mount options and name of the root partition mount | grep "on /mnt " | grep -Po '(?<=\().*(?=\))' > /tmp/.root_mount_options #lsblk -lno MOUNTPOINT,NAME | awk '/^\/mnt / {print $2}' > /tmp/.root_partition #2) choose automatic or manual mode DIALOG " Choose mode " --menu "\n$_Note\nAutomatic mode is designed to\nallow integration with snapper,\nnon-recursive snapshots,\nseparating system and user data\nand restoring snapshots without losing data. " 0 0 2 \ "1" "automatic" \ "2" "manual" 2>/tmp/.subvol_mode if [[ $(cat /tmp/.subvol_mode) != "" ]]; then if [[ $(cat /tmp/.subvol_mode) -eq 2 ]]; then # Create subvolumes manually DIALOG " Create subvolumes " --inputbox "\nInput names of the subvolumes separated by spaces. The first one will be used for mounting /." 0 0 "@ @home @cache" 2>/tmp/.subvols || return 0 cd /mnt for subvol in $(cat /tmp/.subvols); do btrfs subvolume create $subvol done cd # Mount subvolumes umount /mnt # Mount the first subvolume as / mount -o $(cat ${MOUNT_OPTS}),subvol="$(awk '{print $1}' /tmp/.subvols)" "$(cat /tmp/.root_partition)" /mnt # Remove the first subvolume from the subvolume list sed -i -r 's/(\s+)?\S+//1' /tmp/.subvols # Loop to mount all created subvolumes for sub in $(cat /tmp/.subvols); do DIALOG "Mount subvolume $sub" --inputbox "\nInput mountpoint of the subvolume $sub\nas it would appear in installed system\n(without prepending /mnt)\n." 0 0 "/home" 2>/tmp/.mountp || return 0 mkdir -p /mnt/"$(cat /tmp/.mountp)" mount -o $(cat ${MOUNT_OPTS}),subvol="$sub" "$(cat /tmp/.root_partition)" /mnt"$(cat /tmp/.mountp)" done else DIALOG " Automatic btrfs subvolumes" --yesno "\nThis creates subvolumes @ for /,@home for /home, @cache for /var/cache. \n " 0 0 || return 0 # Create subvolumes automatically cd /mnt btrfs subvolume create @ btrfs subvolume create @home btrfs subvolume create @cache #btrfs subvolume create @snapshots cd # Mount subvolumes umount /mnt mount -o $(cat ${MOUNT_OPTS}),subvol=@ "$(cat /tmp/.root_partition)" /mnt mkdir -p /mnt/home mkdir -p /mnt/var/cache mount -o $(cat ${MOUNT_OPTS}),subvol=@home "$(cat /tmp/.root_partition)" /mnt/home #mount -o $(cat ${MOUNT_OPTS}),subvol=@cache "$(cat /tmp/.root_partition)" /mnt/var/cache fi else return 0 fi } mount_existing_subvols() { # Set mount options format_name=$(echo ${PARTITION} | rev | cut -d/ -f1 | rev) format_device=$(lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e "/$format_name/,/disk/p" | awk '/disk/ {print $1}') if [[ "$(cat /sys/block/${format_device}/queue/rotational)" == 1 ]]; then fs_opts="autodefrag,compress=zlib,noatime,nossd,commit=120" else fs_opts="compress=lzo,noatime,space_cache,ssd,commit=120" fi btrfs subvolume list /mnt | cut -d" " -f9 > /tmp/.subvols umount /mnt # Mount subvolumes one by one for subvol in $(cat /tmp/.subvols); do # Ask for mountpoint DIALOG "Mount subvolume $subvol" --inputbox "\nInput mountpoint of the subvolume $subvol\nas it would appear in installed system\n(without prepending /mnt).\n" 0 0 "/" 2>/tmp/.mountp || return 0 [[ -e "/mnt/$(cat /tmp/.mountp)" ]] || mkdir -p /mnt/"$(cat /tmp/.mountp)" # Mount the subvolume mount -o "${fs_opts},subvol=$subvol" "$(cat /tmp/.root_partition)" /mnt"$(cat /tmp/.mountp)" done }