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 layout 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 setuid=off 124 125 # Ports tree 126 /usr/ports compression=lz4,setuid=off 127 /usr/ports/distfiles compression=off,exec=off,setuid=off 128 /usr/ports/packages compression=off,exec=off,setuid=off 129 130 # Source tree (compressed) 131 /usr/src compression=lz4,exec=off,setuid=off 132 /usr/obj # Object files 133 134 # Create /var and friends 135 /var mountpoint=/var 136 /var/crash compression=lz4,exec=off,setuid=off 137 /var/db exec=off,setuid=off 138 /var/empty exec=off,setuid=off 139 /var/log compression=lz4,exec=off,setuid=off 140 /var/mail compression=lz4,exec=off,setuid=off 141 /var/run exec=off,setuid=off 142 /var/tmp compression=lz4,exec=on,setuid=off 143" # END-QUOTE 144 145############################################################ GLOBALS 146 147# 148# Strings that should be moved to an i18n file and loaded with f_include_lang() 149# 150hline_alnum_arrows_punc_tab_enter="Use alnum, arrows, punctuation, TAB or ENTER" 151hline_arrows_space_tab_enter="Use arrows, SPACE, TAB or ENTER" 152hline_arrows_tab_enter="Press arrows, TAB or ENTER" 153msg_back="Back" 154msg_cancel="Cancel" 155msg_change="Change Selection" 156msg_configure_options="Configure Options:" 157msg_create="Install" 158msg_create_desc="Proceed with Installation" 159msg_create_help="Create ZFS boot pool with displayed options" 160msg_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" 161msg_disk_info="Disk Info" 162msg_disk_info_help="Get detailed information on disk device(s)" 163msg_disks_to_use="Disks To Use" 164msg_disks_to_use_help="Choose which disks to use for the Virtual Device (Required)" 165msg_force_4k_sectors="Force 4K Sectors?" 166msg_force_4k_sectors_help="Use gnop(8) to configure forced 4K sector alignment" 167msg_freebsd_installer="FreeBSD Installer" 168msg_geli_encryption="Encrypt Disks?" 169msg_geli_encryption_help="Use geli(8) to encrypt all data partitions" 170msg_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" 171msg_geli_setup="Initializing encryption on the selected disks, this will take several seconds per disk" 172msg_invalid_virtual_device_type="Invalid Virtual Device type \`%s'" 173msg_invalid_virtual_device_type_help="Select another Virtual Device type or Cancel to\nreturn to the ZFS menu. From there you can select\nmore disks or rescan for additional devices." 174msg_last_chance_are_you_sure="Last Chance! Are you sure you want to destroy the current contents of the following disks:\n%s" 175msg_last_chance_are_you_sure_color="\\\\ZrLast Chance!\\\\ZR Are you \\\\Z1sure\\\\Zn you want to \\\\Zr\\\\Z1destroy\\\\Zn the current contents of the following disks:\n%s" 176msg_mirror_desc="Mirror - n-Way Mirroring" 177msg_mirror_help="[2+ Disks] Mirroring provides the best performance, but the least storage" 178msg_no="NO" 179msg_no_disks_present_to_configure="No disk(s) present to configure" 180msg_no_disks_selected="No disks selected." 181msg_not_enough_disks_selected="Not enough disks selected. (%u < %u wanted)" 182msg_ok="OK" 183msg_partition_scheme="Partition Scheme" 184msg_partition_scheme_help="Toggle between GPT and MBR partitioning schemes" 185msg_please_enter_a_name_for_your_zpool="Please enter a name for your zpool:" 186msg_please_enter_amount_of_swap_space="Please enter amount of swap space (SI-Unit suffixes\nrecommended; e.g., \`2g' for 2 Gigabytes):" 187msg_please_select_one_or_more_disks="Please select one or more disks to create a zpool:" 188msg_pool_name="Pool Name" 189msg_pool_name_cannot_be_empty="Pool name cannot be empty." 190msg_pool_name_help="Customize the name of the zpool to be created (Required)" 191msg_processing_selection="Processing selection..." 192msg_raidz1_desc="RAID-Z1 - Single Redundant RAID" 193msg_raidz1_help="[3+ Disks] Withstand failure of 1 disk. Recommended for: 3, 5 or 9 disks" 194msg_raidz2_desc="RAID-Z2 - Double Redundant RAID" 195msg_raidz2_help="[4+ Disks] Withstand failure of 2 disks. Recommended for: 4, 6 or 10 disks" 196msg_raidz3_desc="RAID-Z3 - Triple Redundant RAID" 197msg_raidz3_help="[5+ Disks] Withstand failure of 3 disks. Recommended for: 5, 7 or 11 disks" 198msg_rescan_devices="Rescan Devices" 199msg_rescan_devices_help="Scan for device changes" 200msg_select="Select" 201msg_select_a_disk_device="Select a disk device" 202msg_select_virtual_device_type="Select Virtual Device type:" 203msg_stripe_desc="Stripe - No Redundancy" 204msg_stripe_help="[1+ Disks] Striping provides maximum storage but no redundancy" 205msg_swap_size="Swap Size" 206msg_swap_size_help="Customize how much swap space is allocated to each selected disk" 207msg_these_disks_are_too_small="These disks are too small given the amount of requested\nswap (%s) and/or GELI (%s) partitions, which would take\n50%% or more (not recommended) of each of the following\nselected disk devices:\n\n %s\n\nRecommend changing partition size(s) and/or selecting a\ndifferent set of devices." 208msg_yes="YES" 209msg_zfs_configuration="ZFS Configuration" 210msg_zfs_vdev_type="ZFS VDev Type" 211msg_zfs_vdev_type_help="Select type of ZFS Virtual Device to create" 212 213############################################################ FUNCTIONS 214 215# dialog_menu_main 216# 217# Display the dialog(1)-based application main menu. 218# 219dialog_menu_main() 220{ 221 local title="$DIALOG_TITLE" 222 local btitle="$DIALOG_BACKTITLE" 223 local prompt="$msg_configure_options" 224 local force4k="$msg_no" 225 local usegeli="$msg_no" 226 [ "$ZFSBOOT_GNOP_4K_FORCE_ALIGN" ] && force4k="$msg_yes" 227 [ "$ZFSBOOT_GELI_ENCRYPTION" ] && usegeli="$msg_yes" 228 local menu_list=" 229 '>>> $msg_create' '$msg_create_desc' 230 '$msg_create_help' 231 '- $msg_rescan_devices' '*' 232 '$msg_rescan_devices_help' 233 '- $msg_disk_info' '*' 234 '$msg_disk_info_help' 235 '1 $msg_pool_name' '$ZFSBOOT_POOL_NAME' 236 '$msg_pool_name_help' 237 '2 $msg_disks_to_use' '$ZFSBOOT_DISKS' 238 '$msg_disks_to_use_help' 239 '3 $msg_zfs_vdev_type' '$ZFSBOOT_VDEV_TYPE' 240 '$msg_zfs_vdev_type_help' 241 '4 $msg_force_4k_sectors' '$force4k' 242 '$msg_force_4k_sectors_help' 243 '5 $msg_geli_encryption' '$usegeli' 244 '$msg_geli_encryption_help' 245 '6 $msg_partition_scheme' '$ZFSBOOT_PARTITION_SCHEME' 246 '$msg_partition_scheme_help' 247 '7 $msg_swap_size' '$ZFSBOOT_SWAP_SIZE' 248 '$msg_swap_size_help' 249 " # END-QUOTE 250 local defaultitem= # Calculated below 251 local hline="$hline_alnum_arrows_punc_tab_enter" 252 253 local height width rows 254 eval f_dialog_menu_with_help_size height width rows \ 255 \"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" $menu_list 256 257 # Obtain default-item from previously stored selection 258 f_dialog_default_fetch defaultitem 259 260 local menu_choice 261 menu_choice=$( eval $DIALOG \ 262 --title \"\$title\" \ 263 --backtitle \"\$btitle\" \ 264 --hline \"\$hline\" \ 265 --item-help \ 266 --ok-label \"\$msg_select\" \ 267 --cancel-label \"\$msg_cancel\" \ 268 --default-item \"\$defaultitem\" \ 269 --menu \"\$prompt\" \ 270 $height $width $rows \ 271 $menu_list \ 272 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 273 ) 274 local retval=$? 275 f_dialog_data_sanitize menu_choice 276 f_dialog_menutag_store "$menu_choice" 277 278 # Only update default-item on success 279 [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" 280 281 return $retval 282} 283 284# dialog_edit_disks 285# 286# Edit the list of disks to be used by the ZFS boot pool. 287# 288dialog_edit_disks() 289{ 290 local title="$DIALOG_TITLE" 291 local btitle="$DIALOG_BACKTITLE" 292 local prompt="$msg_please_select_one_or_more_disks" 293 local check_list= # Calculated below 294 local hline="$hline_arrows_space_tab_enter" 295 local dev vardev disks= 296 297 # 298 # Get a [new] list of disk devices 299 # 300 f_device_find "" $DEVICE_TYPE_DISK disks 301 if [ ! "$disks" ]; then 302 f_show_msg "$msg_no_disks_present_to_configure" 303 return $FAILURE 304 fi 305 306 # Lets sort the disks array to be more user friendly 307 disks=$( echo "$disks" | tr ' ' '\n' | sort | tr '\n' ' ' ) 308 309 # 310 # Loop through the list of selected disks and create temporary local 311 # variables mapping their status onto an up-to-date list of disks. 312 # 313 for dev in $ZFSBOOT_DISKS; do 314 f_str2varname "$dev" vardev 315 local _${vardev}_status=on 316 done 317 318 # 319 # Create the checklist menu of discovered disk devices 320 # 321 local on_off 322 for dev in $disks; do 323 local desc= 324 device_$dev get desc desc 325 f_shell_escape "$desc" desc 326 f_str2varname "$dev" vardev 327 f_getvar _${vardev}_status:-off on_off 328 check_list="$check_list '$dev' '$desc' $on_off" 329 done 330 331 # 332 # Prompt the user to check some disks 333 # 334 local height width rows 335 eval f_dialog_checklist_size height width rows \ 336 \"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" $check_list 337 disks=$( eval $DIALOG \ 338 --title \"\$DIALOG_TITLE\" \ 339 --backtitle \"\$DIALOG_BACKTITLE\" \ 340 --hline \"\$hline\" \ 341 --ok-label \"\$msg_ok\" \ 342 --cancel-label \"\$msg_cancel\" \ 343 --checklist \"\$prompt\" \ 344 $height $width $rows \ 345 $check_list \ 346 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 347 ) || return $? 348 # Exit if user either pressed ESC or chose Cancel/No 349 f_dialog_data_sanitize disks 350 351 ZFSBOOT_DISKS="$disks" 352 353 return $DIALOG_OK 354} 355 356# dialog_menu_vdev 357# 358# Prompt the user to select a a Virtual Device type. 359# 360dialog_menu_vdev() 361{ 362 local title="$DIALOG_TITLE" 363 local btitle="$DIALOG_BACKTITLE" 364 local prompt="$msg_select_virtual_device_type" 365 366 # Make sure [potentially scripted] selections are real 367 real_disks= 368 for disk in $ZFSBOOT_DISKS; do 369 f_struct device_$disk && real_disks="$real_disks $disk" 370 done 371 # Make sure we have at least one real disk selected 372 ndisks=$( set -- $real_disks; echo $# ) 373 374 local menu_list=" 375 'stripe' '$msg_stripe_desc' '$msg_stripe_help' 376 'mirror' '$msg_mirror_desc' '$msg_mirror_help' 377 'raidz1' '$msg_raidz1_desc' '$msg_raidz1_help' 378 'raidz2' '$msg_raidz2_desc' '$msg_raidz2_help' 379 'raidz3' '$msg_raidz3_desc' '$msg_raidz3_help' 380 " # END-QUOTE 381 382 local defaultitem="$ZFSBOOT_VDEV_TYPE" 383 local hline="$hline_arrows_tab_enter" 384 local error_msg revalidate_choice 385 386 local mheight mwidth mrows 387 eval f_dialog_menu_size mheight mwidth mrows \ 388 \"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" $menu_list 389 local iheight iwidth 390 f_dialog_infobox_size iheight iwidth \ 391 "$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$msg_processing_selection" 392 393 local menu_choice 394 menu_choice=$( eval $DIALOG \ 395 --title \"\$title\" \ 396 --backtitle \"\$btitle\" \ 397 --hline \"\$hline\" \ 398 --ok-label \"\$msg_ok\" \ 399 --cancel-label \"\$msg_cancel\" \ 400 --item-help \ 401 --default-item \"\$defaultitem\" \ 402 --menu \"\$prompt\" \ 403 $mheight $mwidth $mrows \ 404 $menu_list \ 405 --and-widget \ 406 ${USE_XDIALOG:+--no-buttons} \ 407 --infobox \"\$msg_processing_selection\" \ 408 $iheight $iwidth \ 409 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 410 ) || return $FAILURE 411 f_dialog_data_sanitize menu_choice 412 sleep 0.5 # Give time to read `--and-widget --info-box' 413 414 # Make sure we have enough disks for the desired vdev type 415 case "$menu_choice" in 416 stripe) want_disks=1 ;; 417 mirror) want_disks=2 ;; 418 raidz1) want_disks=3 ;; 419 raidz2) want_disks=4 ;; 420 raidz3) want_disks=5 ;; 421 *) 422 f_show_msg "$msg_invalid_virtual_device_type" \ 423 "$menu_choice" 424 continue 425 esac 426 if [ $ndisks -lt $want_disks ]; then 427 msg_yes="$msg_change" msg_no="$msg_cancel" f_yesno \ 428 "%s: $msg_not_enough_disks_selected\n%s" \ 429 "$menu_choice" $ndisks $want_disks \ 430 "$msg_invalid_virtual_device_type_help" || 431 return $FAILURE 432 dialog_menu_vdev 433 else 434 ZFSBOOT_VDEV_TYPE="$menu_choice" 435 fi 436} 437 438# zfs_create_diskpart $disk $index 439# 440# For each block device to be used in the zpool, rather than just create the 441# zpool with the raw block devices (e.g., da0, da1, etc.) we create partitions 442# so we can have some real swap. This also provides wiggle room incase your 443# replacement drivers do not have the exact same sector counts. 444# 445# NOTE: The MBR layout is more complicated (GPT is preferred). 446# 447zfs_create_diskpart() 448{ 449 local disk="$1" index="$2" 450 local funcname=zfs_create_diskpart 451 local disksize partsize 452 453 # Check arguments 454 [ "$disk" -a "$index" ] || return $FAILURE 455 456 # 457 # Destroy whatever partition layout is currently on disk. 458 # NOTE: `-F' required to destroy if partitions still exist. 459 # NOTE: Failure is ok here, blank disk will have nothing to destroy. 460 # 461 f_quietly gpart destroy -F $disk 462 f_quietly zpool labelclear -f /dev/$disk # Kill it with fire 463 464 # Make doubly-sure backup GPT is destroyed 465 f_quietly gpart create -s gpt $disk || return $FAILURE 466 f_quietly gpart destroy -F $disk || return $FAILURE 467 468 # Calculate partition size given desired amount of swap 469 device_$disk get capacity disksize || return $FAILURE 470 partsize=$(( $disksize - $swapsize )) 471 472 # 473 # Lay down the desired type of partition scheme 474 # 475 local setsize mbrindex 476 case "$ZFSBOOT_PARTITION_SCHEME" in 477 ""|GPT) 478 # 479 # 1. Create GPT layout using labels 480 # 481 gpart create -s gpt $disk || return $FAILURE 482 483 # 484 # 2. Add small freebsd-boot partition labeled `boot#' 485 # 486 gpart add -l gptboot$index -t freebsd-boot -s 512k $disk || 487 return $FAILURE 488 gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 $disk || 489 return $FAILURE 490 491 # zpool will use the `zfs#' GPT labels 492 bootpart=p2 targetpart=p2 493 494 # Change things around if we are using GELI 495 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 496 bootpart=p2 targetpart=p3 497 partsize=$(( $partsize - $gelisize )) 498 gpart add -l boot$index -t freebsd-zfs \ 499 -s ${gelisize}b -a 1m $disk || return $FAILURE 500 # Pedantically nuke any old labels, stop geli 501 f_quietly zpool labelclear -f /dev/$disk$bootpart 502 f_quietly geli detach -f /dev/$disk$targetpart 503 fi 504 505 # 506 # 3. Add freebsd-zfs partition labeled `zfs#' for zpool 507 # NOTE: Using above calculated partsize to leave room for swap. 508 # 509 [ $swapsize -gt 0 ] && setsize="-s ${partsize}b" 510 gpart add -l zfs$index -t freebsd-zfs $setsize -a 1m $disk || 511 return $FAILURE 512 f_quietly zpool labelclear -f /dev/$disk$targetpart # Pedantic 513 514 # 515 # 4. Add freebsd-swap partition labeled `swap#' 516 # 517 if [ $swapsize -gt 0 ]; then 518 gpart add -l swap$index -t freebsd-swap -a 1m $disk || 519 return $FAILURE 520 # Update fstab(5) 521 printf "$fstab_fmt" \ 522 /dev/gpt/swap$index none swap sw 0 0 \ 523 >> $BSDINSTALL_TMPETC/fstab || return $FAILURE 524 fi 525 ;; 526 527 MBR) 528 # 529 # 1. Create MBR layout (no labels) 530 # 531 gpart create -s mbr $disk || return $FAILURE 532 gpart bootcode -b /boot/boot0 $disk || return $FAILURE 533 534 # 535 # 2. Add freebsd slice with all available space 536 # 537 gpart add -t freebsd $disk || return $FAILURE 538 gpart set -a active -i 1 $disk || return $FAILURE 539 f_quietly zpool labelclear -f /dev/${disk}s1 # Pedantic 540 f_quietly gpart destroy -F ${disk}s1 # Pedantic 541 542 # 543 # 3. Write BSD sceme to the freebsd slice 544 # 545 gpart create -s BSD ${disk}s1 || return $FAILURE 546 547 # zpool will use s1a (no labels) 548 bootpart=s1a targetpart=s1a mbrindex=1 549 550 # Change things around if we are using GELI 551 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 552 bootpart=s1a targetpart=s1d 553 partsize=$(( $partsize - $gelisize )) 554 mbrindex=4 # If this is s1a then make the zpool s1d 555 gpart add -t freebsd-zfs -i 1 -s ${gelisize}b \ 556 ${disk}s1 || return $FAILURE 557 # Pedantically nuke any old labels, stop geli 558 f_quietly zpool labelclear -f /dev/$disk$bootpart 559 f_quietly geli detach -f /dev/$disk$targetpart 560 fi 561 562 # 563 # 4. Partition the BSD slice for ZFS 564 # NOTE: Using above calculated partsize to leave room for swap. 565 # 566 [ $swapsize -gt 0 ] && setsize="-s ${partsize}b" 567 gpart add -t freebsd-zfs -i $mbrindex $setsize ${disk}s1 || 568 return $FAILURE 569 f_quietly zpool labelclear -f /dev/$disk$targetpart # Pedantic 570 571 # 572 # 5. Add freebsd-swap partition 573 # 574 if [ $swapsize -gt 0 ]; then 575 gpart add -t freebsd-swap -i 2 ${disk}s1 || 576 return $FAILURE 577 # Update fstab(5) 578 printf "$fstab_fmt" /dev/${disk}s1b none swap sw 0 0 \ 579 >> $BSDINSTALL_TMPETC/fstab || return $FAILURE 580 fi 581 ;; 582 583 *) 584 printf "%s: %s is an unsupported partition scheme" \ 585 "$funcname" "$ZFSBOOT_PARTITION_SCHEME" >&2 586 return $FAILURE 587 588 esac # $ZFSBOOT_PARTITION_SCHEME 589 590 return $SUCCESS 591} 592 593# zfs_create_boot $poolname $vdev_type $real_disks ... 594# 595# Creates boot pool and dataset layout. Returns error if something goes wrong. 596# Errors are printed to stderr for collection and display. 597# 598zfs_create_boot() 599{ 600 local poolname="$1" vdev_type="$2" 601 local fstab_fmt="%s\t\t%s\t%s\t%s\t\t%s\t%s\n" 602 local funcname=zfs_create_boot 603 local bootpart targetpart 604 605 shift 2 # name vdev_type 606 607 # We may need this later 608 local realdisks=$* 609 610 # Pedantic checks; should never be seen 611 if [ ! "$poolname" ]; then 612 echo "$funcname: NULL poolname" >&2 613 return $FAILURE 614 fi 615 if [ $# -lt 1 ]; then 616 echo "$funcname: missing disk arguments" >&2 617 return $FAILURE 618 fi 619 620 # Initialize fstab(5) 621 printf "$fstab_fmt" \ 622 "# Device" Mountpoint FStype Options Dump "Pass#" \ 623 >> $BSDINSTALL_TMPETC/fstab || return $FAILURE 624 625 # Expand SI units in desired sizes 626 local swapsize gelisize 627 f_expand_number "$ZFSBOOT_SWAP_SIZE" swapsize || return $FAILURE 628 f_expand_number "$ZFSBOOT_GELI_BOOT_SIZE" gelisize || return $FAILURE 629 630 # Prepare the disks 631 local n=0 632 for disk in $*; do 633 zfs_create_diskpart $disk $n || return $FAILURE 634 n=$(( $n + 1 )) 635 done 636 637 # MBR boot loader hack part 1 638 # We have to do this early because geli gets in the way later 639 if [ "$ZFSBOOT_PARTITION_SCHEME" = "MBR" ]; then 640 for disk in $realdisks; do 641 dd if=/boot/zfsboot of=/dev/${disk}s1 count=1 || 642 return $FAILURE 643 done 644 fi 645 646 # Forced 4k alignment support provided by Geom NOP (see gnop(8)) 647 local unenc_list= 648 if [ "$ZFSBOOT_GNOP_4K_FORCE_ALIGN" ]; then 649 local new_list= 650 for disk in $*; do 651 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 652 # We don't gnop the encrypted partition 653 # because geli will do this for us 654 # gnop the unencrypted disk 655 gnop create -S 4096 $disk$bootpart || 656 return $FAILURE 657 unenc_list="$unenc_list $disk$bootpart.nop" 658 else 659 gnop create -S 4096 $disk$targetpart || 660 return $FAILURE 661 new_list="$new_list $disk$targetpart.nop" 662 fi 663 done 664 set -- $new_list 665 else 666 local new_list= 667 for disk in $*; do 668 new_list="$new_list $disk$targetpart" 669 [ "$ZFSBOOT_GELI_ENCRYPTION" ] && 670 unenc_list="$unenc_list $disk$bootpart" 671 done 672 set -- $new_list 673 fi 674 675 # 676 # If encryption is enabled, we need to create the GEOMs 677 # 678 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 679 local bootvdev= 680 local geli_pool="$BSDINSTALL_CHROOT/$ZFSBOOT_GELI_POOL_NAME" 681 local key="$ZFSBOOT_GELI_KEY_FILE" 682 683 # Create the parent directories for our unencrypted pool 684 f_quietly umount /mnt 685 mount -t tmpfs none $BSDINSTALL_CHROOT || return $FAILURE 686 687 # Create mirror across the unencrypted partition on all disks 688 [ $( set -- $unenc_list; echo $# ) -gt 1 ] && bootvdev=mirror 689 690 zpool create -o altroot=$BSDINSTALL_CHROOT \ 691 -m "/$ZFSBOOT_GELI_POOL_NAME" -f \ 692 "$ZFSBOOT_GELI_POOL_NAME" $bootvdev $unenc_list || 693 return $FAILURE 694 mkdir -p $geli_pool/boot || return $FAILURE 695 696 # Generate an encryption key using random(4) 697 dd if=/dev/random of="$geli_pool/$key" bs=4096 count=1 || 698 return $FAILURE 699 700 # Create the geli(8) GEOMS 701 local geli_list 702 msg_enter_new_password="$msg_geli_password" \ 703 f_dialog_input_password || return $FAILURE 704 f_dialog_info "$msg_geli_setup" \ 705 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 706 for disk in $realdisks; do 707 echo "$pw_password" | geli init -b -B \ 708 "$geli_pool/boot/$disk$targetpart.eli" \ 709 -e AES-XTS -J - -K "$geli_pool/$key" -l 256 \ 710 -s 4096 $disk$targetpart || return $FAILURE 711 echo "$pw_password" | geli attach -j - \ 712 -k "$geli_pool/$key" $disk$targetpart || 713 return $FAILURE 714 geli_list="$geli_list $disk$targetpart.eli" 715 done 716 set -- $geli_list 717 zfs unmount "$ZFSBOOT_GELI_POOL_NAME" || return $FAILURE 718 f_quietly umount /mnt # done with tmpfs 719 fi 720 721 # 722 # Create the ZFS pool with desired type and disk devices 723 # 724 zpool create -o altroot=$BSDINSTALL_CHROOT -m none -f \ 725 "$poolname" $vdev_type $* || return $FAILURE 726 727 # Customize the zpool a bit... 728 zfs set checksum=fletcher4 "$poolname" || return $FAILURE 729 zfs set atime=off "$poolname" || return $FAILURE 730 731 # 732 # Create ZFS dataset layout within the new boot pool 733 # 734 echo "$ZFSBOOT_DATASETS" | while read dataset options; do 735 # Skip blank lines and comments 736 case "$dataset" in "#"*|"") continue; esac 737 # Remove potential inline comments in options 738 options="${options%%#*}" 739 # Replace tabs with spaces 740 f_replaceall "$options" " " " " options 741 # Reduce contiguous runs of space to one single space 742 oldoptions= 743 while [ "$oldoptions" != "$options" ]; do 744 oldoptions="$options" 745 f_replaceall "$options" " " " " options 746 done 747 # Replace both commas and spaces with ` -o ' 748 f_replaceall "$options" "[ ,]" " -o " options 749 # Create the dataset with desired options 750 zfs create ${options:+-o $options} "$poolname$dataset" || 751 return $FAILURE 752 done 753 754 # Touch up permissions on the tmp directories 755 chmod 1777 $BSDINSTALL_CHROOT/tmp || return $FAILURE 756 chmod 1777 $BSDINSTALL_CHROOT/var/tmp || return $FAILURE 757 758 # Create symlink(s) 759 [ "$ZFSBOOT_GELI_ENCRYPTION" ] && 760 { ln -s $ZFSBOOT_GELI_POOL_NAME/boot $BSDINSTALL_CHROOT/boot || 761 return $FAILURE; } 762 763 # Set bootfs property 764 zpool set bootfs="$poolname/$ZFSBOOT_BEROOT_NAME/$ZFSBOOT_BOOTFS_NAME" \ 765 "$poolname" || return $FAILURE 766 767 # Export the pool(s) 768 zpool export "$poolname" || return $FAILURE 769 [ "$ZFSBOOT_GELI_ENCRYPTION" ] && 770 { zpool export "$ZFSBOOT_GELI_POOL_NAME" || return $FAILURE; } 771 772 # Destroy the gnop devices (if enabled) 773 for disk in ${ZFSBOOT_GNOP_4K_FORCE_ALIGN:+$realdisks}; do 774 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 775 f_quietly gnop destroy $disk$bootpart.nop 776 else 777 f_quietly gnop destroy $disk$targetpart.nop 778 fi 779 done 780 781 # MBR boot loader hack part 2 782 if [ "$ZFSBOOT_PARTITION_SCHEME" = "MBR" ]; then 783 # Stick the ZFS boot loader in the "convienient hole" after 784 # the ZFS internal metadata 785 for disk in $realdisks; do 786 dd if=/boot/zfsboot of=/dev/$disk$bootpart \ 787 skip=1 seek=1024 || return $FAILURE 788 done 789 fi 790 791 # Re-import the ZFS pool(s) 792 zpool import -o altroot=$BSDINSTALL_CHROOT $poolname || return $FAILURE 793 [ "$ZFSBOOT_GELI_ENCRYPTION" ] && 794 { zpool import -o altroot=$BSDINSTALL_CHROOT \ 795 "$ZFSBOOT_GELI_POOL_NAME" || return $FAILURE; } 796 797 # While this is apparently not needed, it seems to help MBR 798 mkdir -p $BSDINSTALL_CHROOT/boot/zfs || return $FAILURE 799 zpool set cachefile=$BSDINSTALL_CHROOT/boot/zfs/zpool.cache \ 800 "$poolname" || return $FAILURE 801 802 # Last, but not least... required lines for rc.conf(5)/loader.conf(5) 803 # NOTE: We later concatenate these into their destination 804 echo 'zfs_enable="YES"' > $BSDINSTALL_TMPETC/rc.conf.zfs || 805 return $FAILURE 806 echo 'zfs_load="YES"' > $BSDINSTALL_TMPBOOT/loader.conf.zfs || 807 return $FAILURE 808 809 # We're all done unless we should go on to do encryption 810 [ "$ZFSBOOT_GELI_ENCRYPTION" ] || return $SUCCESS 811 812 # Some additional GELI requirements for loader.conf(5) 813 echo 'zpool_cache_load="YES"' \ 814 >> $BSDINSTALL_TMPBOOT/loader.conf.zfs || return $FAILURE 815 echo 'zpool_cache_type="/boot/zfs/zpool.cache"' \ 816 >> $BSDINSTALL_TMPBOOT/loader.conf.zfs || return $FAILURE 817 echo 'zpool_cache_name="/boot/zfs/zpool.cache"' \ 818 >> $BSDINSTALL_TMPBOOT/loader.conf.zfs || return $FAILURE 819 820 # 821 # Configure geli(8)-based encryption 822 # 823 echo 'aesni_load="YES"' \ 824 > $BSDINSTALL_TMPBOOT/loader.conf.aesni || return $FAILURE 825 echo 'geom_eli_load="YES"' \ 826 > $BSDINSTALL_TMPBOOT/loader.conf.geli || return $FAILURE 827 printf 'vfs.root.mountfrom="zfs:%s/%s/%s"\n' "$poolname" \ 828 "$ZFSBOOT_BEROOT_NAME" "$ZFSBOOT_BOOTFS_NAME" \ 829 > $BSDINSTALL_TMPBOOT/loader.conf.root || return $FAILURE 830 for disk in $realdisks; do 831 printf 'geli_%s_keyfile0_load="YES"\n' \ 832 "$disk$targetpart" \ 833 > $BSDINSTALL_TMPBOOT/loader.conf.$disk$targetpart || 834 return $FAILURE 835 printf 'geli_%s_keyfile0_type="%s:geli_keyfile0"\n' \ 836 "$disk$targetpart" "$disk$targetpart" \ 837 >> $BSDINSTALL_TMPBOOT/loader.conf.$disk$targetpart || 838 return $FAILURE 839 printf 'geli_%s_keyfile0_name="%s"\n' \ 840 "$disk$targetpart" "$ZFSBOOT_GELI_KEY_FILE" \ 841 >> $BSDINSTALL_TMPBOOT/loader.conf.$disk$targetpart || 842 return $FAILURE 843 done 844 845 return $SUCCESS 846} 847 848# dialog_menu_diskinfo 849# 850# Prompt the user to select a disk and then provide detailed info on it. 851# 852dialog_menu_diskinfo() 853{ 854 local disk 855 856 # 857 # Break from loop when user cancels disk selection 858 # 859 while :; do 860 disk=$( msg_cancel="$msg_back" f_device_menu \ 861 "$DIALOG_TITLE" "$msg_select_a_disk_device" "" \ 862 $DEVICE_TYPE_DISK 2>&1 ) || break 863 864 # Show gpart(8) `show' and camcontrol(8) `inquiry' data 865 f_show_msg "$msg_detailed_disk_info" \ 866 "$disk" "$( gpart show $disk 2> /dev/null )" \ 867 "$disk" "$( camcontrol inquiry $disk 2> /dev/null )" \ 868 "$disk" "$( camcontrol identify $disk 2> /dev/null )" 869 done 870 871 return $SUCCESS 872} 873 874############################################################ MAIN 875 876# 877# Initialize 878# 879f_dialog_title "$msg_zfs_configuration" 880f_dialog_backtitle "$msg_freebsd_installer" 881 882# User may have specifically requested ZFS-related operations be interactive 883! f_interactive && f_zfsinteractive && unset $VAR_NONINTERACTIVE 884 885# 886# Loop over the main menu until we've accomplished what we came here to do 887# 888while :; do 889 if ! f_interactive; then 890 retval=$DIALOG_OK 891 mtag=">>> $msg_create" 892 else 893 dialog_menu_main 894 retval=$? 895 f_dialog_menutag_fetch mtag 896 fi 897 898 f_dprintf "retval=%u mtag=[%s]" $reval "$mtag" 899 [ $retval -eq $DIALOG_OK ] || f_die 900 901 case "$mtag" in 902 ">>> $msg_create") 903 # 904 # First, validate the user's selections 905 # 906 907 # Make sure they gave us a name for the pool 908 if [ ! "$ZFSBOOT_POOL_NAME" ]; then 909 f_show_msg "$msg_pool_name_cannot_be_empty" 910 f_interactive || f_die 911 continue 912 fi 913 # Make sure [potentially scripted] selections are real 914 real_disks= 915 for disk in $ZFSBOOT_DISKS; do 916 f_struct device_$disk && real_disks="$real_disks $disk" 917 done 918 # Make sure we have at least one real disk selected 919 ndisks=$( set -- $real_disks; echo $# ) 920 if [ $ndisks -lt 1 ]; then 921 f_show_msg "$msg_no_disks_selected" 922 f_interactive || f_die 923 continue 924 fi 925 # Make sure we have enough disks for the desired vdev type 926 case "$ZFSBOOT_VDEV_TYPE" in 927 stripe) want_disks=1 ;; 928 mirror) want_disks=2 ;; 929 raidz1) want_disks=3 ;; 930 raidz2) want_disks=4 ;; 931 raidz3) want_disks=5 ;; 932 *) 933 f_show_msg "$msg_invalid_virtual_device_type" \ 934 "$ZFSBOOT_VDEV_TYPE" 935 f_interactive || f_die 936 continue 937 esac 938 if [ $ndisks -lt $want_disks ]; then 939 f_show_msg "%s: $msg_not_enough_disks_selected" \ 940 "$ZFSBOOT_VDEV_TYPE" "$want_disks" 941 f_interactive || f_die 942 continue 943 fi 944 # Make sure each disk will be at least 50% ZFS 945 if f_expand_number "$ZFSBOOT_SWAP_SIZE" swapsize && 946 f_expand_number "$ZFSBOOT_GELI_BOOT_SIZE" gelisize 947 then 948 minsize=$swapsize teeny_disks= 949 [ "$ZFSBOOT_GELI_ENCRYPTION" ] && 950 minsize=$(( $minsize + $gelisize )) 951 for disk in $real_disks; do 952 device_$disk get capacity disksize || continue 953 disksize=$(( $disksize - $minsize )) 954 [ $disksize -lt $minsize ] && 955 teeny_disks="$teeny_disks $disk" 956 done 957 if [ "$teeny_disks" ]; then 958 f_show_msg "$msg_these_disks_are_too_small" \ 959 "$ZFSBOOT_SWAP_SIZE" \ 960 "$ZFSBOOT_GELI_BOOT_SIZE" \ 961 "$teeny_disks" 962 f_interactive || f_die 963 continue 964 fi 965 fi 966 967 # 968 # Last Chance! 969 # 970 if [ ! "$USE_XDIALOG" ]; then 971 f_interactive && DIALOG="$DIALOG --colors" f_noyes \ 972 "$msg_last_chance_are_you_sure_color" \ 973 "$ZFSBOOT_DISKS" || continue 974 else 975 f_interactive && f_noyes \ 976 "$msg_last_chance_are_you_sure" \ 977 "$ZFSBOOT_DISKS" || continue 978 fi 979 980 # 981 # Let's do this 982 # 983 984 vdev_type="$ZFSBOOT_VDEV_TYPE" 985 986 # Blank the vdev type for the default layout 987 [ "$vdev_type" = "stripe" ] && vdev_type= 988 989 if ! error=$( zfs_create_boot "$ZFSBOOT_POOL_NAME" \ 990 "$vdev_type" $real_disks 2>&1 ) 991 then 992 f_dialog_msgbox "$error" 993 f_interactive || f_die 994 continue 995 fi 996 997 break # to success 998 ;; 999 "- $msg_rescan_devices") f_device_rescan ;; 1000 "- $msg_disk_info") dialog_menu_diskinfo ;; 1001 ?" $msg_pool_name") 1002 # Prompt the user to input/change the name for the new pool 1003 f_dialog_input input \ 1004 "$msg_please_enter_a_name_for_your_zpool" \ 1005 "$ZFSBOOT_POOL_NAME" && 1006 ZFSBOOT_POOL_NAME="$input" 1007 ;; 1008 ?" $msg_disks_to_use") dialog_edit_disks ;; 1009 ?" $msg_zfs_vdev_type") dialog_menu_vdev ;; 1010 ?" $msg_force_4k_sectors") 1011 # Toggle the variable referenced both by the menu and later 1012 if [ "$ZFSBOOT_GNOP_4K_FORCE_ALIGN" ]; then 1013 ZFSBOOT_GNOP_4K_FORCE_ALIGN= 1014 else 1015 ZFSBOOT_GNOP_4K_FORCE_ALIGN=1 1016 fi 1017 ;; 1018 ?" $msg_geli_encryption") 1019 # Toggle the variable referenced both by the menu and later 1020 if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then 1021 ZFSBOOT_GELI_ENCRYPTION= 1022 else 1023 ZFSBOOT_GELI_ENCRYPTION=1 1024 fi 1025 ;; 1026 ?" $msg_partition_scheme") 1027 # Toggle between GPT and MBR 1028 if [ "$ZFSBOOT_PARTITION_SCHEME" = GPT ]; then 1029 ZFSBOOT_PARTITION_SCHEME=MBR 1030 else 1031 ZFSBOOT_PARTITION_SCHEME=GPT 1032 fi 1033 ;; 1034 ?" $msg_swap_size") 1035 # Prompt the user to input/change the swap size for each disk 1036 f_dialog_input input \ 1037 "$msg_please_enter_amount_of_swap_space" \ 1038 "$ZFSBOOT_SWAP_SIZE" && 1039 ZFSBOOT_SWAP_SIZE="$input" 1040 ;; 1041 esac 1042done 1043 1044return $SUCCESS 1045 1046################################################################################ 1047# END 1048################################################################################ 1049