]> git.proxmox.com Git - pve-installer.git/blobdiff - proxinstall
zfs: fix wrong command reference in error message
[pve-installer.git] / proxinstall
index dd06727b9bfd0934a2cb2a218f3300a6614901ff..79abc34808cd3db0ea3441d5a9a80c3ce90fe685 100755 (executable)
@@ -21,41 +21,28 @@ use Data::Dumper;
 use File::Basename;
 use File::Path;
 use Time::HiRes;
+use POSIX ":sys_wait_h";
 
 use ProxmoxInstallerSetup;
 
-my $setup = ProxmoxInstallerSetup::setup();
-
-my $opt_testmode;
-
 if (!$ENV{G_SLICE} ||  $ENV{G_SLICE} ne "always-malloc") {
     die "do not use slice allocator (run with 'G_SLICE=always-malloc ./proxinstall ...')\n";
 }
 
+my $opt_testmode;
 if (!GetOptions('testmode=s' => \$opt_testmode)) {
     die "usage error\n";
     exit (-1);
 }
 
+$ENV{'LVM_SUPPRESS_FD_WARNINGS'} = '1';
+
+my ($setup, $cd_info) = ProxmoxInstallerSetup::setup();
+
 my $zfstestpool = "test_rpool";
 my $zfspoolname = $opt_testmode ? $zfstestpool : 'rpool';
 my $zfsrootvolname = "$setup->{product}-1";
 
-my $product_cfg = {
-    pve => {
-       fullname => 'Proxmox VE',
-       port => '8006',
-    },
-    pmg => {
-       fullname => 'Proxmox Mail Gateway',
-       port => '8006',
-    },
-    pbs => {
-       fullname => 'Proxmox Backup Server',
-       port => '8007',
-    },
-};
-
 my $storage_cfg_zfs = <<__EOD__;
 dir: local
        path /var/lib/vz
@@ -71,7 +58,7 @@ my $storage_cfg_btrfs = <<__EOD__;
 dir: local
        path /var/lib/vz
        content iso,vztmpl,backup
-       disabled
+       disable
 
 btrfs: local-btrfs
        path /var/lib/pve/local-btrfs
