From 71789d67630f6a7a9a8c3314884223017410c67c Mon Sep 17 00:00:00 2001 From: Evan James Date: Tue, 1 Jan 2019 20:32:29 -0600 Subject: [PATCH] Basic zfs partitioning and mounting --- data/translations/english.trans | 24 ++++ lib/util-base.sh | 2 +- lib/util-disk.sh | 231 +++++++++++++++++++++++++++----- lib/util-menu.sh | 25 ++-- lib/util.sh | 2 + 5 files changed, 242 insertions(+), 42 deletions(-) diff --git a/data/translations/english.trans b/data/translations/english.trans index 93bbdf1..3715649 100644 --- a/data/translations/english.trans +++ b/data/translations/english.trans @@ -54,6 +54,30 @@ _btrfsSVBody3="This process will be repeated until an asterisk (*) is entered as _btrfsMntBody="Use [Space] to de/select the desired mount options and review carefully. Please do not select multiple versions of the same option." _btrfsMntConfBody="Confirm the following mount options:" +#zfs +_PrepZFS="ZFS (optional)" +_zfsNotSupported="The kernel modules to support ZFS could not be found" +_zfsAutoComplete="Automatic zfs provisioning has been completed" +_zfsMainMenuBody="ZFS is a flexible and resilient file system that combines elements of logical volume management, RAID and traditional file systems. ZFS on Linux requires special handling and is not ideal for beginners.\n\nSelect automatic to select a partition and allow the system to automate the creation a new a zpool and datasets mounted to '/', '/home' and '/var/cache/pacman'. Manual configuration is available but requires specific knowledge of zfs." +_zfsMainMenuOptAutomatic="Automatically configure" +_zfsMainMenuOptManual="Manual configuration" +_zfsManualMenuTitle="ZFS Manual Setup" +_zfsManualMenuBody="Please select an option below" +_zfsManualMenuOptCreate="Create a new zpool" +_zfsManualMenuOptImport="Import an existing zpool" +_zfsManualMenuOptNewFile="Create and mount a ZFS filesystem" +_zfsManualMenuOptNewLegacy="Create a legacy ZFS filesystem" +_zfsManualMenuOptNewZvol="Create a new ZVOL" +_zfsManualMenuOptDestroy="Destroy a ZFS dataset" +_zfsZpoolPartMenuTitle="Select a partition" +_zfsZpoolPartMenuBody="Select a partition to hold the ZFS zpool" +_zfsZpoolCTitle="zpool Creation" +_zfsZpoolCBody="Enter the name for the new zpool" +_zfsZpoolCValidation1="zpool names must start with a letter and are limited to only alphanumeric characters and the special characters : . - _" +_zfsZpoolCValidation2="zpool names cannot start with the reserved words (log, mirror, raidz, raidz1, raidz2, raidz3, or spare)" +_zfsCancelled="Operation cancelled" +_zfsFoundRoot="Using ZFS root on '/'" + # Autopartition _AutoPartBody1="Warning: ALL data on" _AutoPartBody2="will be destroyed.\n\nA 512MB boot partition will first be created, followed by a second (root or '/') partition using all remaining space." diff --git a/lib/util-base.sh b/lib/util-base.sh index 5a60f32..521fbcb 100644 --- a/lib/util-base.sh +++ b/lib/util-base.sh @@ -74,7 +74,7 @@ enable_services() { arch_chroot "systemctl enable zfs-import.target" 2>$ERR check_for_error "enable zfs-import.target" "$?" # we also need create the cachefile - zpool set cachefile=/etc/zfs/zpool.cache zpmain 2>$ERR + zpool set cachefile=/etc/zfs/zpool.cache $(findmnt ${MOUNTPOINT} -lno SOURCE | awk -F / '{print $1}') 2>$ERR check_for_error "create zpool cache" "$?" cp /etc/zfs/zpool.cache ${MOUNTPOINT}/etc/zfs/zpool.cache 2>$ERR check_for_error "copy cache file" "$?" diff --git a/lib/util-disk.sh b/lib/util-disk.sh index a550d34..20875a1 100644 --- a/lib/util-disk.sh +++ b/lib/util-disk.sh @@ -881,6 +881,162 @@ lvm_del_all() { fi } +# returns a list of devices containing zfs members +list_zfs_devs() { + zpool status -PL 2>/dev/null | awk '{print $1}' | grep "^/" +} + +# 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 + 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 + declare -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}') + + # Create the zpool + zpool create -m none ${ZFS_ZPOOL_NAME} ${PARTUUID} 2>$ERR + check_for_error "Creating zpool ${ZFS_ZPOOL_NAME} on device ${PARTITION} using partuuid ${PARTUUID}" + + 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=$ZFS_ZPOOL_NAME/$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 "data" "none" + zfs_create_dataset "ROOT" "none" + zfs_create_dataset "ROOT/manjaro" "none" + zfs_create_dataset "ROOT/manjaro/root" "/" + zfs_create_dataset "data/home" "/home" + zfs_create_dataset "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_menu_manual() { + declare -i loopmenu=1 + while ((loopmenu)); do + DIALOG " $_zfsManualMenuTitle " --menu "\n$_zfsManualMenuBody\n " 22 60 7 \ + "$_zfsManualMenuOptCreate" "" \ + "$_zfsManualMenuOptImport" "" \ + "$_zfsManualMenuOptNewFile" "" \ + "$_zfsManualMenuOptNewLegacy" "" \ + "$_zfsManualMenuOptNewZvol" "" \ + "$_zfsManualMenuOptDestroy" "" \ + "$_Back" "" 2>${ANSWER} + + case $(cat ${ANSWER}) in + "$_zfsManualMenuOptCreate") zfs_create_zpool + ;; + "$_zfsManualMenuOptImport") return 0 + ;; + "$_zfsManualMenuOptNewFile") return 0 + ;; + "$_zfsManualMenuOptNewLegacy") return 0 + ;; + "$_zfsManualMenuOptNewZvol") return 0 + ;; + "$_zfsManualMenuOptDestroy") return 0 + ;; + *) 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 @@ -922,6 +1078,7 @@ make_esp() { fi fi } + mount_partitions() { # Warn users that they CAN mount partitions without formatting them! DIALOG " $_PrepMntPart " --msgbox "\n$_WarnMount1 '$_FSSkip' $_WarnMount2\n " 15 65 @@ -929,12 +1086,19 @@ mount_partitions() { # LVM Detection. If detected, activate. lvm_detect - # Ensure partitions are unmounted (i.e. where mounted previously), and then list available partitions + # 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 - # Filter out partitions that have already been mounted and partitions that just contain crypt device + + # Filter out partitions that have already been mounted and partitions that just contain crypt or zfs devices list_mounted > /tmp/.ignore_part + list_zfs_devs >> /tmp/.ignore_part list_containing_crypt >> /tmp/.ignore_part check_for_error "ignore crypted: $(list_containing_crypt)" @@ -942,34 +1106,40 @@ mount_partitions() { delete_partition_in_list $part done - # 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 - # 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 [[ $(lsblk -lno FSTYPE,MOUNTPOINT | awk '/ \/mnt$/ {print $1}') == btrfs ]]; then - # Check if there are subvolumes already on the btrfs partition - if [[ $(btrfs subvolume list /mnt | wc -l) -gt 1 ]] && DIALOG " The volume has already subvolumes " --yesno "\nFound subvolumes $(btrfs subvolume list /mnt | 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 && touch /tmp/.btrfsroot - fi - else - [[ -e /tmp/.btrfsroot ]] && rm /tmp/.btrfsroot - fi + # check to see if we already have a zfs root device 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 + # 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 [[ $(lsblk -lno FSTYPE,MOUNTPOINT | awk '/ \/mnt$/ {print $1}') == btrfs ]]; then + # Check if there are subvolumes already on the btrfs partition + if [[ $(btrfs subvolume list /mnt | wc -l) -gt 1 ]] && DIALOG " The volume has already subvolumes " --yesno "\nFound subvolumes $(btrfs subvolume list /mnt | 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 && touch /tmp/.btrfsroot + fi + else + [[ -e /tmp/.btrfsroot ]] && rm /tmp/.btrfsroot + fi + fi # Identify and create swap, if applicable make_swap @@ -1097,6 +1267,7 @@ get_cryptboot(){ fi } + btrfs_subvolumes() { #1) save mount options and name of the root partition mount | grep "on /mnt " | grep -Po '(?<=\().*(?=\))' > /tmp/.root_mount_options diff --git a/lib/util-menu.sh b/lib/util-menu.sh index fd5ad40..4e1ff1c 100644 --- a/lib/util-menu.sh +++ b/lib/util-menu.sh @@ -223,18 +223,19 @@ prep_menu() { local PARENT="$FUNCNAME" declare -i loopmenu=1 while ((loopmenu)); do - submenu 9 - DIALOG " $_PrepMenuTitle " --default-item ${HIGHLIGHT_SUB} --menu "\n$_PrepMenuBody\n " 20 60 10 \ + submenu 10 + DIALOG " $_PrepMenuTitle " --default-item ${HIGHLIGHT_SUB} --menu "\n$_PrepMenuBody\n " 20 60 11 \ "1" "$_VCKeymapTitle" \ "2" "$_DevShowOpt" \ "3" "$_PrepPartDisk|>" \ "4" "$_PrepLUKS|>" \ "5" "$_PrepLVM $_PrepLVM2|>" \ - "6" "$_PrepMntPart" \ - "7" "$_PrepMirror|>" \ - "8" "$_PrepPacKey" \ - "9" "$_HostCache" \ - "10" "$_Back" 2>${ANSWER} + "6" "$_PrepZFS|>" \ + "7" "$_PrepMntPart" \ + "8" "$_PrepMirror|>" \ + "9" "$_PrepPacKey" \ + "10" "$_HostCache" \ + "11" "$_Back" 2>${ANSWER} HIGHLIGHT_SUB=$(cat ${ANSWER}) case $(cat ${ANSWER}) in @@ -250,11 +251,13 @@ prep_menu() { ;; "5") lvm_menu ;; - "6") mount_partitions + "6") zfs_menu + ;; + "7") mount_partitions ;; - "7") configure_mirrorlist + "8") configure_mirrorlist ;; - "8") clear + "9") clear ( ctrlc(){ return 0 @@ -265,7 +268,7 @@ prep_menu() { check_for_error 'refresh pacman-keys' ) ;; - "9") set_cache + "10") set_cache ;; *) loopmenu=0 return 0 diff --git a/lib/util.sh b/lib/util.sh index 4ba3cbd..0f355ba 100644 --- a/lib/util.sh +++ b/lib/util.sh @@ -64,6 +64,8 @@ VG_PARTS="" LVM_SEP_BOOT=0 # 1 = Seperate /boot, 2 = seperate /boot & LVM LV_SIZE_INVALID=0 # Is LVM LV size entered valid? VG_SIZE_TYPE="" # Is VG in Gigabytes or Megabytes? +ZFS=0 +ZFS_ZPOOL_NAME="" # Mounting MOUNT="" # Installation: All other mounts branching