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