@@ -108,8 +95,10 @@ sub file_read_firstline {
 
 my $logfd = IO::File->new(">/tmp/install.log");
 
-my $proxmox_libdir = $opt_testmode ?
-    Cwd::cwd() . "/testdir/var/lib/pve-installer" : "/var/lib/pve-installer";
+my $proxmox_libdir = $opt_testmode
+    ? Cwd::cwd() . "/testdir/var/lib/proxmox-installer"
+    : "/var/lib/proxmox-installer"
+    ;
 my $proxmox_cddir = $opt_testmode ? "../pve-cd-builder/tmp/data-gz/" : "/cdrom";
 my $proxmox_pkgdir = "${proxmox_cddir}/proxmox/packages/";
 
@@ -409,11 +398,7 @@ sub run_command {
 
     my $orig_pid = $$;
 
-    my $pid;
-    eval {
-       $pid = open3($writer, $reader, $error, @$cmd) || die $!;
-    };
-
+    my $pid = eval { open3($writer, $reader, $error, @$cmd) || die $!; };
     my $err = $@;
 
     # catch exec errors
@@ -484,6 +469,23 @@ sub run_command {
     return $ostream;
 }
 
+# forks and runs the provided coderef in the child
+# do not use syscmd or run_command as both confuse the GTK mainloop if
+# run from a child process
+sub run_in_background {
+    my ($cmd) = @_;
+
+    my $pid = fork() // die "fork failed: $!\n";
+    if (!$pid) {
+       eval { $cmd->(); };
+       if (my $err = $@) {
+           warn "run_in_background error: $err\n";
+           POSIX::_exit(1);
+       }
+       POSIX::_exit(0);
+    }
+}
+
 sub detect_country {
 
     print "trying to detect country...\n";
@@ -776,7 +778,7 @@ sub update_progress {
     my ($frac, $start, $end, $text) = @_;
 
     my $part = $end - $start;
-    my $res = $start + $frac*$part;
+    my $res = $start + $frac * $part;
 
     $progress->set_fraction ($res);
     $progress->set_text (sprintf ("%d%%", int ($res*100)));
@@ -898,8 +900,8 @@ sub zfs_create_rpool {
     syscmd("zfs create $zfspoolname/ROOT/$zfsrootvolname")  == 0 ||
        die "unable to create zfs $zfspoolname/ROOT/$zfsrootvolname volume\n";
 
-    # disable atime during install
-    syscmd("zfs set atime=off $zfspoolname") == 0 ||
+    # default to `relatime` on, fast enough for the installer and production
+    syscmd("zfs set atime=on relatime=on $zfspoolname") == 0 ||
        die "unable to set zfs properties\n";
 
     my $value = $config_options->{compress};
@@ -928,14 +930,19 @@ my $udevadm_trigger_block = sub {
 my $clean_disk = sub {
     my ($disk) = @_;
 
-    my $partitions = `lsblk --output kname --noheadings --path --list $disk`;
-    foreach my $part (split "\n", $partitions) {
+    # sort longest first as we need to cleanup depth-first
+    my @partitions = sort { length($b) <=> length($a) }
+       split("\n", `lsblk --output kname --noheadings --path --list $disk`);
+
+    for my $part (@partitions) {
        next if $part eq $disk;
        next if $part !~ /^\Q$disk\E/;
        eval { syscmd("pvremove -ff -y $part"); };
        eval { syscmd("zpool labelclear -f $part"); };
        eval { syscmd("dd if=/dev/zero of=$part bs=1M count=16"); };
     }
+    eval { syscmd("wipefs -a " . cmd2string(\@partitions)) };
+    warn "$@" if $@;
 };
 
 sub partition_bootable_disk {
@@ -958,7 +965,19 @@ sub partition_bootable_disk {
     }
 
     my $hdgb = int($hdsize/(1024*1024));
-    die "hardisk '$target_dev' too small (${hdgb}GB)\n" if $hdgb < 8;
+
+    my ($hard_limit, $soft_limit) = (2, 8);
+
+    die "root disk '$target_dev' too small (${hdgb} GB < $hard_limit GB)\n" if $hdgb < $hard_limit;
+    if ($hdgb < $soft_limit) {
+       my $response = display_prompt(
+           "Root disk space ${hdgb} GB is below recommended minimum space of $soft_limit GB,"
+           ." installation might not be successful! Continue?"
+       );
+       die "root disk '$target_dev' too small (${hdgb} GB < $soft_limit GB), and warning not accepted.\n"
+           if $response ne 'ok';
+    }
+
 
     syscmd("sgdisk -Z ${target_dev}");
 
@@ -1050,9 +1069,7 @@ sub ask_existing_vg_rename_or_abort {
     }
     $message .= "or cancel the installation?";
 
-    my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'question', 'ok-cancel', $message);
-    my $response = $dialog->run();
-    $dialog->destroy();
+    my $response = display_prompt($message);
 
     if ($response eq 'ok') {
        for my $vg_uuid (keys %$duplicate_vgs) {
@@ -1087,74 +1104,92 @@ sub create_lvm_volumes {
     syscmd("/sbin/vgcreate $vgname $lvmdev") == 0 ||
        die "unable to create volume group '$vgname'\n";
 
-    my $hdgb = int($os_size/(1024*1024));
-    my $space = (($hdgb > 128) ? 16 : ($hdgb/8))*1024*1024;
+    my $hdgb = int($os_size / (1024 * 1024));
+
+    # always leave some space at the end to avoid roudning issues with LVM's physical extent (PE)
+    # size of 4 MB.
+    my $space = $hdgb <= 32 ? 4 * 1024 : (($hdgb > 128 ? 16 : $hdgb / 8) * 1024 * 1024);
 
     my $rootsize;
-    my $datasize;
+    my $datasize = 0;
 
     if ($setup->{product} eq 'pve') {
 
-       my $maxroot;
+       my $maxroot_mb;
        if ($config_options->{maxroot}) {
-           $maxroot = $config_options->{maxroot};
+           $maxroot_mb = $config_options->{maxroot} * 1024;
        } else {
-           $maxroot = 96;
+           $maxroot_mb = 96 * 1024;
        }
 
-       $rootsize = (($hdgb > ($maxroot*4)) ? $maxroot : $hdgb/4)*1024*1024;
-
-       my $rest = $os_size - $swap_size - $rootsize; # in KB
+       my $rest = $os_size - $swap_size;
+       my $rest_mb = int($rest / 1024);
 
-       my $minfree;
-       if (defined($config_options->{minfree})) {
-           $minfree = (($config_options->{minfree}*1024*1024) >= $rest ) ? $space :
-               $config_options->{minfree}*1024*1024 ;
+       my $rootsize_mb;
+       if ($rest_mb < 12 * 1024) {
+           # no point in wasting space, try to get us actually installed and align down to 4 MB
+           $rootsize_mb = ($rest_mb - 0.1) & ~3;
+       } elsif ($rest_mb < 48 * 1024) {
+           my $masked = int($rest_mb / 2) & ~3; # align down to 4 MB
+           $rootsize_mb = $masked;
        } else {
-           $minfree = $space;
+           $rootsize_mb = $rest_mb / 4 + 12 * 1024;
        }
 
-       $rest = $rest - $minfree;
+       $rootsize_mb = $maxroot_mb if $rootsize_mb > $maxroot_mb;
+       $rootsize = int($rootsize_mb * 1024);
 
-       if (defined($config_options->{maxvz})) {
-           $rest = (($config_options->{maxvz}*1024*1024) <= $rest) ?
-               $config_options->{maxvz}*1024*1024 : $rest;
+       $rest -= $rootsize; # in KB
+
+       my $minfree = $space;
+       if (defined(my $cfg_minfree = $config_options->{minfree})) {
+           $minfree = $cfg_minfree * 1024 * 1024 >= $rest ? $space : $cfg_minfree * 1024 * 1024;
+       }
+
+       $rest = int($rest - $minfree) & ~0xFFF; # align down to 4 MB boundaries
+
+       if (defined(my $maxvz = $config_options->{maxvz})) {
+           $rest = $maxvz * 1024 * 1024 <= $rest ? $maxvz * 1024 * 1024 : $rest;
        }
 
        $datasize = $rest;
 
     } else {
        my $minfree = defined($config_options->{minfree}) ? $config_options->{minfree}*1024*1024 : $space;
-       $rootsize = $os_size - $minfree - $swap_size; # in KB
+       $rootsize = int($os_size - $minfree - $swap_size); # in KB
+       $rootsize &= ~0xFFF; # align down to 4 MB boundaries
     }
 
     if ($swap_size) {
-       syscmd("/sbin/lvcreate -L${swap_size}K -nswap $vgname") == 0 ||
+       syscmd("/sbin/lvcreate -Wy --yes -L${swap_size}K -nswap $vgname") == 0 ||
            die "unable to create swap volume\n";
 
        $swapfile = "/dev/$vgname/swap";
     }
 
-    syscmd("/sbin/lvcreate -L${rootsize}K -nroot $vgname") == 0 ||
+    syscmd("/sbin/lvcreate -Wy --yes -L${rootsize}K -nroot $vgname") == 0 ||
        die "unable to create root volume\n";
 
-    if ($datasize > 4*1024*1024) {
+    if ($datasize > 4 * 1024 * 1024) {
        my $metadatasize = $datasize/100; # default 1% of data
        $metadatasize = 1024*1024 if $metadatasize < 1024*1024; # but at least 1G
        $metadatasize = 16*1024*1024 if $metadatasize > 16*1024*1024; # but at most 16G
 
        # otherwise the metadata is taken out of $minfree
-       $datasize -= 2*$metadatasize;
+       $datasize -= 2 * $metadatasize;
 
        # 1 4MB PE to allow for rounding
-       $datasize -= 4*1024;
+       $datasize -= 4 * 1024;
 
-       syscmd("/sbin/lvcreate -L${datasize}K -ndata $vgname") == 0 ||
+       syscmd("/sbin/lvcreate -Wy --yes -L${datasize}K -ndata $vgname") == 0 ||
            die "unable to create data volume\n";
 
        syscmd("/sbin/lvconvert --yes --type thin-pool --poolmetadatasize ${metadatasize}K $vgname/data") == 0 ||
            die "unable to create data thin-pool\n";
     } else {
+       if  ($setup->{product} eq 'pve' && !defined($config_options->{maxvz})) {
+           display_message("Skipping auto-creation of LVM thinpool for guest data due to low space.");
+       }
        $datadev = undef;
     }
 
@@ -1169,18 +1204,21 @@ sub compute_swapsize {
 
     my $hdgb = int($hdsize/(1024*1024));
 
-    my $swapsize;
+    my $swapsize_kb;
     if (defined($config_options->{swapsize})) {
-       $swapsize = $config_options->{swapsize}*1024*1024;
+       $swapsize_kb = $config_options->{swapsize} * 1024 * 1024;
     } else {
-       my $ss = int ($total_memory / 1024);
-       $ss = 4 if $ss < 4;
-       $ss = ($hdgb/8) if $ss > ($hdgb/8);
-       $ss = 8 if $ss > 8;
-       $swapsize = $ss*1024*1024;
+       my $ss = int($total_memory);
+       $ss = 4096 if $ss < 4096 && $hdgb >= 64;
+       $ss = 2048 if $ss < 2048 && $hdgb >= 32;
+       $ss = 1024 if $ss >= 2048 && $hdgb <= 16;
+       $ss = 512 if $ss < 512;
+       $ss = int($hdgb * 128) if $ss > $hdgb * 128;
+       $ss = 8192 if $ss > 8192;
+       $swapsize_kb = int($ss * 1024) & ~0xFFF; # align to 4 MB to avoid all to odd SWAP size
     }
 
-    return $swapsize;
+    return $swapsize_kb;
 }
 
 my sub chroot_chown {
@@ -1206,11 +1244,11 @@ my sub chroot_chmod {
        die "chroot: unable to change permission mode for '$path'\n";
 }
 
-sub prepare_systemd_boot_esp {
+sub prepare_proxmox_boot_esp {
     my ($espdev, $targetdir) = @_;
 
-    syscmd("chroot $targetdir pve-efiboot-tool init $espdev") == 0 ||
-       die "unable to init ESP and install systemd-boot loader on '$espdev'\n";
+    syscmd("chroot $targetdir proxmox-boot-tool init $espdev") == 0 ||
+       die "unable to init ESP and install proxmox-boot loader on '$espdev'\n";
 }
 
 sub prepare_grub_efi_boot_esp {
@@ -1285,11 +1323,9 @@ sub extract_data {
     my $bootloader_err;
 
     eval {
-
-
        my $maxper = 0.25;
 
-       update_progress(0, 0, $maxper, "create partitions");
+       update_progress(0, 0, $maxper, "cleanup root-disks");
 
        syscmd("vgchange -an") if !$opt_testmode; # deactivate all detected VGs
 
@@ -1322,15 +1358,21 @@ sub extract_data {
        } elsif ($use_btrfs) {
 
            my ($devlist, $btrfs_mode) = get_btrfs_raid_setup();
+
+           foreach my $hd (@$devlist) {
+               $clean_disk->(@$hd[1]);
+           }
+
+           update_progress(0, 0.02, $maxper, "create partitions");
+
            my $btrfs_partitions = [];
            my $disksize;
            foreach my $hd (@$devlist) {
                my $devname = @$hd[1];
                my $logical_bsize = @$hd[4];
 
-               &$clean_disk($devname);
                my ($size, $osdev, $efidev) =
-                   partition_bootable_disk($devname, undef, '8300');
+                   partition_bootable_disk($devname, $config_options->{hdsize}, '8300');
                $rootdev = $osdev if !defined($rootdev); # simply point to first disk
                my $by_id = find_stable_path("/dev/disk/by-id", $devname);
                push @$bootdevinfo, {
@@ -1344,28 +1386,31 @@ sub extract_data {
                $disksize = $size;
            }
 
-           &$udevadm_trigger_block();
+           $udevadm_trigger_block->();
+
+           update_progress(0, 0.03, $maxper, "create btrfs");
 
            btrfs_create($btrfs_partitions, $btrfs_mode);
 
        } elsif ($use_zfs) {
 
-           my ($devlist, $bootdevlist, $vdev) = get_zfs_raid_setup();
+           my ($devlist, $vdev) = get_zfs_raid_setup();
 
            foreach my $hd (@$devlist) {
-               &$clean_disk(@$hd[1]);
+               $clean_disk->(@$hd[1]);
            }
 
+           update_progress(0, 0.02, $maxper, "create partitions");
+
+           # install esp/boot part on all, we can only win!
            my $disksize;
-           foreach my $hd (@$bootdevlist) {
+           for my $hd (@$devlist) {
                my $devname = @$hd[1];
                my $logical_bsize = @$hd[4];
 
                my ($size, $osdev, $efidev) =
                    partition_bootable_disk($devname, $config_options->{hdsize}, 'BF01');
 
-               zfs_mirror_size_check($disksize, $size) if $disksize;
-
                push @$bootdevinfo, {
                    esp => $efidev,
                    devname => $devname,
@@ -1375,7 +1420,7 @@ sub extract_data {
                $disksize = $size;
            }
 
-           &$udevadm_trigger_block();
+           $udevadm_trigger_block->();
 
            foreach my $di (@$bootdevinfo) {
                my $devname = $di->{devname};
@@ -1393,13 +1438,17 @@ sub extract_data {
                $vdev =~ s/ $devname/ $by_id/ if $by_id;
            }
 
+           update_progress(0, 0.03, $maxper, "create rpool");
+
            zfs_create_rpool($vdev);
 
        } else {
 
            die "target '$target_hd' is not a valid block device\n" if ! -b $target_hd;
 
-           &$clean_disk($target_hd);
+           $clean_disk->($target_hd);
+
+           update_progress(0, 0.02, $maxper, "create partitions");
 
            my $logical_bsize = logical_blocksize($target_hd);
 
@@ -1418,6 +1467,8 @@ sub extract_data {
                logical_bsize => $logical_bsize,
            };
 
+           update_progress(0, 0.03, $maxper, "create LVs");
+
            my $swap_size = compute_swapsize($os_size);
            ($rootdev, $swapfile, $datadev) =
                create_lvm_volumes($osdev, $os_size, $swap_size);
@@ -1432,13 +1483,13 @@ sub extract_data {
                die "unable to set zfs properties\n";
        }
 
-       update_progress(0.03, 0, $maxper, "create swap space");
+       update_progress(0.04, 0, $maxper, "create swap space");
        if ($swapfile) {
            syscmd("mkswap -f $swapfile") == 0 ||
                die "unable to create swap space\n";
        }
 
-       update_progress(0.05, 0, $maxper, "creating filesystems");
+       update_progress(0.045, 0, $maxper, "creating root filesystems");
 
        foreach my $di (@$bootdevinfo) {
            next if !$di->{esp};
@@ -1513,12 +1564,13 @@ sub extract_data {
            }
        });
 
-       syscmd("mount -n -t tmpfs tmpfs $targetdir/tmp") == 0 ||
-           die "unable to mount tmpfs on $targetdir/tmp\n";
-       syscmd("mount -n -t proc proc $targetdir/proc") == 0 ||
-           die "unable to mount proc on $targetdir/proc\n";
-       syscmd("mount -n -t sysfs sysfs $targetdir/sys") == 0 ||
-           die "unable to mount sysfs on $targetdir/sys\n";
+       syscmd("mount -n -t tmpfs tmpfs $targetdir/tmp") == 0 || die "unable to mount tmpfs on $targetdir/tmp\n";
+
+       mkdir "$targetdir/tmp/pkg";
+       syscmd("mount -n --bind '$proxmox_pkgdir' '$targetdir/tmp/pkg'") == 0
+           || die "unable to bind-mount packages on $targetdir/tmp/pkg\n";
+       syscmd("mount -n -t proc proc $targetdir/proc") == 0 || die "unable to mount proc on $targetdir/proc\n";
+       syscmd("mount -n -t sysfs sysfs $targetdir/sys") == 0 || die "unable to mount sysfs on $targetdir/sys\n";
        if ($boot_type eq 'efi') {
            syscmd("mount -n -t efivarfs efivarfs $targetdir/sys/firmware/efi/efivars") == 0 ||
                die "unable to mount efivarfs on $targetdir/sys/firmware/efi/efivars: $!\n";
@@ -1562,9 +1614,9 @@ sub extract_data {
                "\nauto vmbr0\niface vmbr0 $ntype static\n" .
                "\taddress $cidr\n" .
                "\tgateway $gateway\n" .
-               "\tbridge_ports $ethdev\n" .
-               "\tbridge_stp off\n" .
-               "\tbridge_fd 0\n";
+               "\tbridge-ports $ethdev\n" .
+               "\tbridge-stp off\n" .
+               "\tbridge-fd 0\n";
        } else {
            $ifaces .= "auto $ethdev\n" .
                "iface $ethdev $ntype static\n" .
@@ -1691,10 +1743,8 @@ _EOD
            my ($deb) = $path =~ m/${proxmox_pkgdir}\/(.*\.deb)/;
            update_progress($count/$pkg_count, 0.5, 0.75, "extracting $deb");
            print "extracting: $deb\n";
-           syscmd("cp $path $targetdir/tmp/$deb") == 0 ||
-               die "installation of package $deb failed\n";
-           syscmd("chroot $targetdir dpkg $dpkg_opts --force-depends --no-triggers --unpack /tmp/$deb") == 0 ||
-               die "installation of package $deb failed\n";
+           syscmd("chroot $targetdir dpkg $dpkg_opts --force-depends --no-triggers --unpack /tmp/pkg/$deb") == 0
+               || die "installation of package $deb failed\n";
            update_progress((++$count)/$pkg_count, 0.5, 0.75);
        }
 
@@ -1706,8 +1756,7 @@ _EOD
        run_command($cmd, sub {
            my $line = shift;
            if ($line =~ m/Setting up\s+(\S+)/) {
-               update_progress((++$count)/$pkg_count, 0.75, 0.95,
-                               "configuring $1");
+               update_progress((++$count)/$pkg_count, 0.75, 0.95, "configuring $1");
            }
        });
 
@@ -1722,9 +1771,6 @@ _EOD
        # create /etc/aliases.db (/etc/aliases is shipped in the base squashfs)
        syscmd("chroot $targetdir /usr/bin/newaliases");
 
-       # enable NTP (timedatectl set-ntp true  does not work without DBUS)
-       syscmd("chroot $targetdir /bin/systemctl enable systemd-timesyncd.service");
-
        unlink  "$targetdir/proxmox_install_mode";
 
        # set timezone
@@ -1765,10 +1811,11 @@ _EOD
        update_progress(0.8, 0.95, 1, "make system bootable");
 
        if ($use_zfs) {
-           syscmd("sed -i -e 's/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX=\"root=ZFS=$zfspoolname\\/ROOT\\/$zfsrootvolname boot=zfs\"/' $targetdir/etc/default/grub") == 0 ||
-               die "unable to update /etc/default/grub\n";
+           # add ZFS options while preserving existing kernel cmdline
+           my $zfs_snippet = "GRUB_CMDLINE_LINUX=\"\$GRUB_CMDLINE_LINUX root=ZFS=$zfspoolname/ROOT/$zfsrootvolname boot=zfs\"";
+           write_config($zfs_snippet, "$targetdir/etc/default/grub.d/zfs.cfg");
 
-           write_config("root=ZFS=$zfspoolname/ROOT/$zfsrootvolname boot=zfs", "$targetdir/etc/kernel/cmdline");
+           write_config("root=ZFS=$zfspoolname/ROOT/$zfsrootvolname boot=zfs\n", "$targetdir/etc/kernel/cmdline");
 
        }
 
@@ -1802,19 +1849,19 @@ _EOD
 
                foreach my $di (@$bootdevinfo) {
                    my $dev = $di->{devname};
-                   if (!$native_4k_disk_bootable) {
-                       eval {
-                           syscmd("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
-                                   die "unable to install the i386-pc boot loader on '$dev'\n";
-                       };
-                       push @$bootloader_err_list, $@ if $@;
-                   }
+                   if ($use_zfs) {
+                       prepare_proxmox_boot_esp($di->{esp}, $targetdir);
+                   } else {
+                       if (!$native_4k_disk_bootable) {
+                           eval {
+                               syscmd("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
+                                       die "unable to install the i386-pc boot loader on '$dev'\n";
+                           };
+                           push @$bootloader_err_list, $@ if $@;
+                       }
 
                    eval {
                        if (my $esp = $di->{esp}) {
-                           if ($use_zfs) {
-                               prepare_systemd_boot_esp($esp, $targetdir);
-                           } else {
                                prepare_grub_efi_boot_esp($dev, $esp, $targetdir);
                            }
                        }
@@ -1911,10 +1958,12 @@ _EOD
 
     syscmd("umount $targetdir/run");
     syscmd("umount $targetdir/mnt/hostrun");
+    syscmd("umount $targetdir/tmp/pkg");
     syscmd("umount $targetdir/tmp");
     syscmd("umount $targetdir/proc");
     syscmd("umount $targetdir/sys/firmware/efi/efivars");
     syscmd("umount $targetdir/sys");
+    rmdir("$targetdir/mnt/hostrun");
 
     if ($use_zfs) {
        syscmd("zfs umount -a") == 0 ||
@@ -1930,8 +1979,8 @@ _EOD
        syscmd("zfs set mountpoint=/ $zfspoolname/ROOT/$zfsrootvolname") == 0 ||
            die "zfs set mountpoint failed\n";
 
-       syscmd("zpool set bootfs=$zfspoolname/ROOT/$zfsrootvolname $zfspoolname")  == 0 ||
-           die "zfs set bootfs failed\n";
+       syscmd("zpool set bootfs=$zfspoolname/ROOT/$zfsrootvolname $zfspoolname") == 0 ||
+           die "zpool set bootfs failed\n";
        syscmd("zpool export $zfspoolname");
     }
 
@@ -1973,14 +2022,16 @@ sub display_html {
 
     $filename = $steps[$step_number]->{html} if !$filename;
 
-    my $path = "${proxmox_libdir}/html/$filename";
-
-    my $url = "file://$path";
+    my $htmldir = "${proxmox_libdir}/html";
+    my $path;
+    if (-f "$htmldir/$setup->{product}/$filename") {
+       $path = "$htmldir/$setup->{product}/$filename";
+    } else {
+       $path = "$htmldir/$filename";
+    }
 
     my $data = file_get_contents($path);
 
-    my $product = $product_cfg->{$setup->{product}};
-
     if ($filename eq 'license.htm') {
        my $license = eval { decode('utf8', file_get_contents("${proxmox_cddir}/EULA")) };
        if (my $err = $@) {
@@ -1993,16 +2044,17 @@ sub display_html {
     } elsif ($filename eq 'success.htm') {
        my $addr = $ipversion == 6 ? "[${ipaddress}]" : "$ipaddress";
        $data =~ s/__IPADDR__/$addr/g;
-       $data =~ s/__PORT__/$product->{port}/g;
+       $data =~ s/__PORT__/$setup->{port}/g;
 
        my $autoreboot_msg = $config_options->{autoreboot}
            ? "Automatic reboot scheduled in $autoreboot_seconds seconds."
            : '';
        $data =~ s/__AUTOREBOOT_MSG__/$autoreboot_msg/;
     }
-    $data =~ s/__FULL_PRODUCT_NAME__/$product->{fullname}/g;
+    $data =~ s/__FULL_PRODUCT_NAME__/$setup->{fullname}/g;
 
-    $htmlview->load_html($data, $url);
+    # always set base-path to common path, all resources are accesible from there.
+    $htmlview->load_html($data,  "file://$htmldir/");
 
     $last_display_change = time();
 }
@@ -2256,8 +2308,7 @@ sub get_ip_config {
 sub display_message {
     my ($msg) = @_;
 
-    my $dialog = Gtk3::MessageDialog->new($window, 'modal',
-                                         'info', 'ok', $msg);
+    my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'info', 'ok', $msg);
     $dialog->run();
     $dialog->destroy();
 }
@@ -2265,12 +2316,21 @@ sub display_message {
 sub display_error {
     my ($msg) = @_;
 
-    my $dialog = Gtk3::MessageDialog->new($window, 'modal',
-                                         'error', 'ok', $msg);
+    my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'error', 'ok', $msg);
     $dialog->run();
     $dialog->destroy();
 }
 
+sub display_prompt {
+    my ($query) = @_;
+
+    my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'question', 'ok-cancel', $query);
+    my $response = $dialog->run();
+    $dialog->destroy();
+
+    return $response;
+}
+
 my $ipconf_first_view = 1;
 
 sub create_ipconf_view {
@@ -2480,8 +2540,6 @@ sub create_ack_view {
 
     my $vbox =  Gtk3::VBox->new(0, 0);
     $inbox->pack_start($vbox, 1, 0, 0);
-    #my $hbox =  Gtk3::HBox->new(0, 0);
-    #$vbox->pack_start($hbox, 0, 0, 10);
 
     my $reboot_checkbox = Gtk3::CheckButton->new('Automatically reboot after successful installation');
     $reboot_checkbox->set_active(1);
@@ -2492,7 +2550,7 @@ sub create_ack_view {
     $vbox->pack_start($reboot_checkbox, 0, 0, 2);
 
     my $ack_template = "${proxmox_libdir}/html/ack_template.htm";
-    my $ack_html = "${proxmox_libdir}/html/$steps[$step_number]->{html}";
+    my $ack_html = "${proxmox_libdir}/html/$setup->{product}/$steps[$step_number]->{html}";
     my $html_data = file_get_contents($ack_template);
 
     my %config_values = (
@@ -2511,7 +2569,7 @@ sub create_ack_view {
        __dnsserver__ => $dnsserver,
     );
 
-    while ( my ($k, $v) = each %config_values) {
+    while (my ($k, $v) = each %config_values) {
        $html_data =~ s/$k/$v/g;
     }
 
@@ -2531,24 +2589,32 @@ sub get_device_desc {
     my ($devname, $size, $model) = @_;
 
     if ($size && ($size > 0)) {
-       $size = int($size/2048); # size in MB, from 512B "sectors"
+       $size = int($size/2048); # size in MiB, from 512B "sectors"
 
        my $text = "$devname (";
        if ($size >= 1024) {
-           $size = int($size/1024); # size in GB
-           $text .= "${size}GB";
+           $size = $size/1024; # size in GiB
+           if ($size >= 1024) {
+               $size = $size/1024; # size in TiB
+               $text .= sprintf("%.2f", $size) . "TiB";
+           } else {
+               $text .= sprintf("%.2f", $size) . "GiB";
+           }
        } else {
-           $text .= "${size}MB";
+           $text .= "${size}MiB";
        }
 
        $text .= ", $model" if $model;
        $text .= ")";
+       return $text;
 
     } else {
        return $devname;
     }
 }
 
+my $last_layout;
+my $country_layout;
 sub update_layout {
     my ($cb, $kmap) = @_;
 
@@ -2562,7 +2628,14 @@ sub update_layout {
        $i++;
     }
 
-    $cb->set_active($ind || $def || 0);
+    my $val = $ind || $def || 0;
+
+    if (!defined($kmap)) {
+       $last_layout //= $val;
+    } elsif (!defined($country_layout) || $country_layout != $val) {
+       $last_layout = $country_layout = $val;
+    }
+    $cb->set_active($last_layout);
 }
 
 my $lastzonecb;
@@ -2639,7 +2712,7 @@ sub create_password_view {
     $hbox2->pack_start($pwe2, 0, 0, 0);
 
     my $hbox3 = Gtk3::HBox->new(0, 0);
-    $label = Gtk3::Label->new("E-Mail");
+    $label = Gtk3::Label->new("Email");
     $label->set_size_request(150, -1);
     $label->set_alignment(1, 0.5);
     $hbox3->pack_start($label, 0, 0, 10);
@@ -2676,14 +2749,14 @@ sub create_password_view {
 
        my $t3 = $eme->get_text;
        if ($t3 !~ m/^[\w\+\-\~]+(\.[\w\+\-\~]+)*@[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*$/) {
-           display_message("E-Mail does not look like a valid address" .
+           display_message("Email does not look like a valid address" .
                             " (user\@domain.tld)");
            $eme->grab_focus();
            return;
        }
 
        if ($t3 eq 'mail@example.invalid') {
-           display_message("Please enter a valid E-Mail address");
+           display_message("Please enter a valid Email address");
            $eme->grab_focus();
            return;
        }
@@ -2699,6 +2772,7 @@ sub create_password_view {
 
 }
 
+my $installer_kmap;
 sub create_country_view {
 
     cleanup_view();
@@ -2744,21 +2818,30 @@ sub create_country_view {
 
     $kmapcb->signal_connect ('changed' => sub {
        my $sel = $kmapcb->get_active_text();
+       $last_layout = $kmapcb->get_active();
        if (my $kmap = $cmap->{kmaphash}->{$sel}) {
            my $xkmap = $cmap->{kmap}->{$kmap}->{x11};
            my $xvar = $cmap->{kmap}->{$kmap}->{x11var};
            $keymap = $kmap;
 
+           return if (defined($installer_kmap) && $installer_kmap eq $kmap);
+
+           $installer_kmap = $keymap;
+
            if (! $opt_testmode) {
                syscmd ("setxkbmap $xkmap $xvar");
+
                my $kbd_config = qq{
                    XKBLAYOUT="$xkmap"
                    XKBVARIANT="$xvar"
                    BACKSPACE="guess"
                };
                $kbd_config =~ s/^\s+//gm;
-               write_config($kbd_config, '/etc/default/keyboard');
-               syscmd ("setupcon");
+
+               run_in_background( sub {
+                   write_config($kbd_config, '/etc/default/keyboard');
+                   system("setupcon");
+               });
            }
        }
     });
@@ -2878,8 +2961,8 @@ my $create_basic_grid = sub {
     $grid->set_row_spacing(10);
     $grid->set_hexpand(1);
 
-    $grid->set_margin_start(5);
-    $grid->set_margin_end(5);
+    $grid->set_margin_start(10);
+    $grid->set_margin_end(20);
     $grid->set_margin_top(5);
     $grid->set_margin_bottom(5);
 
@@ -2906,25 +2989,83 @@ my $create_label_widget_grid = sub {
     return $grid;
 };
 
+# only relevant for raid with its multipl diskX to diskY mappings.
+my $get_selected_hdsize = sub {
+    my $hdsize = shift;
+    return $hdsize if defined($hdsize);
+
+    # compute the smallest disk size of the actually selected disks
+    my $hd_count = scalar(@$hds);
+    for (my $i = 0; $i < $hd_count; $i++) {
+       my $cur_hd = $config_options->{"disksel$i"} // next;
+       my $disksize = int(@$cur_hd[2] / (2 * 1024 * 1024.0)); # size in GB
+       $hdsize //= $disksize;
+       $hdsize = $disksize if $disksize < $hdsize;
+    }
+
+    if (my $cfg_hdsize = $config_options->{hdsize}) {
+       # had the dialog open previously and set an even lower size than the disk selection allows
+       $hdsize = $cfg_hdsize if $cfg_hdsize < $hdsize;
+    }
+    return $hdsize // 0; # fall back to zero, e.g., if none is selected hdsize cannot be any size
+};
+
+my sub update_hdsize_adjustment {
+    my ($adjustment, $hdsize) = @_;
+
+    $hdsize = $get_selected_hdsize->($hdsize);
+    # expect that lower = 0 and step increments = 1 still are valid
+    $adjustment->set_upper($hdsize + 1);
+    $adjustment->set_value($hdsize);
+}
+
+my sub create_hdsize_adjustment {
+    my ($hdsize) = @_;
+    $hdsize = $get_selected_hdsize->($hdsize);
+    # params are: initial value, lower, upper, step increment, page increment, page size
+    return Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
+}
+
+my sub get_hdsize_spin_button {
+    my $hdsize = shift;
+
+    my $hdsize_entry_buffer = Gtk3::EntryBuffer->new(undef, 1);
+    my $hdsize_size_adj = create_hdsize_adjustment($hdsize);
+
+    my $spinbutton_hdsize = Gtk3::SpinButton->new($hdsize_size_adj, 1, 1);
+    $spinbutton_hdsize->set_buffer($hdsize_entry_buffer);
+    $spinbutton_hdsize->set_adjustment($hdsize_size_adj);
+    $spinbutton_hdsize->set_tooltip_text("only use specified size (GB) of the harddisk (rest left unpartitioned)");
+    return $spinbutton_hdsize;
+};
+
 my $create_raid_disk_grid = sub {
+    my ($hdsize_buttons) = @_;
+
+    my $hd_count = scalar(@$hds);
     my $disk_labeled_widgets = [];
-    for (my $i = 0; $i < @$hds; $i++) {
+    for (my $i = 0; $i < $hd_count; $i++) {
        my $disk_selector = Gtk3::ComboBoxText->new();
        $disk_selector->append_text("-- do not use --");
        $disk_selector->set_active(0);
        $disk_selector->set_visible(1);
+
        foreach my $hd (@$hds) {
            my ($disk, $devname, $size, $model, $logical_bsize) = @$hd;
            $disk_selector->append_text(get_device_desc ($devname, $size, $model));
-           $disk_selector->{pve_disk_id} = $i;
-           $disk_selector->signal_connect (changed => sub {
-               my $w = shift;
-               my $diskid = $w->{pve_disk_id};
-               my $a = $w->get_active - 1;
-               $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
-           });
        }
 
+       $disk_selector->{pve_disk_id} = $i;
+       $disk_selector->signal_connect(changed => sub {
+           my $w = shift;
+           my $diskid = $w->{pve_disk_id};
+           my $a = $w->get_active - 1;
+           $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
+           for my $btn (@$hdsize_buttons) {
+               update_hdsize_adjustment($btn->get_adjustment());
+           }
+       });
+
        if ($hdoption_first_setup) {
            $disk_selector->set_active ($i+1) if $hds->[$i];
        } else {
@@ -2943,42 +3084,44 @@ my $create_raid_disk_grid = sub {
        push @$disk_labeled_widgets, "Harddisk $i", $disk_selector;
     }
 
+    my $clear_all_button = Gtk3::Button->new('_Deselect All');
+    if ($hd_count > 3) {
+       $clear_all_button->signal_connect('clicked', sub {
+           my $is_widget = 0;
+           for my $disk_selector (@$disk_labeled_widgets) {
+               $disk_selector->set_active(0) if $is_widget;
+               $is_widget ^= 1;
+           }
+       });
+       $clear_all_button->set_visible(1);
+    }
+
     my $scrolled_window = Gtk3::ScrolledWindow->new();
     $scrolled_window->set_hexpand(1);
-    $scrolled_window->set_propagate_natural_height(1) if @$hds > 4;
-    $scrolled_window->add(&$create_label_widget_grid($disk_labeled_widgets));
-    $scrolled_window->set_policy('never', 'automatic');
-
-    return $scrolled_window;
-#    &$create_label_widget_grid($disk_labeled_widgets)
-};
+    $scrolled_window->set_propagate_natural_height(1) if $hd_count > 4;
 
-# shared between different ui parts (e.g., ZFS and "normal" single disk FS)
-my $hdsize_size_adj;
-my $hdsize_entry_buffer;
+    my $diskgrid = $create_label_widget_grid->($disk_labeled_widgets);
 
-my $get_hdsize_spinbtn = sub {
-    my $hdsize = shift;
+    $scrolled_window->add($diskgrid);
+    $scrolled_window->set_policy('never', 'automatic');
+    $scrolled_window->set_visible(1);
+    $scrolled_window->set_min_content_height(190);
 
-    $hdsize_entry_buffer //= Gtk3::EntryBuffer->new(undef, 1);
+    my $vbox = Gtk3::Box->new('vertical', 0);
+    $vbox->pack_start($scrolled_window, 1, 1, 10);
 
-    if (defined($hdsize)) {
-       $hdsize_size_adj = Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
-    } else {
-       die "called get_hdsize_spinbtn with \$hdsize_size_adj not defined but did not pass hdsize!\n"
-           if !defined($hdsize_size_adj);
-    }
+    my $hbox = Gtk3::Box->new('horizontal', 0);
+    $hbox->pack_end($clear_all_button, 0, 0, 20);
+    $hbox->set_visible(1);
+    $vbox->pack_end($hbox, 0, 0, 0);
 
-    my $spinbutton_hdsize = Gtk3::SpinButton->new($hdsize_size_adj, 1, 1);
-    $spinbutton_hdsize->set_buffer($hdsize_entry_buffer);
-    $spinbutton_hdsize->set_adjustment($hdsize_size_adj);
-    $spinbutton_hdsize->set_tooltip_text("only use specified size (GB) of the harddisk (rest left unpartitioned)");
-    return $spinbutton_hdsize;
+    return $vbox;
 };
 
 my $create_raid_advanced_grid = sub {
+    my ($hdsize_btn) = @_;
     my $labeled_widgets = [];
-    my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9,13,1);
+    my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9, 13, 1);
     $spinbutton_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
     $spinbutton_ashift->signal_connect ("value-changed" => sub {
        my $w = shift;
@@ -2991,8 +3134,7 @@ my $create_raid_advanced_grid = sub {
 
     my $combo_compress = Gtk3::ComboBoxText->new();
     $combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset");
-    # note: gzip / lze not allowed for bootfs vdevs
-    my $comp_opts = ["on","off","lzjb","lz4"];
+    my $comp_opts = ["on","off","lzjb","lz4", "zle", "gzip", "zstd"];
     foreach my $opt (@$comp_opts) {
        $combo_compress->append($opt, $opt);
     }
@@ -3030,12 +3172,18 @@ my $create_raid_advanced_grid = sub {
     $spinbutton_copies->set_value($config_options->{copies});
     push @$labeled_widgets, "copies", $spinbutton_copies;
 
-    push @$labeled_widgets, "hdsize", $get_hdsize_spinbtn->();
-    return &$create_label_widget_grid($labeled_widgets);;
+    push @$labeled_widgets, "hdsize", $hdsize_btn;
+    return $create_label_widget_grid->($labeled_widgets);;
 };
 
-sub create_hdoption_view {
+my $create_btrfs_raid_advanced_grid = sub {
+    my ($hdsize_btn) = @_;
+    my $labeled_widgets = [];
+    push @$labeled_widgets, "hdsize", $hdsize_btn;
+    return $create_label_widget_grid->($labeled_widgets);;
+};
 
+sub create_hdoption_view {
     my $dialog = Gtk3::Dialog->new();
 
     $dialog->set_title("Harddisk options");
@@ -3045,37 +3193,39 @@ sub create_hdoption_view {
     my $contarea = $dialog->get_content_area();
 
     my $hbox2 =  Gtk3::Box->new('horizontal', 0);
-    $contarea->pack_start($hbox2, 1, 1, 10);
+    $contarea->pack_start($hbox2, 1, 1, 5);
 
     my $grid =  Gtk3::Grid->new();
     $grid->set_column_spacing(10);
     $grid->set_row_spacing(10);
 
-    $hbox2->pack_start($grid, 1, 0, 10);
+    $hbox2->pack_start($grid, 1, 0, 5);
 
     my $row = 0;
 
     # Filesystem type
-
     my $label0 = Gtk3::Label->new("Filesystem");
     $label0->set_alignment (1, 0.5);
     $grid->attach($label0, 0, $row, 1, 1);
 
     my $fstypecb = Gtk3::ComboBoxText->new();
-
-    my $fstype = ['ext4', 'xfs',
-                 'zfs (RAID0)', 'zfs (RAID1)',
-                 'zfs (RAID10)', 'zfs (RAIDZ-1)',
-                 'zfs (RAIDZ-2)', 'zfs (RAIDZ-3)'];
-
+    my $fstype = [
+       'ext4',
+       'xfs',
+       'zfs (RAID0)',
+       'zfs (RAID1)',
+       'zfs (RAID10)',
+       'zfs (RAIDZ-1)',
+       'zfs (RAIDZ-2)',
+       'zfs (RAIDZ-3)',
+    ];
     push @$fstype, 'btrfs (RAID0)', 'btrfs (RAID1)', 'btrfs (RAID10)'
        if $setup->{enable_btrfs};
 
     my $tcount = 0;
     foreach my $tmp (@$fstype) {
        $fstypecb->append_text($tmp);
-       $fstypecb->set_active ($tcount)
-           if $config_options->{filesys} eq $tmp;
+       $fstypecb->set_active ($tcount) if $config_options->{filesys} eq $tmp;
        $tcount++;
     }
 
@@ -3090,7 +3240,7 @@ sub create_hdoption_view {
     $grid->attach($sep, 0, $row, 2, 1);
     $row++;
 
-    my $hw_raid_note = Gtk3::Label->new("Note: ZFS is not compatible with disks backed by a hardware RAID controller. For details see the reference documentation.");
+    my $hw_raid_note = Gtk3::Label->new(""); # text will be set below, before making it visible
     $hw_raid_note->set_line_wrap(1);
     $hw_raid_note->set_max_width_chars(30);
     $hw_raid_note->set_visible(0);
@@ -3101,13 +3251,14 @@ sub create_hdoption_view {
     # size compute
     my $hdsize = 0;
     if ( -b $target_hd) {
-       $hdsize = int(hd_size ($target_hd) / (1024*1024.0)); # size in GB
+       $hdsize = int(hd_size ($target_hd) / (1024 * 1024.0)); # size in GB
     } elsif ($target_hd) {
-       $hdsize = int((-s $target_hd) / (1024*1024*1024.0));
+       $hdsize = int((-s $target_hd) / (1024 * 1024 * 1024.0));
     }
 
-    my $spinbutton_hdsize = $get_hdsize_spinbtn->($hdsize);
-    push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize;
+    my $spinbutton_hdsize_nonraid = get_hdsize_spin_button($hdsize);
+    push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize_nonraid;
+    my $spinbutton_hdsize = $spinbutton_hdsize_nonraid;
 
     my $entry_swapsize = Gtk3::Entry->new();
     $entry_swapsize->set_tooltip_text("maximum SWAP size (GB)");
@@ -3138,13 +3289,17 @@ sub create_hdoption_view {
        push @$hdsize_labeled_widgets, "maxvz", $entry_maxvz;
     }
 
+    my $spinbutton_hdsize_zfs = get_hdsize_spin_button($hdsize);
+    my $spinbutton_hdsize_btrfs = get_hdsize_spin_button($hdsize);
+    my $hdsize_buttons = [ $spinbutton_hdsize_zfs, $spinbutton_hdsize_btrfs ];
     my $options_stack = Gtk3::Stack->new();
     $options_stack->set_visible(1);
     $options_stack->set_hexpand(1);
     $options_stack->set_vexpand(1);
-    $options_stack->add_titled(&$create_raid_disk_grid(), "raiddisk", "Disk Setup");
+    $options_stack->add_titled(&$create_raid_disk_grid($hdsize_buttons), "raiddisk", "Disk Setup");
     $options_stack->add_titled(&$create_label_widget_grid($hdsize_labeled_widgets), "hdsize", "Size Options");
-    $options_stack->add_titled(&$create_raid_advanced_grid("zfs"), "raidzfsadvanced", "Advanced Options");
+    $options_stack->add_titled(&$create_raid_advanced_grid($spinbutton_hdsize_zfs), "raidzfsadvanced", "Advanced Options");
+    $options_stack->add_titled(&$create_btrfs_raid_advanced_grid($spinbutton_hdsize_btrfs), "raidbtrfsadvanced", "Advanced Options");
     $options_stack->set_visible_child_name("raiddisk");
     my $options_stack_switcher = Gtk3::StackSwitcher->new();
     $options_stack_switcher->set_halign('center');
@@ -3158,20 +3313,36 @@ sub create_hdoption_view {
 
     my $switch_view = sub {
        my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
-       my $enable_zfs_opts = $config_options->{filesys} =~ m/zfs/;
+       my $is_zfs = $config_options->{filesys} =~ m/zfs/;
 
        $target_hd_combo->set_visible(!$raid);
        $options_stack->get_child_by_name("hdsize")->set_visible(!$raid);
        $options_stack->get_child_by_name("raiddisk")->set_visible($raid);
+
+       if ($raid) {
+           my $msg = "<b>Note</b>: " . ($is_zfs
+               ? "ZFS is not compatible with hardware RAID controllers, for details see the documentation."
+               : "BTRFS integration in $setup->{fullname} is a technology preview!"
+           );
+           $hw_raid_note->set_markup($msg);
+       }
        $hw_raid_note->set_visible($raid);
-       $options_stack_switcher->set_visible($enable_zfs_opts);
-       $options_stack->get_child_by_name("raidzfsadvanced")->set_visible($enable_zfs_opts);
+       $options_stack_switcher->set_visible($raid);
+       $options_stack->get_child_by_name("raidzfsadvanced")->set_visible($is_zfs);
+       $options_stack->get_child_by_name("raidbtrfsadvanced")->set_visible(!$is_zfs);
        if ($raid) {
            $target_hd_label->set_text("Target: $config_options->{filesys} ");
            $options_stack->set_visible_child_name("raiddisk");
        } else {
            $target_hd_label->set_text("Target Harddisk: ");
        }
+
+       if ($raid) {
+           $spinbutton_hdsize = $is_zfs ? $spinbutton_hdsize_zfs : $spinbutton_hdsize_btrfs;
+       } else {
+           $spinbutton_hdsize = $spinbutton_hdsize_nonraid;
+       }
+
        my (undef, $pref_width) = $dialog->get_preferred_width();
        my (undef, $pref_height) = $dialog->get_preferred_height();
        $pref_height = 750 if $pref_height > 750;
@@ -3274,7 +3445,6 @@ sub legacy_bios_4k_check {
 }
 
 sub get_zfs_raid_setup {
-
     my $filesys = $config_options->{filesys};
 
     my $devlist = &$get_raid_devlist();
@@ -3282,11 +3452,8 @@ sub get_zfs_raid_setup {
     my $diskcount = scalar(@$devlist);
     die "$filesys needs at least one device\n" if $diskcount < 1;
 
-    my $bootdevlist = [];
-
     my $cmd= '';
     if ($filesys eq 'zfs (RAID0)') {
-       push @$bootdevlist, @$devlist[0];
        foreach my $hd (@$devlist) {
            legacy_bios_4k_check(@$hd[4]);
            $cmd .= " @$hd[1]";
@@ -3300,14 +3467,11 @@ sub get_zfs_raid_setup {
            zfs_mirror_size_check($expected_size, @$hd[2]);
            legacy_bios_4k_check(@$hd[4]);
            $cmd .= " @$hd[1]";
-           push @$bootdevlist, $hd;
        }
     } elsif ($filesys eq 'zfs (RAID10)') {
        die "zfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
        die "zfs (RAID10) needs an even number of devices\n" if $diskcount & 1;
 
-       push @$bootdevlist, @$devlist[0], @$devlist[1];
-
        for (my $i = 0; $i < $diskcount; $i+=2) {
            my $hd1 = @$devlist[$i];
            my $hd2 = @$devlist[$i+1];
@@ -3328,13 +3492,12 @@ sub get_zfs_raid_setup {
            zfs_mirror_size_check($expected_size, @$hd[2]);
            legacy_bios_4k_check(@$hd[4]);
            $cmd .= " @$hd[1]";
-           push @$bootdevlist, $hd;
        }
     } else {
        die "unknown zfs mode '$filesys'\n";
     }
 
-    return ($devlist, $bootdevlist, $cmd);
+    return ($devlist, $cmd);
 }
 
 sub get_btrfs_raid_setup {
@@ -3389,7 +3552,7 @@ sub create_hdsel_view {
 
     foreach my $hd (@$hds) {
        ($disk, $devname, $size, $model, $logical_bsize) = @$hd;
-       $target_hd_combo->append_text (get_device_desc($devname, $size, $model));
+       $target_hd_combo->append_text(get_device_desc($devname, $size, $model));
     }
 
     my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
@@ -3514,10 +3677,8 @@ sub create_intro_view {
     cleanup_view();
 
     if (int($total_memory) < 1024) {
-       my $product = $product_cfg->{$setup->{product}};
-
        display_error("Less than 1 GiB of usable memory detected, installation will probably fail.\n\n".
-           "See 'System Requirements' in the $product->{fullname} documentation.");
+           "See 'System Requirements' in the $setup->{fullname} documentation.");
     }
 
     if ($setup->{product} eq 'pve') {
@@ -3576,4 +3737,9 @@ create_intro_view () if !$initial_error;
 
 Gtk3->main;
 
+# reap left over zombie processes
+while ((my $child = waitpid(-1, POSIX::WNOHANG)) > 0) {
+    print "reaped child $child\n";
+}
+
 exit 0;