1#!/bin/sh 2#- 3# Copyright (c) 2013 Allan Jude 4# Copyright (c) 2013 Devin Teske 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28# $FreeBSD$ 29# 30############################################################ INCLUDES 31 32BSDCFG_SHARE="/usr/share/bsdconfig" 33. $BSDCFG_SHARE/common.subr || exit 1 34f_dprintf "%s: loading includes..." "$0" 35f_include $BSDCFG_SHARE/device.subr 36f_include $BSDCFG_SHARE/dialog.subr 37f_include $BSDCFG_SHARE/password/password.subr 38f_include $BSDCFG_SHARE/variable.subr 39 40############################################################ CONFIGURATION 41 42# 43# Default name of the boot-pool 44# 45: ${ZFSBOOT_POOL_NAME:=zroot} 46 47# 48# Default name for the boot environment parent dataset 49# 50: ${ZFSBOOT_BEROOT_NAME:=ROOT} 51 52# 53# Default name for the primany boot environment 54# 55: ${ZFSBOOT_BOOTFS_NAME:=default} 56 57# 58# Default Virtual Device (vdev) type to create 59# 60: ${ZFSBOOT_VDEV_TYPE:=stripe} 61 62# 63# Should we use gnop(8) to configure a transparent mapping to 4K sectors? 64# 65: ${ZFSBOOT_GNOP_4K_FORCE_ALIGN:=1} 66 67# 68# Should we use geli(8) to encrypt the drives? 69# NB: Automatically enables ZFSBOOT_BOOT_POOL 70# 71: ${ZFSBOOT_GELI_ENCRYPTION=} 72 73# 74# Default path to the geli(8) keyfile used in drive encryption 75# 76: ${ZFSBOOT_GELI_KEY_FILE:=/boot/encryption.key} 77 78# 79# Create a separate boot pool? 80# NB: Automatically set when using geli(8) or MBR 81# 82: ${ZFSBOOT_BOOT_POOL=} 83 84# 85# Default name for boot pool when enabled (e.g., geli(8) or MBR) 86# 87: ${ZFSBOOT_BOOT_POOL_NAME:=bootpool} 88 89# 90# Default size for boot pool when enabled (e.g., geli(8) or MBR) 91# 92: ${ZFSBOOT_BOOT_POOL_SIZE:=2g} 93 94# 95# Default disks to use (always empty unless being scripted) 96# 97: ${ZFSBOOT_DISKS:=} 98 99# 100# Default partitioning scheme to use on disks 101# 102: ${ZFSBOOT_PARTITION_SCHEME:=GPT} 103 104# 105# How much swap to put on each block device in the boot zpool 106# NOTE: Value passed to gpart(8); which supports SI unit suffixes. 107# 108: ${ZFSBOOT_SWAP_SIZE:=2g} 109 110# 111# Should we use geli(8) to encrypt the swap? 112# 113: ${ZFSBOOT_SWAP_ENCRYPTION=} 114 115# 116# Should we use gmirror(8) to mirror the swap? 117# 118: ${ZFSBOOT_SWAP_MIRROR=} 119 120# 121# Default ZFS datasets for root zpool 122# 123# NOTE: Requires /tmp, /var/tmp, /$ZFSBOOT_BOOTFS_NAME/$ZFSBOOT_BOOTFS_NAME 124# NOTE: Anything after pound/hash character [#] is ignored as a comment. 125# 126f_isset ZFSBOOT_DATASETS || ZFSBOOT_DATASETS=" 127 # DATASET OPTIONS (comma or space separated; or both) 128 129 # Boot Environment [BE] root and default boot dataset 130 /$ZFSBOOT_BEROOT_NAME mountpoint=none 131 /$ZFSBOOT_BEROOT_NAME/$ZFSBOOT_BOOTFS_NAME mountpoint=/ 132 133 # Compress /tmp, allow exec but not setuid 134 /tmp mountpoint=/tmp,compression=lz4,exec=on,setuid=off 135 136 # Don't mount /usr so that 'base' files go to the BEROOT 137 /usr mountpoint=/usr,canmount=off 138 139 # Home directories separated so they are common to all BEs 140 /usr/home # NB: /home is a symlink to /usr/home 141 142 # Ports tree 143 /usr/ports compression=lz4,setuid=off 144 145 # Source tree (compressed) 146 /usr/src compression=lz4,exec=off,setuid=off 147 148 # Create /var and friends 149 /var mountpoint=/var 150 /var/crash compression=lz4,exec=off,setuid=off 151 /var/log compression=lz4,exec=off,setuid=off 152 /var/mail compression=lz4,atime=on 153 /var/tmp compression=lz4,exec=on,setuid=off 154" # END-QUOTE 155 156# 157# If interactive and the user has not explicitly chosen a vdev type or disks, 158# make the user confirm scripted/default choices when proceeding to install. 159# 160: ${ZFSBOOT_CONFIRM_LAYOUT:=1} 161 162############################################################ GLOBALS 163 164# 165# Format of a line in printf(1) syntax to add to fstab(5) 166# 167FSTAB_FMT="%s\t\t%s\t%s\t%s\t\t%s\t%s\n" 168 169# 170# Command strings for various tasks 171# 172CHMOD_MODE='chmod %s "%s"' 173DD_WITH_OPTIONS='dd if="%s" of="%s" %s' 174ECHO_APPEND='echo "%s" >> "%s"' 175GELI_ATTACH='geli attach -j - -k "%s" "%s"' 176GELI_DETACH_F='geli detach -f "%s"' 177GELI_PASSWORD_INIT='geli init -b -B "%s" -e %s -J - -K "%s" -l 256 -s 4096 "%s"' 178GNOP_CREATE='gnop create -S 4096 "%s"' 179GNOP_DESTROY='gnop destroy "%s"' 180GPART_ADD='gpart add -t %s "%s"' 181GPART_ADD_INDEX='gpart add -i %s -t %s "%s"' 182GPART_ADD_INDEX_WITH_SIZE='gpart add -i %s -t %s -s %s "%s"' 183GPART_ADD_LABEL='gpart add -l %s -t %s "%s"' 184GPART_ADD_LABEL_WITH_SIZE='gpart add -l %s -t %s -s %s "%s"' 185GPART_BOOTCODE='gpart bootcode -b "%s" "%s"' 186GPART_BOOTCODE_PART='gpart bootcode -b "%s" -p "%s" -i %s "%s"' 187GPART_CREATE='gpart create -s %s "%s"' 188GPART_DESTROY_F='gpart destroy -F "%s"' 189GPART_SET_ACTIVE='gpart set -a active -i %s "%s"' 190GRAID_DELETE='graid delete "%s"' 191LN_SF='ln -sf "%s" "%s"' 192MKDIR_P='mkdir -p "%s"' 193MOUNT_TYPE='mount -t %s "%s" "%s"' 194PRINTF_CONF="printf '%s=\"%%s\"\\\n' %s >> \"%s\"" 195PRINTF_FSTAB='printf "$FSTAB_FMT" "%s" "%s" "%s" "%s" "%s" "%s" >> "%s"' 196SHELL_TRUNCATE=':> "%s"' 197SWAP_GMIRROR_LABEL='gmirror label swap %s' 198UMOUNT='umount "%s"' 199ZFS_CREATE_WITH_OPTIONS='zfs create %s "%s"' 200ZFS_SET='zfs set "%s" "%s"' 201ZFS_UNMOUNT='zfs unmount "%s"' 202ZPOOL_CREATE_WITH_OPTIONS='zpool create %s "%s" %s %s' 203ZPOOL_DESTROY='zpool destroy "%s"' 204ZPOOL_EXPORT='zpool export "%s"' 205ZPOOL_IMPORT_WITH_OPTIONS='zpool import %s "%s"' 206ZPOOL_LABELCLEAR_F='zpool labelclear -f "%s"' 207ZPOOL_SET='zpool set %s "%s"' 208 209# 210# Strings that should be moved to an i18n file and loaded with f_include_lang() 211# 212hline_alnum_arrows_punc_tab_enter="Use alnum, arrows, punctuation, TAB or ENTER" 213hline_arrows_space_tab_enter="Use arrows, SPACE, TAB or ENTER" 214hline_arrows_tab_enter="Press arrows, TAB or ENTER" 215msg_an_unknown_error_occurred="An unknown error occurred" 216msg_back="Back" 217msg_cancel="Cancel" 218msg_change_selection="Change Selection" 219msg_configure_options="Configure Options:" 220msg_detailed_disk_info="gpart(8) show %s:\n%s\n\ncamcontrol(8) inquiry %s:\n%s\n\n\ncamcontrol(8) identify %s:\n%s\n" 221msg_disk_info="Disk Info" 222msg_disk_info_help="Get detailed information on disk device(s)" 223msg_encrypt_disks="Encrypt Disks?" 224msg_encrypt_disks_help="Use geli(8) to encrypt all data partitions" 225msg_error="Error" 226msg_force_4k_sectors="Force 4K Sectors?" 227msg_force_4k_sectors_help="Use gnop(8) to configure forced 4K sector alignment" 228msg_freebsd_installer="FreeBSD Installer" 229msg_geli_password="Enter a strong passphrase, used to protect your encryption keys. You will be required to enter this passphrase each time the system is booted" 230msg_geli_setup="Initializing encryption on selected disks,\n this will take several seconds per disk" 231msg_install="Install" 232msg_install_desc="Proceed with Installation" 233msg_install_help="Create ZFS boot pool with displayed options" 234msg_invalid_boot_pool_size="Invalid boot pool size \`%s'" 235msg_invalid_disk_argument="Invalid disk argument \`%s'" 236msg_invalid_index_argument="Invalid index argument \`%s'" 237msg_invalid_swap_size="Invalid swap size \`%s'" 238msg_invalid_virtual_device_type="Invalid Virtual Device type \`%s'" 239msg_last_chance_are_you_sure="Last Chance! Are you sure you want to destroy\nthe current contents of the following disks:\n\n %s" 240msg_last_chance_are_you_sure_color='\\ZrLast Chance!\\ZR Are you \\Z1sure\\Zn you want to \\Zr\\Z1destroy\\Zn\nthe current contents of the following disks:\n\n %s' 241msg_mirror_desc="Mirror - n-Way Mirroring" 242msg_mirror_help="[2+ Disks] Mirroring provides the best performance, but the least storage" 243msg_missing_disk_arguments="missing disk arguments" 244msg_missing_one_or_more_scripted_disks="Missing one or more scripted disks!" 245msg_no="NO" 246msg_no_disks_present_to_configure="No disk(s) present to configure" 247msg_no_disks_selected="No disks selected." 248msg_not_enough_disks_selected="Not enough disks selected. (%u < %u minimum)" 249msg_null_disk_argument="NULL disk argument" 250msg_null_index_argument="NULL index argument" 251msg_null_poolname="NULL poolname" 252msg_ok="OK" 253msg_partition_scheme="Partition Scheme" 254msg_partition_scheme_help="Toggle between GPT and MBR partitioning schemes" 255msg_please_enter_a_name_for_your_zpool="Please enter a name for your zpool:" 256msg_please_enter_amount_of_swap_space="Please enter amount of swap space (SI-Unit suffixes\nrecommended; e.g., \`2g' for 2 Gigabytes):" 257msg_please_select_one_or_more_disks="Please select one or more disks to create a zpool:" 258msg_pool_name="Pool Name" 259msg_pool_name_cannot_be_empty="Pool name cannot be empty." 260msg_pool_name_help="Customize the name of the zpool to be created (Required)" 261msg_pool_type_disks="Pool Type/Disks:" 262msg_pool_type_disks_help="Choose type of ZFS Virtual Device and disks to use (Required)" 263msg_processing_selection="Processing selection..." 264msg_raidz1_desc="RAID-Z1 - Single Redundant RAID" 265msg_raidz1_help="[3+ Disks] Withstand failure of 1 disk. Recommended for: 3, 5 or 9 disks" 266msg_raidz2_desc="RAID-Z2 - Double Redundant RAID" 267msg_raidz2_help="[4+ Disks] Withstand failure of 2 disks. Recommended for: 4, 6 or 10 disks" 268msg_raidz3_desc="RAID-Z3 - Triple Redundant RAID" 269msg_raidz3_help="[5+ Disks] Withstand failure of 3 disks. Recommended for: 5, 7 or 11 disks" 270msg_rescan_devices="Rescan Devices" 271msg_rescan_devices_help="Scan for device changes" 272msg_select="Select" 273msg_select_a_disk_device="Select a disk device" 274msg_select_virtual_device_type="Select Virtual Device type:" 275msg_stripe_desc="Stripe - No Redundancy" 276msg_stripe_help="[1+ Disks] Striping provides maximum storage but no redundancy" 277msg_swap_encrypt="Encrypt Swap?" 278msg_swap_encrypt_help="Encrypt swap partitions with temporary keys, discarded on reboot" 279msg_swap_mirror="Mirror Swap?" 280msg_swap_mirror_help="Mirror swap partitions for redundancy, breaks crash dumps" 281msg_swap_size="Swap Size" 282msg_swap_size_help="Customize how much swap space is allocated to each selected disk" 283msg_these_disks_are_too_small="These disks are too small given the amount of requested\nswap (%s) and/or geli(8) (%s) partitions, which would\ntake 50%% or more of each of the following selected disk\ndevices (not recommended):\n\n %s\n\nRecommend changing partition size(s) and/or selecting a\ndifferent set of devices." 284msg_unable_to_get_disk_capacity="Unable to get disk capacity of \`%s'" 285msg_unsupported_partition_scheme="%s is an unsupported partition scheme" 286msg_user_cancelled="User Cancelled." 287msg_yes="YES" 288msg_zfs_configuration="ZFS Configuration" 289 290############################################################ FUNCTIONS 291 292# dialog_menu_main 293# 294# Display the dialog(1)-based application main menu. 295# 296dialog_menu_main() 297{ 298 local title="$DIALOG_TITLE" 299 local btitle="$DIALOG_BACKTITLE" 300 local prompt="$msg_configure_options" 301 local force4k="$msg_no" 302 local usegeli="$msg_no" 303 local swapgeli="$msg_no" 304 local swapmirror="$msg_no" 305 [ "$ZFSBOOT_GNOP_4K_FORCE_ALIGN" ] && force4k="$msg_yes" 306 [ "$ZFSBOOT_GELI_ENCRYPTION" ] && usegeli="$msg_yes" 307 [ "$ZFSBOOT_SWAP_ENCRYPTION" ] && swapgeli="$msg_yes" 308 [ "$ZFSBOOT_SWAP_MIRROR" ] && swapmirror="$msg_yes" 309 local disks n 310 f_count n $ZFSBOOT_DISKS 311 { [ $n -eq 1 ] && disks=disk; } || disks=disks # grammar 312 local menu_list=" 313 '>>> $msg_install' '$msg_install_desc' 314 '$msg_install_help' 315 'T $msg_pool_type_disks' '$ZFSBOOT_VDEV_TYPE: $n $disks' 316 '$msg_pool_type_disks_help' 317 '- $msg_rescan_devices' '*' 318 '$msg_rescan_devices_help' 319 '- $msg_disk_info' '*' 320 '$msg_disk_info_help' 321 'N $msg_pool_name' '$ZFSBOOT_POOL_NAME' 322 '$msg_pool_name_help' 323 '4 $msg_force_4k_sectors' '$force4k' 324 '$msg_force_4k_sectors_help' 325 'E $msg_encrypt_disks' '$usegeli' 326 '$msg_encrypt_disks_help' 327 'P $msg_partition_scheme' '$ZFSBOOT_PARTITION_SCHEME' 328 '$msg_partition_scheme_help' 329 'S $msg_swap_size' '$ZFSBOOT_SWAP_SIZE' 330 '$msg_swap_size_help' 331 'M $msg_swap_mirror' '$swapmirror' 332 '$msg_swap_mirror_help' 333 'W $msg_swap_encrypt' '$swapgeli' 334 '$msg_swap_encrypt_help' 335 " # END-QUOTE 336 local defaultitem= # Calculated below 337 local hline="$hline_alnum_arrows_punc_tab_enter" 338 339 local height width rows 340 eval f_dialog_menu_with_help_size height width rows \ 341 \"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" $menu_list 342 343 # Obtain default-item from previously stored selection 344 f_dialog_default_fetch defaultitem 345 346 local menu_choice 347 menu_choice=$( eval $DIALOG \ 348 --title \"\$title\" \ 349 --backtitle \"\$btitle\" \ 350 --hline \"\$hline\" \ 351 --item-help \ 352 --ok-label \"\$msg_select\" \ 353 --cancel-label \"\$msg_cancel\" \ 354 --default-item \"\$defaultitem\" \ 355 --menu \"\$prompt\" \ 356 $height $width $rows \ 357 $menu_list \ 358 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 359 ) 360 local retval=$? 361 f_dialog_data_sanitize menu_choice 362 f_dialog_menutag_store "$menu_choice" 363 364 # Only update default-item on success 365 [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" 366 367 return $retval 368} 369 370# dialog_last_chance $disks ... 371# 372# Display a list of the disks that the user is about to destroy. The default 373# action is to return error status unless the user explicitly (non-default) 374# selects "Yes" from the noyes dialog. 375# 376dialog_last_chance() 377{ 378 local title="$DIALOG_TITLE" 379 local btitle="$DIALOG_BACKTITLE" 380 local prompt # Calculated below 381 local hline="$hline_arrows_tab_enter" 382 383 local height=8 width=50 prefix=" " 384 local plen=${#prefix} list= line= 385 local max_width=$(( $width - 3 - $plen )) 386 387 local yes no defaultno extra_args format 388 if [ "$USE_XDIALOG" ]; then 389 yes=ok no=cancel defaultno=default-no 390 extra_args="--wrap --left" 391 format="$msg_last_chance_are_you_sure" 392 else 393 yes=yes no=no defaultno=defaultno 394 extra_args="--colors --cr-wrap" 395 format="$msg_last_chance_are_you_sure_color" 396 fi 397 398 local disk line_width 399 for disk in $*; do 400 if [ "$line" ]; then 401 line_width=${#line} 402 else 403 line_width=$plen 404 fi 405 line_width=$(( $line_width + 1 + ${#disk} )) 406 # Add newline before disk if it would exceed max_width 407 if [ $line_width -gt $max_width ]; then 408 list="$list$line\n" 409 line="$prefix" 410 height=$(( $height + 1 )) 411 fi 412 # Add the disk to the list 413 line="$line $disk" 414 done 415 # Append the left-overs 416 if [ "${line#$prefix}" ]; then 417 list="$list$line" 418 height=$(( $height + 1 )) 419 fi 420 421 # Add height for Xdialog(1) 422 [ "$USE_XDIALOG" ] && height=$(( $height + $height / 5 + 3 )) 423 424 prompt=$( printf "$format" "$list" ) 425 f_dprintf "%s: Last Chance!" "$0" 426 $DIALOG \ 427 --title "$title" \ 428 --backtitle "$btitle" \ 429 --hline "$hline" \ 430 --$defaultno \ 431 --$yes-label "$msg_yes" \ 432 --$no-label "$msg_no" \ 433 $extra_args \ 434 --yesno "$prompt" $height $width 435} 436 437# dialog_menu_layout 438# 439# Configure Virtual Device type and disks to use for the ZFS boot pool. User 440# must select enough disks to satisfy the chosen vdev type. 441# 442dialog_menu_layout() 443{ 444 local funcname=dialog_menu_layout 445 local title="$DIALOG_TITLE" 446 local btitle="$DIALOG_BACKTITLE" 447 local vdev_prompt="$msg_select_virtual_device_type" 448 local disk_prompt="$msg_please_select_one_or_more_disks" 449 local vdev_menu_list=" 450 'stripe' '$msg_stripe_desc' '$msg_stripe_help' 451 'mirror' '$msg_mirror_desc' '$msg_mirror_help' 452 'raidz1' '$msg_raidz1_desc' '$msg_raidz1_help' 453 'raidz2' '$msg_raidz2_desc' '$msg_raidz2_help' 454 'raidz3' '$msg_raidz3_desc' '$msg_raidz3_help' 455 " # END-QUOTE 456 local disk_check_list= # Calculated below 457 local vdev_hline="$hline_arrows_tab_enter" 458 local disk_hline="$hline_arrows_space_tab_enter" 459 460 # Warn the user if vdev type is not valid 461 case "$ZFSBOOT_VDEV_TYPE" in 462 stripe|mirror|raidz1|raidz2|raidz3) : known good ;; 463 *) 464 f_dprintf "%s: Invalid virtual device type \`%s'" \ 465 $funcname "$ZFSBOOT_VDEV_TYPE" 466 f_show_err "$msg_invalid_virtual_device_type" \ 467 "$ZFSBOOT_VDEV_TYPE" 468 f_interactive || return $FAILURE 469 esac 470 471 # Calculate size of vdev menu once only 472 local vheight vwidth vrows 473 eval f_dialog_menu_with_help_size vheight vwidth vrows \ 474 \"\$title\" \"\$btitle\" \"\$vdev_prompt\" \"\$vdev_hline\" \ 475 $vdev_menu_list 476 477 # Get a list of probed disk devices 478 local disks= 479 debug= f_device_find "" $DEVICE_TYPE_DISK disks 480 481 # Prune out mounted md(4) devices that may be part of the boot process 482 local disk name new_list= 483 for disk in $disks; do 484 debug= $disk get name name 485 case "$name" in 486 md[0-9]*) f_mounted -b "/dev/$name" && continue ;; 487 esac 488 new_list="$new_list $disk" 489 done 490 disks="${new_list# }" 491 492 # Debugging 493 if [ "$debug" ]; then 494 local disk_names= 495 for disk in $disks; do 496 debug= $disk get name name 497 disk_names="$disk_names $name" 498 done 499 f_dprintf "$funcname: disks=[%s]" "${disk_names# }" 500 fi 501 502 if [ ! "$disks" ]; then 503 f_dprintf "No disk(s) present to configure" 504 f_show_err "$msg_no_disks_present_to_configure" 505 return $FAILURE 506 fi 507 508 # Lets sort the disks array to be more user friendly 509 f_device_sort_by name disks disks 510 511 # 512 # Operate in a loop so we can (if interactive) repeat if not enough 513 # disks are selected to satisfy the chosen vdev type or user wants to 514 # back-up to the previous menu. 515 # 516 local vardisk ndisks onoff selections vdev_choice breakout device 517 local valid_disks all_valid want_disks desc height width rows 518 while :; do 519 # 520 # Confirm the vdev type that was selected 521 # 522 if f_interactive && [ "$ZFSBOOT_CONFIRM_LAYOUT" ]; then 523 vdev_choice=$( eval $DIALOG \ 524 --title \"\$title\" \ 525 --backtitle \"\$btitle\" \ 526 --hline \"\$vdev_hline\" \ 527 --ok-label \"\$msg_ok\" \ 528 --cancel-label \"\$msg_cancel\" \ 529 --item-help \ 530 --default-item \"\$ZFSBOOT_VDEV_TYPE\" \ 531 --menu \"\$vdev_prompt\" \ 532 $vheight $vwidth $vrows \ 533 $vdev_menu_list \ 534 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 535 ) || return $? 536 # Exit if user pressed ESC or chose Cancel/No 537 f_dialog_data_sanitize vdev_choice 538 539 ZFSBOOT_VDEV_TYPE="$vdev_choice" 540 f_dprintf "$funcname: ZFSBOOT_VDEV_TYPE=[%s]" \ 541 "$ZFSBOOT_VDEV_TYPE" 542 fi 543 544 # Determine the number of disks needed for this vdev type 545 want_disks=0 546 case "$ZFSBOOT_VDEV_TYPE" in 547 stripe) want_disks=1 ;; 548 mirror) want_disks=2 ;; 549 raidz1) want_disks=3 ;; 550 raidz2) want_disks=4 ;; 551 raidz3) want_disks=5 ;; 552 esac 553 554 # 555 # Warn the user if any scripted disks are invalid 556 # 557 valid_disks= all_valid=${ZFSBOOT_DISKS:+1} # optimism 558 for disk in $ZFSBOOT_DISKS; do 559 if debug= f_device_find -1 \ 560 $disk $DEVICE_TYPE_DISK device 561 then 562 valid_disks="$valid_disks $disk" 563 continue 564 fi 565 f_dprintf "$funcname: \`%s' is not a real disk" "$disk" 566 all_valid= 567 done 568 if [ ! "$all_valid" ]; then 569 if [ "$ZFSBOOT_DISKS" ]; then 570 f_show_err \ 571 "$msg_missing_one_or_more_scripted_disks" 572 else 573 f_dprintf "No disks selected." 574 f_interactive || 575 f_show_err "$msg_no_disks_selected" 576 fi 577 f_interactive || return $FAILURE 578 fi 579 ZFSBOOT_DISKS="${valid_disks# }" 580 581 # 582 # Short-circuit if we're running non-interactively 583 # 584 if ! f_interactive || [ ! "$ZFSBOOT_CONFIRM_LAYOUT" ]; then 585 f_count ndisks $ZFSBOOT_DISKS 586 [ $ndisks -ge $want_disks ] && break # to success 587 588 # Not enough disks selected 589 f_dprintf "$funcname: %s: %s (%u < %u minimum)" \ 590 "$ZFSBOOT_VDEV_TYPE" \ 591 "Not enough disks selected." \ 592 $ndisks $want_disks 593 f_interactive || return $FAILURE 594 msg_yes="$msg_change_selection" msg_no="$msg_cancel" \ 595 f_yesno "%s: $msg_not_enough_disks_selected" \ 596 "$ZFSBOOT_VDEV_TYPE" $ndisks $want_disks || 597 return $FAILURE 598 fi 599 600 # 601 # Confirm the disks that were selected 602 # Loop until the user cancels or selects enough disks 603 # 604 breakout= 605 while :; do 606 # Loop over list of available disks, resetting state 607 for disk in $disks; do 608 f_isset _${disk}_status && _${disk}_status= 609 done 610 611 # Loop over list of selected disks and create temporary 612 # locals to map statuses onto up-to-date list of disks 613 for disk in $ZFSBOOT_DISKS; do 614 debug= f_device_find -1 \ 615 $disk $DEVICE_TYPE_DISK disk 616 f_isset _${disk}_status || 617 local _${disk}_status 618 _${disk}_status=on 619 done 620 621 # Create the checklist menu of discovered disk devices 622 disk_check_list= 623 for disk in $disks; do 624 desc= 625 $disk get name name 626 $disk get desc desc 627 f_shell_escape "$desc" desc 628 f_getvar _${disk}_status:-off onoff 629 disk_check_list="$disk_check_list 630 $name '$desc' $onoff" 631 done 632 633 eval f_dialog_checklist_size height width rows \ 634 \"\$title\" \"\$btitle\" \"\$prompt\" \ 635 \"\$hline\" $disk_check_list 636 637 selections=$( eval $DIALOG \ 638 --title \"\$DIALOG_TITLE\" \ 639 --backtitle \"\$DIALOG_BACKTITLE\" \ 640 --separate-output \ 641 --hline \"\$hline\" \ 642 --ok-label \"\$msg_ok\" \ 643 --cancel-label \"\$msg_back\" \ 644 --checklist \"\$prompt\" \ 645 $height $width $rows \ 646 $disk_check_list \ 647 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 648 ) || break 649 # Loop if user pressed ESC or chose Cancel/No 650 f_dialog_data_sanitize selections 651 652 ZFSBOOT_DISKS="$selections" 653 f_dprintf "$funcname: ZFSBOOT_DISKS=[%s]" \ 654 "$ZFSBOOT_DISKS" 655 656 f_count ndisks $ZFSBOOT_DISKS 657 [ $ndisks -ge $want_disks ] && 658 breakout=break && break 659 660 # Not enough disks selected 661 f_dprintf "$funcname: %s: %s (%u < %u minimum)" \ 662 "$ZFSBOOT_VDEV_TYPE" \ 663 "Not enough disks selected." \ 664 $ndisks $want_disks 665 msg_yes="$msg_change_selection" msg_no="$msg_cancel" \ 666 f_yesno "%s: $msg_not_enough_disks_selected" \ 667 "$ZFSBOOT_VDEV_TYPE" $ndisks $want_disks || 668 break 669 done 670 [ "$breakout" = "break" ] && break 671 [ "$ZFSBOOT_CONFIRM_LAYOUT" ] || return $FAILURE 672 done 673 674 return $DIALOG_OK 675} 676 677# zfs_create_diskpart $disk $index 678# 679# For each block device to be used in the zpool, rather than just create the 680# zpool with the raw block devices (e.g., da0, da1, etc.) we create partitions 681# so we can have some real swap. This also provides wiggle room incase your 682# replacement drivers do not have the exact same sector counts. 683# 684# NOTE: $swapsize and $bootsize should be defined by the calling function. 685# NOTE: Sets $bootpart and $targetpart for the calling function. 686# 687zfs_create_diskpart() 688{ 689 local funcname=zfs_create_diskpart 690 local disk="$1" index="$2" 691 692 # Check arguments 693 if [ ! "$disk" ]; then 694 f_dprintf "$funcname: NULL disk argument" 695 msg_error="$msg_error: $funcname" \ 696 f_show_err "$msg_null_disk_argument" 697 return $FAILURE 698 fi 699 if [ "${disk#*[$IFS]}" != "$disk" ]; then 700 f_dprintf "$funcname: Invalid disk argument \`%s'" "$disk" 701 msg_error="$msg_error: $funcname" \ 702 f_show_err "$msg_invalid_disk_argument" "$disk" 703 return $FAILURE 704 fi 705 if [ ! "$index" ]; then 706 f_dprintf "$funcname: NULL index argument" 707 msg_error="$msg_error: $funcname" \ 708 f_show_err "$msg_null_index_argument" 709 return $FAILURE 710 fi 711 if ! f_isinteger "$index"; then 712 f_dprintf "$funcname: Invalid index argument \`%s'" "$index" 713 msg_error="$msg_error: $funcname" \ 714 f_show_err "$msg_invalid_index_argument" "$index" 715 return $FAILURE 716 fi 717 f_dprintf "$funcname: disk=[%s] index=[%s]" "$disk" "$index" 718 719 # Check for unknown partition scheme before proceeding further 720 case "$ZFSBOOT_PARTITION_SCHEME" in 721 ""|MBR|GPT) : known good ;; 722 *) 723 f_dprintf "$funcname: %s is an unsupported partition scheme" \ 724 "$ZFSBOOT_PARTITION_SCHEME" 725 msg_error="$msg_error: $funcname" f_show_err \ 726 "$msg_unsupported_partition_scheme" \ 727 "$ZFSBOOT_PARTITION_SCHEME" 728 return $FAILURE 729 esac 730 731 # 732 # Destroy whatever partition layout is currently on disk. 733 # NOTE: `-F' required to destroy if partitions still exist. 734 # NOTE: Failure is ok here, blank disk will have nothing to destroy. 735 # 736 f_dprintf "$funcname: Destroying all data/layouts on \`%s'..." "$disk" 737 f_eval_catch -d $funcname gpart "$GPART_DESTROY_F" $disk 738 f_eval_catch -d $funcname graid "$GRAID_DELETE" $disk 739 f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" /dev/$disk 740 741 # Make doubly-sure backup GPT is destroyed 742 f_eval_catch -d $funcname gpart "$GPART_CREATE" gpt $disk 743 f_eval_catch -d $funcname gpart "$GPART_DESTROY_F" $disk 744 745 # 746 # Enable boot pool if encryption is desired 747 # 748 [ "$ZFSBOOT_GELI_ENCRYPTION" ] && ZFSBOOT_BOOT_POOL=1 749 750 # 751 # Lay down the desired type of partition scheme 752 # 753 local setsize mbrindex 754 case "$ZFSBOOT_PARTITION_SCHEME" in 755 ""|GPT) f_dprintf "$funcname: Creating GPT layout..." 756 # 757 # 1. Create GPT layout using labels 758 # 759 f_eval_catch $funcname gpart "$GPART_CREATE" gpt $disk || 760 return $FAILURE 761 762 # 763 # 2. Add small freebsd-boot partition labeled `boot#' 764 # 765 f_eval_catch $funcname gpart "$GPART_ADD_LABEL_WITH_SIZE" \ 766 gptboot$index freebsd-boot 512k $disk || 767 return $FAILURE 768 f_eval_catch $funcname gpart "$GPART_BOOTCODE_PART" \ 769 /boot/pmbr /boot/gptzfsboot 1 $disk || 770 return $FAILURE 771 772 # NB: zpool will use the `zfs#' GPT labels 773 bootpart=p2 swappart=p2 targetpart=p2 774 [ ${swapsize:-0} -gt 0 ] && targetpart=p3 775 776 # 777 # Prepare boot pool if enabled (e.g., for geli(8)) 778 # 779 if [ "$ZFSBOOT_BOOT_POOL" ]; then 780 bootpart=p2 swappart=p3 targetpart=p3 781 [ ${swapsize:-0} -gt 0 ] && targetpart=p4 782 f_eval_catch $funcname gpart \ 783 "$GPART_ADD_LABEL_WITH_SIZE" boot$index \ 784 freebsd-zfs ${bootsize}b $disk || 785 return $FAILURE 786 # Pedantically nuke any old labels 787 f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ 788 /dev/$disk$bootpart 789 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 790 # Pedantically detach targetpart for later 791 f_eval_catch -d $funcname geli \ 792 "$GELI_DETACH_F" \ 793 /dev/$disk$targetpart 794 fi 795 fi 796 797 # 798 # 3. Add freebsd-swap partition labeled `swap#' 799 # 800 if [ ${swapsize:-0} -gt 0 ]; then 801 f_eval_catch $funcname gpart \ 802 "$GPART_ADD_LABEL_WITH_SIZE" swap$index \ 803 freebsd-swap ${swapsize}b $disk || 804 return $FAILURE 805 # Pedantically nuke any old labels on the swap 806 f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ 807 /dev/$disk$swappart 808 fi 809 810 # 811 # 4. Add freebsd-zfs partition labeled `zfs#' for zroot 812 # 813 f_eval_catch $funcname gpart "$GPART_ADD_LABEL" \ 814 zfs$index freebsd-zfs $disk || return $FAILURE 815 f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ 816 /dev/$disk$targetpart 817 ;; 818 819 MBR) f_dprintf "$funcname: Creating MBR layout..." 820 # 821 # 1. Create MBR layout (no labels) 822 # 823 f_eval_catch $funcname gpart "$GPART_CREATE" mbr $disk || 824 return $FAILURE 825 f_eval_catch $funcname gpart "$GPART_BOOTCODE" /boot/mbr \ 826 $disk || return $FAILURE 827 828 # 829 # 2. Add freebsd slice with all available space 830 # 831 f_eval_catch $funcname gpart "$GPART_ADD" freebsd $disk || 832 return $FAILURE 833 f_eval_catch $funcname gpart "$GPART_SET_ACTIVE" 1 $disk || 834 return $FAILURE 835 # Pedantically nuke any old labels 836 f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ 837 /dev/${disk}s1 838 # Pedantically nuke any old scheme 839 f_eval_catch -d $funcname gpart "$GPART_DESTROY_F" ${disk}s1 840 841 # 842 # 3. Write BSD scheme to the freebsd slice 843 # 844 f_eval_catch $funcname gpart "$GPART_CREATE" BSD ${disk}s1 || 845 return $FAILURE 846 847 # NB: zpool will use s1a (no labels) 848 bootpart=s1a swappart=s1b targetpart=s1d mbrindex=4 849 850 # 851 # Always prepare a boot pool on MBR 852 # 853 ZFSBOOT_BOOT_POOL=1 854 f_eval_catch $funcname gpart \ 855 "$GPART_ADD_INDEX_WITH_SIZE" \ 856 1 freebsd-zfs ${bootsize}b ${disk}s1 || 857 return $FAILURE 858 # Pedantically nuke any old labels 859 f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ 860 /dev/$disk$bootpart 861 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 862 # Pedantically detach targetpart for later 863 f_eval_catch -d $funcname geli \ 864 "$GELI_DETACH_F" \ 865 /dev/$disk$targetpart 866 fi 867 868 # 869 # 4. Add freebsd-swap partition 870 # 871 if [ ${swapsize:-0} -gt 0 ]; then 872 f_eval_catch $funcname gpart \ 873 "$GPART_ADD_INDEX_WITH_SIZE" 2 \ 874 freebsd-swap ${swapsize}b ${disk}s1 || 875 return $FAILURE 876 # Pedantically nuke any old labels on the swap 877 f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ 878 /dev/${disk}s1b 879 fi 880 881 # 882 # 5. Add freebsd-zfs partition for zroot 883 # 884 f_eval_catch $funcname gpart "$GPART_ADD_INDEX" \ 885 $mbrindex freebsd-zfs ${disk}s1 || return $FAILURE 886 f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ 887 /dev/$disk$targetpart # Pedantic 888 f_eval_catch $funcname dd "$DD_WITH_OPTIONS" \ 889 /boot/zfsboot /dev/${disk}s1 count=1 || 890 return $FAILURE 891 ;; 892 893 esac # $ZFSBOOT_PARTITION_SCHEME 894 895 # Update fstab(5) 896 if [ "$isswapmirror" ]; then 897 # This is not the first disk in the mirror, do nothing 898 elif [ "$ZFSBOOT_SWAP_ENCRYPTION" -a "$ZFSBOOT_SWAP_MIRROR" ]; then 899 f_eval_catch $funcname printf "$PRINTF_FSTAB" \ 900 /dev/mirror/swap.eli none swap sw 0 0 \ 901 $BSDINSTALL_TMPETC/fstab || 902 return $FAILURE 903 isswapmirror=1 904 elif [ "$ZFSBOOT_SWAP_MIRROR" ]; then 905 f_eval_catch $funcname printf "$PRINTF_FSTAB" \ 906 /dev/mirror/swap none swap sw 0 0 \ 907 $BSDINSTALL_TMPETC/fstab || 908 return $FAILURE 909 isswapmirror=1 910 elif [ "$ZFSBOOT_SWAP_ENCRYPTION" ]; then 911 f_eval_catch $funcname printf "$PRINTF_FSTAB" \ 912 /dev/$disk${swappart}.eli none swap sw 0 0 \ 913 $BSDINSTALL_TMPETC/fstab || 914 return $FAILURE 915 else 916 f_eval_catch $funcname printf "$PRINTF_FSTAB" \ 917 /dev/$disk$swappart none swap sw 0 0 \ 918 $BSDINSTALL_TMPETC/fstab || 919 return $FAILURE 920 fi 921 922 return $SUCCESS 923} 924 925# zfs_create_boot $poolname $vdev_type $disks ... 926# 927# Creates boot pool and dataset layout. Returns error if something goes wrong. 928# Errors are printed to stderr for collection and display. 929# 930zfs_create_boot() 931{ 932 local funcname=zfs_create_boot 933 local zroot_name="$1" 934 local zroot_vdevtype="$2" 935 local zroot_vdevs= # Calculated below 936 local swap_devs= # Calculated below 937 local boot_vdevs= # Used for geli(8) and/or MBR layouts 938 shift 2 # poolname vdev_type 939 local disks="$*" disk 940 local isswapmirror 941 local bootpart targetpart swappart # Set by zfs_create_diskpart() below 942 943 # 944 # Pedantic checks; should never be seen 945 # 946 if [ ! "$zroot_name" ]; then 947 f_dprintf "$funcname: NULL poolname" 948 msg_error="$msg_error: $funcname" \ 949 f_show_err "$msg_null_poolname" 950 return $FAILURE 951 fi 952 if [ $# -lt 1 ]; then 953 f_dprintf "$funcname: missing disk arguments" 954 msg_error="$msg_error: $funcname" \ 955 f_show_err "$msg_missing_disk_arguments" 956 return $FAILURE 957 fi 958 f_dprintf "$funcname: poolname=[%s] vdev_type=[%s]" \ 959 "$zroot_name" "$zroot_vdevtype" 960 961 # 962 # Initialize fstab(5) 963 # 964 f_dprintf "$funcname: Initializing temporary fstab(5) file..." 965 f_eval_catch $funcname sh "$SHELL_TRUNCATE" $BSDINSTALL_TMPETC/fstab || 966 return $FAILURE 967 f_eval_catch $funcname printf "$PRINTF_FSTAB" \ 968 "# Device" Mountpoint FStype Options Dump "Pass#" \ 969 $BSDINSTALL_TMPETC/fstab || return $FAILURE 970 971 # 972 # Expand SI units in desired sizes 973 # 974 f_dprintf "$funcname: Expanding supplied size values..." 975 local swapsize bootsize 976 if ! f_expand_number "$ZFSBOOT_SWAP_SIZE" swapsize; then 977 f_dprintf "$funcname: Invalid swap size \`%s'" \ 978 "$ZFSBOOT_SWAP_SIZE" 979 f_show_err "$msg_invalid_swap_size" "$ZFSBOOT_SWAP_SIZE" 980 return $FAILURE 981 fi 982 if ! f_expand_number "$ZFSBOOT_BOOT_POOL_SIZE" bootsize; then 983 f_dprintf "$funcname: Invalid boot pool size \`%s'" \ 984 "$ZFSBOOT_BOOT_POOL_SIZE" 985 f_show_err "$msg_invalid_boot_pool_size" \ 986 "$ZFSBOOT_BOOT_POOL_SIZE" 987 return $FAILURE 988 fi 989 f_dprintf "$funcname: ZFSBOOT_SWAP_SIZE=[%s] swapsize=[%s]" \ 990 "$ZFSBOOT_SWAP_SIZE" "$swapsize" 991 f_dprintf "$funcname: ZFSBOOT_BOOT_POOL_SIZE=[%s] bootsize=[%s]" \ 992 "$ZFSBOOT_BOOT_POOL_SIZE" "$bootsize" 993 994 # 995 # Destroy the pool in-case this is our second time 'round (case of 996 # failure and installer presented ``Retry'' option to come back). 997 # 998 # NB: If we don't destroy the pool, later gpart(8) destroy commands 999 # that try to clear existing partitions (see zfs_create_diskpart()) 1000 # will fail with a `Device Busy' error, leading to `GEOM exists'. 1001 # 1002 f_eval_catch -d $funcname zpool "$ZPOOL_DESTROY" "$zroot_name" 1003 1004 # 1005 # Prepare the disks and build pool device list(s) 1006 # 1007 f_dprintf "$funcname: Preparing disk partitions for ZFS pool..." 1008 [ "$ZFSBOOT_GNOP_4K_FORCE_ALIGN" ] && 1009 f_dprintf "$funcname: With 4k alignment using gnop(8)..." 1010 local n=0 1011 for disk in $disks; do 1012 zfs_create_diskpart $disk $n || return $FAILURE 1013 # Now $bootpart, $targetpart, and $swappart are set (suffix 1014 # for $disk) 1015 1016 # Forced 4k alignment support using Geom NOP (see gnop(8)) 1017 if [ "$ZFSBOOT_GNOP_4K_FORCE_ALIGN" ]; then 1018 if [ "$ZFSBOOT_BOOT_POOL" ]; then 1019 boot_vdevs="$boot_vdevs $disk$bootpart.nop" 1020 f_eval_catch $funcname gnop "$GNOP_CREATE" \ 1021 $disk$bootpart || return $FAILURE 1022 fi 1023 # Don't gnop encrypted partition 1024 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 1025 zroot_vdevs="$zroot_vdevs $disk$targetpart.eli" 1026 else 1027 zroot_vdevs="$zroot_vdevs $disk$targetpart.nop" 1028 f_eval_catch $funcname gnop "$GNOP_CREATE" \ 1029 $disk$targetpart || 1030 return $FAILURE 1031 fi 1032 else 1033 if [ "$ZFSBOOT_BOOT_POOL" ]; then 1034 boot_vdevs="$boot_vdevs $disk$bootpart" 1035 fi 1036 zroot_vdevs="$zroot_vdevs $disk$targetpart" 1037 fi 1038 1039 n=$(( $n + 1 )) 1040 done # disks 1041 1042 # 1043 # If we need/want a boot pool, create it 1044 # 1045 if [ "$ZFSBOOT_BOOT_POOL" ]; then 1046 local bootpool_vdevtype= # Calculated below 1047 local bootpool_options= # Calculated below 1048 local bootpool_name="$ZFSBOOT_BOOT_POOL_NAME" 1049 local bootpool="$BSDINSTALL_CHROOT/$bootpool_name" 1050 local zroot_key="${ZFSBOOT_GELI_KEY_FILE#/}" 1051 1052 f_dprintf "$funcname: Setting up boot pool..." 1053 [ "$ZFSBOOT_GELI_ENCRYPTION" ] && 1054 f_dprintf "$funcname: For encrypted root disk..." 1055 1056 # Create parent directory for boot pool 1057 f_eval_catch -d $funcname umount "$UMOUNT" /mnt 1058 f_eval_catch $funcname mount "$MOUNT_TYPE" tmpfs none \ 1059 $BSDINSTALL_CHROOT || return $FAILURE 1060 1061 # Create mirror across the boot partition on all disks 1062 local nvdevs 1063 f_count nvdevs $boot_vdevs 1064 [ $nvdevs -gt 1 ] && bootpool_vdevtype=mirror 1065 1066 bootpool_options="-o altroot=$BSDINSTALL_CHROOT" 1067 bootpool_options="$bootpool_options -m \"/$bootpool_name\" -f" 1068 f_eval_catch $funcname zpool "$ZPOOL_CREATE_WITH_OPTIONS" \ 1069 "$bootpool_options" "$bootpool_name" \ 1070 "$bootpool_vdevtype" "$boot_vdevs" || 1071 return $FAILURE 1072 1073 f_eval_catch $funcname mkdir "$MKDIR_P" "$bootpool/boot" || 1074 return $FAILURE 1075 1076 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 1077 # Generate an encryption key using random(4) 1078 f_eval_catch $funcname dd "$DD_WITH_OPTIONS" \ 1079 /dev/random "$bootpool/$zroot_key" \ 1080 "bs=4096 count=1" || return $FAILURE 1081 else 1082 # Clean up 1083 f_eval_catch $funcname zfs "$ZFS_UNMOUNT" \ 1084 "$bootpool_name" || return $FAILURE 1085 f_eval_catch -d $funcname umount "$UMOUNT" /mnt # tmpfs 1086 fi 1087 1088 fi 1089 1090 # 1091 # Create the geli(8) GEOMS 1092 # 1093 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 1094 # Prompt user for password (twice) 1095 if ! msg_enter_new_password="$msg_geli_password" \ 1096 f_dialog_input_password 1097 then 1098 f_dprintf "$funcname: User cancelled" 1099 f_show_err "$msg_user_cancelled" 1100 return $FAILURE 1101 fi 1102 1103 # Initialize geli(8) on each of the target partitions 1104 for disk in $disks; do 1105 f_dialog_info "$msg_geli_setup" \ 1106 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 1107 if ! echo "$pw_password" | f_eval_catch \ 1108 $funcname geli "$GELI_PASSWORD_INIT" \ 1109 "$bootpool/boot/$disk$targetpart.eli" \ 1110 AES-XTS "$bootpool/$zroot_key" \ 1111 $disk$targetpart 1112 then 1113 f_interactive || f_die 1114 unset pw_password # Sensitive info 1115 return $FAILURE 1116 fi 1117 if ! echo "$pw_password" | f_eval_catch \ 1118 $funcname geli "$GELI_ATTACH" \ 1119 "$bootpool/$zroot_key" $disk$targetpart 1120 then 1121 f_interactive || f_die 1122 unset pw_password # Sensitive info 1123 return $FAILURE 1124 fi 1125 done 1126 unset pw_password # Sensitive info 1127 1128 # Clean up 1129 f_eval_catch $funcname zfs "$ZFS_UNMOUNT" "$bootpool_name" || 1130 return $FAILURE 1131 f_eval_catch -d $funcname umount "$UMOUNT" /mnt # tmpfs 1132 fi 1133 # 1134 # Create the gmirror(8) GEOMS for swap 1135 # 1136 if [ "$ZFSBOOT_SWAP_MIRROR" ]; then 1137 for disk in $disks; do 1138 swap_devs="$swap_devs $disk$swappart" 1139 done 1140 f_eval_catch $funcname gmirror "$SWAP_GMIRROR_LABEL" \ 1141 "$swap_devs" || return $FAILURE 1142 fi 1143 1144 # 1145 # Create the ZFS root pool with desired type and disk devices 1146 # 1147 f_dprintf "$funcname: Creating root pool..." 1148 f_eval_catch $funcname zpool "$ZPOOL_CREATE_WITH_OPTIONS" \ 1149 "-o altroot=$BSDINSTALL_CHROOT -m none -f" \ 1150 "$zroot_name" "$zroot_vdevtype" "$zroot_vdevs" || 1151 return $FAILURE 1152 1153 # Customize the zroot a bit... 1154 local option 1155 f_dprintf "$funcname: Setting miscellaneous options on root pool..." 1156 for option in atime=off; do 1157 f_eval_catch $funcname zfs "$ZFS_SET" $option "$zroot_name" || 1158 return $FAILURE 1159 done 1160 1161 # 1162 # Create ZFS dataset layout within the new root pool 1163 # 1164 f_dprintf "$funcname: Creating ZFS datasets..." 1165 echo "$ZFSBOOT_DATASETS" | while read dataset options; do 1166 # Skip blank lines and comments 1167 case "$dataset" in "#"*|"") continue; esac 1168 # Remove potential inline comments in options 1169 options="${options%%#*}" 1170 # Replace tabs with spaces 1171 f_replaceall "$options" " " " " options 1172 # Reduce contiguous runs of space to one single space 1173 oldoptions= 1174 while [ "$oldoptions" != "$options" ]; do 1175 oldoptions="$options" 1176 f_replaceall "$options" " " " " options 1177 done 1178 # Replace both commas and spaces with ` -o ' 1179 f_replaceall "$options" "[ ,]" " -o " options 1180 # Create the dataset with desired options 1181 f_eval_catch $funcname zfs "$ZFS_CREATE_WITH_OPTIONS" \ 1182 "${options:+-o $options}" "$zroot_name$dataset" || 1183 return $FAILURE 1184 done 1185 1186 # Touch up permissions on the tmp directories 1187 f_dprintf "$funcname: Modifying directory permissions..." 1188 local dir 1189 for dir in /tmp /var/tmp; do 1190 f_eval_catch $funcname chmod "$CHMOD_MODE" 1777 \ 1191 $BSDINSTALL_CHROOTDIR$dir || return $FAILURE 1192 done 1193 1194 # Create symlink(s) 1195 if [ "$ZFSBOOT_BOOT_POOL" ]; then 1196 f_dprintf "$funcname: Creating /boot symlink for boot pool..." 1197 f_eval_catch $funcname ln "$LN_SF" "$bootpool_name/boot" \ 1198 $BSDINSTALL_CHROOT/boot || return $FAILURE 1199 fi 1200 1201 # Set bootfs property 1202 local zroot_bootfs="$ZFSBOOT_BEROOT_NAME/$ZFSBOOT_BOOTFS_NAME" 1203 f_dprintf "$funcname: Setting bootfs property..." 1204 f_eval_catch $funcname zpool "$ZPOOL_SET" \ 1205 "bootfs=\"$zroot_name/$zroot_bootfs\"" "$zroot_name" || 1206 return $FAILURE 1207 1208 # Export the pool(s) 1209 f_dprintf "$funcname: Temporarily exporting ZFS pool(s)..." 1210 f_eval_catch $funcname zpool "$ZPOOL_EXPORT" "$zroot_name" || 1211 return $FAILURE 1212 if [ "$ZFSBOOT_BOOT_POOL" ]; then 1213 f_eval_catch $funcname zpool "$ZPOOL_EXPORT" \ 1214 "$bootpool_name" || return $FAILURE 1215 fi 1216 1217 # Destroy the gnop devices (if enabled) 1218 for disk in ${ZFSBOOT_GNOP_4K_FORCE_ALIGN:+$disks}; do 1219 if [ "$ZFSBOOT_BOOT_POOL" ]; then 1220 f_eval_catch -d $funcname gnop "$GNOP_DESTROY" \ 1221 $disk$bootpart.nop 1222 fi 1223 if [ ! "$ZFSBOOT_GELI_ENCRYPTION" ]; then 1224 f_eval_catch -d $funcname gnop "$GNOP_DESTROY" \ 1225 $disk$targetpart.nop 1226 fi 1227 done 1228 1229 # MBR boot loader touch-up 1230 if [ "$ZFSBOOT_PARTITION_SCHEME" = "MBR" ]; then 1231 f_dprintf "$funcname: Updating MBR boot loader on disks..." 1232 # Stick the ZFS boot loader in the "convienient hole" after 1233 # the ZFS internal metadata 1234 for disk in $disks; do 1235 f_eval_catch $funcname dd "$DD_WITH_OPTIONS" \ 1236 /boot/zfsboot /dev/$disk$bootpart \ 1237 "skip=1 seek=1024" || return $FAILURE 1238 done 1239 fi 1240 1241 # Re-import the ZFS pool(s) 1242 f_dprintf "$funcname: Re-importing ZFS pool(s)..." 1243 f_eval_catch $funcname zpool "$ZPOOL_IMPORT_WITH_OPTIONS" \ 1244 "-o altroot=\"$BSDINSTALL_CHROOT\"" "$zroot_name" || 1245 return $FAILURE 1246 if [ "$ZFSBOOT_BOOT_POOL" ]; then 1247 f_eval_catch $funcname zpool "$ZPOOL_IMPORT_WITH_OPTIONS" \ 1248 "-o altroot=\"$BSDINSTALL_CHROOT\"" \ 1249 "$bootpool_name" || return $FAILURE 1250 fi 1251 1252 # While this is apparently not needed, it seems to help MBR 1253 f_dprintf "$funcname: Configuring zpool.cache for zroot..." 1254 f_eval_catch $funcname mkdir "$MKDIR_P" $BSDINSTALL_CHROOT/boot/zfs || 1255 return $FAILURE 1256 f_eval_catch $funcname zpool "$ZPOOL_SET" \ 1257 "cachefile=\"$BSDINSTALL_CHROOT/boot/zfs/zpool.cache\"" \ 1258 "$zroot_name" || return $FAILURE 1259 1260 # Last, but not least... required lines for rc.conf(5)/loader.conf(5) 1261 # NOTE: We later concatenate these into their destination 1262 f_dprintf "%s: Configuring rc.conf(5)/loader.conf(5) additions..." \ 1263 "$funcname" 1264 f_eval_catch $funcname echo "$ECHO_APPEND" 'zfs_enable=\"YES\"' \ 1265 $BSDINSTALL_TMPETC/rc.conf.zfs || return $FAILURE 1266 f_eval_catch $funcname echo "$ECHO_APPEND" 'zfs_load=\"YES\"' \ 1267 $BSDINSTALL_TMPBOOT/loader.conf.zfs || return $FAILURE 1268 f_eval_catch $funcname echo "$ECHO_APPEND" \ 1269 'kern.geom.label.disk_ident.enable=\"0\"' \ 1270 $BSDINSTALL_TMPBOOT/loader.conf.zfs || return $FAILURE 1271 f_eval_catch $funcname echo "$ECHO_APPEND" \ 1272 'kern.geom.label.gptid.enable=\"0\"' \ 1273 $BSDINSTALL_TMPBOOT/loader.conf.zfs || return $FAILURE 1274 1275 if [ "$ZFSBOOT_SWAP_MIRROR" ]; then 1276 f_eval_catch $funcname echo "$ECHO_APPEND" 'geom_mirror_load=\"YES\"' \ 1277 $BSDINSTALL_TMPBOOT/loader.conf.gmirror || return $FAILURE 1278 fi 1279 1280 # We're all done unless we should go on for boot pool 1281 [ "$ZFSBOOT_BOOT_POOL" ] || return $SUCCESS 1282 1283 # Set cachefile for boot pool so it auto-imports at system start 1284 f_dprintf "$funcname: Configuring zpool.cache for boot pool..." 1285 f_eval_catch $funcname zpool "$ZPOOL_SET" \ 1286 "cachefile=\"$BSDINSTALL_CHROOT/boot/zfs/zpool.cache\"" \ 1287 "$bootpool_name" || return $FAILURE 1288 1289 # Some additional geli(8) requirements for loader.conf(5) 1290 for option in \ 1291 'zpool_cache_load=\"YES\"' \ 1292 'zpool_cache_type=\"/boot/zfs/zpool.cache\"' \ 1293 'zpool_cache_name=\"/boot/zfs/zpool.cache\"' \ 1294 ; do 1295 f_eval_catch $funcname echo "$ECHO_APPEND" "$option" \ 1296 $BSDINSTALL_TMPBOOT/loader.conf.zfs || 1297 return $FAILURE 1298 done 1299 f_eval_catch $funcname printf "$PRINTF_CONF" vfs.root.mountfrom \ 1300 "\"zfs:$zroot_name/$zroot_bootfs\"" \ 1301 $BSDINSTALL_TMPBOOT/loader.conf.root || return $FAILURE 1302 1303 # We're all done unless we should go on to do encryption 1304 [ "$ZFSBOOT_GELI_ENCRYPTION" ] || return $SUCCESS 1305 1306 # 1307 # Configure geli(8)-based encryption 1308 # 1309 f_dprintf "$funcname: Configuring disk encryption..." 1310 f_eval_catch $funcname echo "$ECHO_APPEND" 'aesni_load=\"YES\"' \ 1311 $BSDINSTALL_TMPBOOT/loader.conf.aesni || return $FAILURE 1312 f_eval_catch $funcname echo "$ECHO_APPEND" 'geom_eli_load=\"YES\"' \ 1313 $BSDINSTALL_TMPBOOT/loader.conf.geli || return $FAILURE 1314 for disk in $disks; do 1315 f_eval_catch $funcname printf "$PRINTF_CONF" \ 1316 geli_%s_keyfile0_load "$disk$targetpart YES" \ 1317 $BSDINSTALL_TMPBOOT/loader.conf.$disk$targetpart || 1318 return $FAILURE 1319 f_eval_catch $funcname printf "$PRINTF_CONF" \ 1320 geli_%s_keyfile0_type \ 1321 "$disk$targetpart $disk$targetpart:geli_keyfile0" \ 1322 $BSDINSTALL_TMPBOOT/loader.conf.$disk$targetpart || 1323 return $FAILURE 1324 f_eval_catch $funcname printf "$PRINTF_CONF" \ 1325 geli_%s_keyfile0_name \ 1326 "$disk$targetpart \"$ZFSBOOT_GELI_KEY_FILE\"" \ 1327 $BSDINSTALL_TMPBOOT/loader.conf.$disk$targetpart || 1328 return $FAILURE 1329 done 1330 1331 return $SUCCESS 1332} 1333 1334# dialog_menu_diskinfo 1335# 1336# Prompt the user to select a disk and then provide detailed info on it. 1337# 1338dialog_menu_diskinfo() 1339{ 1340 local device disk 1341 1342 # 1343 # Break from loop when user cancels disk selection 1344 # 1345 while :; do 1346 device=$( msg_cancel="$msg_back" f_device_menu \ 1347 "$DIALOG_TITLE" "$msg_select_a_disk_device" "" \ 1348 $DEVICE_TYPE_DISK 2>&1 ) || break 1349 $device get name disk 1350 1351 # Show gpart(8) `show' and camcontrol(8) `inquiry' data 1352 f_show_msg "$msg_detailed_disk_info" \ 1353 "$disk" "$( gpart show $disk 2> /dev/null )" \ 1354 "$disk" "$( camcontrol inquiry $disk 2> /dev/null )" \ 1355 "$disk" "$( camcontrol identify $disk 2> /dev/null )" 1356 done 1357 1358 return $SUCCESS 1359} 1360 1361############################################################ MAIN 1362 1363# 1364# Initialize 1365# 1366f_dialog_title "$msg_zfs_configuration" 1367f_dialog_backtitle "$msg_freebsd_installer" 1368 1369# User may have specifically requested ZFS-related operations be interactive 1370! f_interactive && f_zfsinteractive && unset $VAR_NONINTERACTIVE 1371 1372# 1373# Debugging 1374# 1375f_dprintf "BSDINSTALL_CHROOT=[%s]" "$BSDINSTALL_CHROOT" 1376f_dprintf "BSDINSTALL_TMPETC=[%s]" "$BSDINSTALL_TMPETC" 1377f_dprintf "FSTAB_FMT=[%s]" "$FSTAB_FMT" 1378 1379# 1380# Loop over the main menu until we've accomplished what we came here to do 1381# 1382while :; do 1383 if ! f_interactive; then 1384 retval=$DIALOG_OK 1385 mtag=">>> $msg_install" 1386 else 1387 dialog_menu_main 1388 retval=$? 1389 f_dialog_menutag_fetch mtag 1390 fi 1391 1392 f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" 1393 [ $retval -eq $DIALOG_OK ] || f_die 1394 1395 case "$mtag" in 1396 ">>> $msg_install") 1397 # 1398 # First, validate the user's selections 1399 # 1400 1401 # Make sure they gave us a name for the pool 1402 if [ ! "$ZFSBOOT_POOL_NAME" ]; then 1403 f_dprintf "Pool name cannot be empty." 1404 f_show_err "$msg_pool_name_cannot_be_empty" 1405 continue 1406 fi 1407 1408 # Validate vdev type against number of disks selected/scripted 1409 # (also validates that ZFSBOOT_DISKS are real [probed] disks) 1410 # NB: dialog_menu_layout supports running non-interactively 1411 dialog_menu_layout || continue 1412 1413 # Make sure each disk will be at least 50% ZFS 1414 if f_expand_number "$ZFSBOOT_SWAP_SIZE" swapsize && 1415 f_expand_number "$ZFSBOOT_BOOT_POOL_SIZE" bootsize 1416 then 1417 minsize=$swapsize teeny_disks= 1418 [ "$ZFSBOOT_BOOT_POOL" ] && 1419 minsize=$(( $minsize + $bootsize )) 1420 for disk in $ZFSBOOT_DISKS; do 1421 debug= f_device_find -1 \ 1422 $disk $DEVICE_TYPE_DISK device 1423 $device get capacity disksize || continue 1424 [ ${disksize:-0} -ge 0 ] || disksize=0 1425 disksize=$(( $disksize - $minsize )) 1426 [ $disksize -lt $minsize ] && 1427 teeny_disks="$teeny_disks $disk" 1428 done 1429 if [ "$teeny_disks" ]; then 1430 f_dprintf "swapsize=[%s] bootsize[%s] %s" \ 1431 "$ZFSBOOT_SWAP_SIZE" \ 1432 "$ZFSBOOT_BOOT_POOL_SIZE" \ 1433 "minsize=[$minsize]" 1434 f_dprintf "These disks are too small: %s" \ 1435 "$teeny_disks" 1436 f_show_err "$msg_these_disks_are_too_small" \ 1437 "$ZFSBOOT_SWAP_SIZE" \ 1438 "$ZFSBOOT_BOOT_POOL_SIZE" \ 1439 "$teeny_disks" 1440 continue 1441 fi 1442 fi 1443 1444 # 1445 # Last Chance! 1446 # 1447 if f_interactive; then 1448 dialog_last_chance $ZFSBOOT_DISKS || continue 1449 fi 1450 1451 # 1452 # Let's do this 1453 # 1454 1455 vdev_type="$ZFSBOOT_VDEV_TYPE" 1456 1457 # Blank the vdev type for the default layout 1458 [ "$vdev_type" = "stripe" ] && vdev_type= 1459 1460 zfs_create_boot "$ZFSBOOT_POOL_NAME" \ 1461 "$vdev_type" $ZFSBOOT_DISKS || continue 1462 1463 break # to success 1464 ;; 1465 ?" $msg_pool_type_disks") 1466 ZFSBOOT_CONFIRM_LAYOUT=1 1467 dialog_menu_layout 1468 # User has poked settings, disable later confirmation 1469 ZFSBOOT_CONFIRM_LAYOUT= 1470 ;; 1471 "- $msg_rescan_devices") f_device_rescan ;; 1472 "- $msg_disk_info") dialog_menu_diskinfo ;; 1473 ?" $msg_pool_name") 1474 # Prompt the user to input/change the name for the new pool 1475 f_dialog_input input \ 1476 "$msg_please_enter_a_name_for_your_zpool" \ 1477 "$ZFSBOOT_POOL_NAME" && 1478 ZFSBOOT_POOL_NAME="$input" 1479 ;; 1480 ?" $msg_force_4k_sectors") 1481 # Toggle the variable referenced both by the menu and later 1482 if [ "$ZFSBOOT_GNOP_4K_FORCE_ALIGN" ]; then 1483 ZFSBOOT_GNOP_4K_FORCE_ALIGN= 1484 else 1485 ZFSBOOT_GNOP_4K_FORCE_ALIGN=1 1486 fi 1487 ;; 1488 ?" $msg_encrypt_disks") 1489 # Toggle the variable referenced both by the menu and later 1490 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 1491 ZFSBOOT_GELI_ENCRYPTION= 1492 else 1493 ZFSBOOT_GNOP_4K_FORCE_ALIGN=1 1494 ZFSBOOT_GELI_ENCRYPTION=1 1495 fi 1496 ;; 1497 ?" $msg_partition_scheme") 1498 # Toggle between GPT and MBR 1499 if [ "$ZFSBOOT_PARTITION_SCHEME" = GPT ]; then 1500 ZFSBOOT_PARTITION_SCHEME=MBR 1501 else 1502 ZFSBOOT_PARTITION_SCHEME=GPT 1503 fi 1504 ;; 1505 ?" $msg_swap_size") 1506 # Prompt the user to input/change the swap size for each disk 1507 f_dialog_input input \ 1508 "$msg_please_enter_amount_of_swap_space" \ 1509 "$ZFSBOOT_SWAP_SIZE" && 1510 ZFSBOOT_SWAP_SIZE="${input:-0}" 1511 ;; 1512 ?" $msg_swap_mirror") 1513 # Toggle the variable referenced both by the menu and later 1514 if [ "$ZFSBOOT_SWAP_MIRROR" ]; then 1515 ZFSBOOT_SWAP_MIRROR= 1516 else 1517 ZFSBOOT_SWAP_MIRROR=1 1518 fi 1519 ;; 1520 ?" $msg_swap_encrypt") 1521 # Toggle the variable referenced both by the menu and later 1522 if [ "$ZFSBOOT_SWAP_ENCRYPTION" ]; then 1523 ZFSBOOT_SWAP_ENCRYPTION= 1524 else 1525 ZFSBOOT_SWAP_ENCRYPTION=1 1526 fi 1527 ;; 1528 esac 1529done 1530 1531return $SUCCESS 1532 1533################################################################################ 1534# END 1535################################################################################ 1536