I was showing this to a few people at DebConf and people seemed generally in favour of the UI changes, so I thought I'd post it for wider comment here.
The process of setting up complex block devices (LVM, RAID, crypto) in partman is currently rather tedious. You have to separately edit each partition you want to use as a physical volume, change its "Use as:" field, and then a "Configure <foo>" menu item appears that lets you assemble the physical volumes. This is very cumbersome, and it also has discoverability problems - if your goal is to set up LVM, you can't even see the menu item that lets you do that until you're part-way through the process. I'd like to introduce an alternative way to do this that's more goal-oriented: in other words, I'd like people to be able to select "Configure the Logical Volume Manager", select from a menu of all the partitions that *could* be used as LVM PVs, and have partman-lvm automatically set them up for them. The attached set of three patches implements this for each of partman-lvm, partman-md, and partman-crypto. In the case of partman-crypto, I had to add a menu, which both brings it into line with LVM and RAID and will allow adding a way to deconfigure encrypted volumes in the future (#381892). The partman-md patch is rather complicated because I had to cope with the way it trims partitions already selected as active devices when prompting for spare devices; the code in this patch is the result of several attempts, and most of my previous attempts had much twistier uses of IFS that I eventually rejected. Talking through this with Max Vozeler identified several problems that I'd still like to fix: * There are several common chunks of code that should be moved into partman-base. * It'd be nice to show more detail in the partition menus, especially including a status field and the mountpoint. We can't quite reuse the standard partition tree as this relies on a separate line to identify the disk device, which doesn't really work here, but we could do something similar. * There's some weird bug in handling of locked devices; the LVM menu, at least, offers the underlying devices of configured encrypted volumes when it shouldn't. * Max suggests that we might exclude partitions with mountpoints to reduce the probability of mistakes. This seems to make sense - d-i doesn't automatically set up mountpoints, so if a partition has a mountpoint then it's because the user deliberately gave it one. If you'd like to try this out, then I guess a reasonable way to do so would be to play with a current Ubuntu installation image: http://cdimage.ubuntu.com/releases/karmic/alpha-3/ (alpha release produced today, CD-sized; use the "server" option) http://cdimage.ubuntu.com/ubuntu-server/daily/current/ (server daily build, may be arbitrarily broken) http://cdimage.ubuntu.com/netboot/karmic/ (netboot images, may be arbitrarily broken though usually not too bad) Thanks, -- Colin Watson [cjwat...@ubuntu.com]
=== modified file 'choose_method/lvm/choices' --- choose_method/lvm/choices 2007-12-05 20:18:24 +0000 +++ choose_method/lvm/choices 2009-07-13 21:40:14 +0000 @@ -1,45 +1,11 @@ #!/bin/sh -. /lib/partman/lib/base.sh +. /lib/partman/lib/lvm-base.sh dev=$1 id=$2 -cd $dev - -lvm=no -if grep -q "/dev/md" $dev/device; then - # LVM on software RAID - lvm=yes -elif grep -q "/dev/mapper/" $dev/device; then - # LVM on device-mapper crypto - if type dmsetup >/dev/null 2>&1; then - device=$(cat $dev/device) - if [ "$(dmsetup status $device | cut -d' ' -f3)" = crypt ]; then - lvm=yes - fi - fi -fi - -# sparc can not have LVM starting at 0 or it will destroy the partition table -if [ "$(udpkg --print-architecture)" = sparc ] && \ - [ "${id%%-*}" = 0 ] && [ $lvm = no ]; then - exit 0 -fi - -if [ $lvm = no ]; then - open_dialog VALID_FLAGS $id - while { read_line flag; [ "$flag" ]; }; do - if [ "$flag" = lvm ]; then - lvm=yes - fi - done - close_dialog -fi - -if [ $lvm = no ]; then - exit 0 -fi +pv_allowed "$dev" "$id" || exit 0 db_metaget partman/method_long/lvm description === modified file 'choose_partition/lvm/choices' --- choose_partition/lvm/choices 2007-12-05 20:18:57 +0000 +++ choose_partition/lvm/choices 2009-07-09 09:42:49 +0000 @@ -3,8 +3,9 @@ . /usr/share/debconf/confmodule . /lib/partman/lib/lvm-base.sh -# Only show menu option if there is at least one LVM PV -[ $(pv_list | wc -l) -gt 0 ] || exit 0 +# Only show menu option if there is at least one partition that can be used +# as an LVM PV +[ $(pv_list_allowed | wc -l) -gt 0 ] || exit 0 db_metaget partman-lvm/text/configure_lvm description === modified file 'choose_partition/lvm/do_option' --- choose_partition/lvm/do_option 2008-07-31 11:29:20 +0000 +++ choose_partition/lvm/do_option 2009-07-22 13:12:33 +0000 @@ -43,15 +43,32 @@ do_display() { } do_vg_create() { - local pvs pv output vg + local pvs line pv output vg pathmap pvs="" + pathmap="" # Look for free PVs - for pv in $(pv_list_free); do - pv_get_info "$pv" - output=$(printf "%-30s (%sMB)" "$pv" "$SIZE") + IFS="$NL" + for line in $(pv_list_allowed_free); do + restore_ifs + local dev="${line%%$TAB*}" + line="${line#*$TAB}" + local id="${line%%$TAB*}" + line="${line#*$TAB}" + local size="${line%%$TAB*}" + local path="${line#*$TAB}" + cd $dev + if ([ -e "$path" ] && pv_get_info "$path") || [ ! -s "$id/visual_filesystem" ]; then + output=$(printf "%-30s (%sMB)" "$path" "$SIZE") + else + local visual="$(cat "$id/visual_filesystem")" + output=$(printf "%-30s (%sMB; %s)" "$path" "$(convert_to_megabytes $size)" "$visual") + fi pvs="${pvs:+$pvs, }$output" + pathmap="${pathmap:+$pathmap$NL}$path$TAB$dev//$id" + IFS="$NL" done + restore_ifs if [ -z "$pvs" ]; then db_input critical partman-lvm/nopartitions db_go || true @@ -103,8 +120,33 @@ do_vg_create() { db_go || true return fi - pvs=$(echo "$RET" | sed -e 's/ *([^)]*) *//g') - pvs=$(csv_to_ssv "$pvs") + pvs=$(echo "$RET" | sed -e "s/ *([^)]*) *//g; s/ *, */\\$NL/g") + + local newpvs= + local need_commit= + IFS="$NL" + for pv in $pvs; do + for line in $pathmap; do + restore_ifs + if [ "${line%%$TAB*}" = "$pv" ]; then + local devid="${line#*$TAB}" + local path + if path="$(pv_prepare "${devid%//*}" "${devid#*//}")"; then + need_commit=true + fi + newpvs="${newpvs:+$newpvs }$path" + break + fi + IFS="$NL" + done + IFS="$NL" + done + restore_ifs + pvs="$newpvs" + + if [ "$need_commit" ]; then + do_initial_setup + fi if ! vg_create "$vg" $pvs; then db_subst partman-lvm/vgcreate_error VG "$vg" @@ -160,16 +202,35 @@ do_vg_delete() { } do_vg_extend() { - local pvs pv output vgs vg + local pvs line pv output vgs vg pathmap vgs="" + pathmap="" # Get eligible PVs pvs="" - for pv in $(pv_list_free); do - pv_get_info "$pv" - output=$(printf "%-30s (%sMB)" "$pv" "$SIZE") + IFS="$NL" + for line in $(pv_list_allowed_free); do + restore_ifs + local dev="${line%%$TAB*}" + line="${line#*$TAB}" + local id="${line%%$TAB*}" + line="${line#*$TAB}" + local size="${line%%$TAB*}" + local path="${line#*$TAB}" + cd $dev + if [ -e "$path" ] && pv_get_info "$path"; then + output=$(printf "%-30s (%sMB)" "$path" "$SIZE") + elif [ ! -f "$id/visual_filesystem" ]; then + continue + else + local visual="$(cat "$id/visual_filesystem")" + output=$(printf "%-30s (%sMB; %s)" "$path" "$(convert_to_megabytes $size)" "$visual") + fi pvs="${pvs:+$pvs, }$output" + pathmap="${pathmap:+$pathmap$NL}$path$TAB$dev//$id" + IFS="$NL" done + restore_ifs if [ -z "$pvs" ]; then db_input critical partman-lvm/nopartitions db_go || true @@ -208,8 +269,33 @@ do_vg_extend() { db_go || true return fi - pvs=$(echo "$RET" | sed -e 's/ *([^)]*) *//g') - pvs=$(csv_to_ssv "$pvs") + pvs=$(echo "$RET" | sed -e "s/ *([^)]*) *//g; s/ *, */\\$NL/g") + + local newpvs= + local need_commit= + IFS="$NL" + for pv in $pvs; do + for line in $pathmap; do + restore_ifs + if [ "${line%%$TAB*}" = "$pv" ]; then + local devid="${line#*$TAB}" + local path + if path="$(pv_prepare "${devid%//*}" "${devid#*//}")"; then + need_commit=true + fi + newpvs="${newpvs:+$newpvs }$path" + break + fi + IFS="$NL" + done + IFS="$NL" + done + restore_ifs + pvs="$newpvs" + + if [ "$need_commit" ]; then + do_initial_setup + fi for pv in $pvs; do if ! vg_extend "$vg" "$pv"; then @@ -440,6 +526,7 @@ do_initial_setup while [ 1 ]; do # Get some statistics + allowed_pvs=$(pv_list_allowed_free | wc -l) free_pvs=$(pv_list_free | wc -l) used_pvs=$(pvs --noheadings | wc -l) # All PVs used_pvs=$(( $used_pvs - $free_pvs )) # Used = All PVs - Free PVs @@ -450,7 +537,7 @@ while [ 1 ]; do choices="" choices_l10n="" lvm_menu_add display - if [ $free_pvs -gt 0 ]; then + if [ $allowed_pvs -gt 0 ]; then lvm_menu_add createvg fi # Other options only if there is at least one VG @@ -482,7 +569,7 @@ while [ 1 ]; do if [ $lvs -gt 0 ]; then lvm_menu_add deletelv fi - if [ $free_pvs -gt 0 ]; then + if [ $allowed_pvs -gt 0 ]; then lvm_menu_add extendvg fi if [ "$do_reducevg" ]; then === modified file 'lib/lvm-base.sh' --- lib/lvm-base.sh 2009-03-11 15:15:37 +0000 +++ lib/lvm-base.sh 2009-07-17 18:10:35 +0000 @@ -179,6 +179,124 @@ lvm_name_ok() { return 0 } +# Would a PV be allowed on this partition? +pv_allowed () { + local dev=$1 + local id=$2 + + cd $dev + + local lvm=no + if grep -q "/dev/md" $dev/device; then + # LVM on software RAID + lvm=yes + elif grep -q "/dev/mapper/" $dev/device; then + # LVM on device-mapper crypto + if type dmsetup >/dev/null 2>&1; then + device=$(cat $dev/device) + if [ "$(dmsetup status $device | cut -d' ' -f3)" = crypt ]; then + lvm=yes + fi + fi + fi + + # sparc can not have LVM starting at 0 or it will destroy the partition table + if [ "$(udpkg --print-architecture)" = sparc ] && \ + [ "${id%%-*}" = 0 ] && [ $lvm = no ]; then + return 1 + fi + + if [ $lvm = no ]; then + local fs + open_dialog PARTITION_INFO $id + read_line x1 x2 x3 x4 fs x6 x7 + close_dialog + if [ "$fs" = free ]; then + # parted can't deal with VALID_FLAGS on free space + # as yet, so unfortunately we have to special-case + # label types. + local label + open_dialog GET_LABEL_TYPE + read_line label + close_dialog + case $label in + amiga|bsd|dasd|gpt|mac|msdos|sun) + # ... by creating a partition + lvm=yes + ;; + esac + else + local flag + open_dialog VALID_FLAGS $id + while { read_line flag; [ "$flag" ]; }; do + if [ "$flag" = lvm ]; then + lvm=yes + fi + done + close_dialog + fi + fi + + [ $lvm = yes ] +} + +pv_list_allowed () { + local IFS + local partitions + local freenum=1 + for dev in $DEVICES/*; do + [ -d $dev ] || continue + cd $dev + + open_dialog PARTITIONS + partitions="$(read_paragraph)" + close_dialog + + local id size fs path + IFS="$TAB" + echo "$partitions" | + while { read x1 id size x4 fs path x7; [ "$id" ]; }; do + restore_ifs + if pv_allowed "$dev" "$id"; then + if [ "$fs" = free ]; then + printf "%s\t%s\t%s\t%s free #%d\n" "$dev" "$id" "$size" "$(mapdevfs "$(cat "$dev/device")")" "$freenum" + freenum="$(($freenum + 1))" + else + printf "%s\t%s\t%s\t%s\n" "$dev" "$id" "$size" "$(mapdevfs "$path")" + fi + fi + IFS="$TAB" + done + restore_ifs + done +} + +pv_list_allowed_free () { + local line + + IFS="$NL" + for line in $(pv_list_allowed); do + restore_ifs + local dev="${line%%$TAB*}" + local rest="${line#$TAB*}" + local id="${rest%%$TAB*}" + if [ -e "$dev/locked" ] || [ -e "$dev/$id/locked" ]; then + continue + fi + local pv="${line##*$TAB}" + if [ ! -e "$pv" ]; then + echo "$line" + else + local vg=$(lvm_get_info pvs vg_name "$pv" || true) + if [ -z "$vg" ]; then + echo "$line" + fi + fi + IFS="$NL" + done + restore_ifs +} + ############################################################################### # # Physical Volume utility functions @@ -255,6 +373,68 @@ pv_list_free() { done } +# Prepare a partition for use as a PV. If this returns true, then it did +# some work and a commit is necessary. Prints the new path. +pv_prepare() { + local dev="$1" + local id="$2" + local size parttype fs path + + cd "$dev" + open_dialog PARTITION_INFO "$id" + read_line x1 id size freetype fs path x7 + close_dialog + + if [ "$fs" = free ]; then + local newtype + + case $freetype in + primary) + newtype=primary + ;; + logical) + newtype=logical + ;; + pri/log) + local parttype + open_dialog PARTITIONS + while { read_line x1 x2 x3 parttype x5 x6 x7; [ "$parttype" ]; }; do + if [ "$parttype" = primary ]; then + has_primary=yes + fi + done + close_dialog + if [ "$has_primary" = yes ]; then + newtype=logical + else + newtype=primary + fi + ;; + esac + + open_dialog NEW_PARTITION $newtype ext2 $id full $size + read_line x1 id x3 x4 x5 path x7 + close_dialog + fi + + mkdir -p "$id" + local method="$(cat "$id/method" 2>/dev/null || true)" + if [ "$method" = swap ]; then + disable_swap "$id" + fi + if [ "$method" != lvm ]; then + echo lvm >"$id/method" + rm -f "$id/use_filesystem" + rm -f "$id/format" + update_partition "$dev" "$id" + echo "$path" + return 0 + fi + + echo "$path" + return 1 +} + # Initialize a PV pv_create() { local pv
=== modified file 'choose_method/md/choices' --- choose_method/md/choices 2007-12-28 15:33:57 +0000 +++ choose_method/md/choices 2009-07-20 08:14:20 +0000 @@ -1,30 +1,11 @@ #!/bin/sh -. /lib/partman/lib/base.sh +. /lib/partman/lib/md-base.sh dev=$1 id=$2 -# sparc can not have RAID starting at 0 or it will destroy the partition table -if [ "$(udpkg --print-architecture)" = sparc ] && \ - [ "${id%%-*}" = "0" ]; then - exit 0 -fi - -cd $dev - -md=no -open_dialog VALID_FLAGS $id -while { read_line flag; [ "$flag" ]; }; do - if [ "$flag" = raid ]; then - md=yes - fi -done -close_dialog - -if [ $md = no ]; then - exit 0 -fi +md_allowed "$dev" "$id" || exit 0 db_metaget partman/method_long/raid description === modified file 'choose_partition/md/choices' --- choose_partition/md/choices 2009-07-17 20:08:26 +0000 +++ choose_partition/md/choices 2009-07-20 08:16:24 +0000 @@ -1,26 +1,10 @@ #!/bin/sh -. /lib/partman/lib/base.sh +. /lib/partman/lib/md-base.sh -use_raid=no +# Only show menu option if there is at least one partition that can be used +# as a RAID physical volume +[ $(md_list_allowed | wc -l) -gt 0 ] || exit 0 -# Make sure at least one RAID partition is setup -for dev in $DEVICES/*; do - [ -d "$dev" ] || continue - cd $dev - open_dialog PARTITIONS - while { read_line num id size type fs path name; [ "$id" ]; }; do - if [ -f $id/method ]; then - method=$(cat $id/method) - if [ $method = raid ]; then - use_raid=yes - fi - fi - done - close_dialog -done - -if [ "$use_raid" = yes ]; then - db_metaget partman-md/text/configure_md description - printf "md\t%s\n" "$RET" -fi +db_metaget partman-md/text/configure_md description +printf "md\t%s\n" "$RET" === modified file 'choose_partition/md/do_option' --- choose_partition/md/do_option 2009-07-17 20:08:26 +0000 +++ choose_partition/md/do_option 2009-07-20 08:16:58 +0000 @@ -24,41 +24,120 @@ do_initial_setup () { --config=/tmp/mdadm.conf --auto=yes } -prune_partitions () { - local chosen="$1" - local new_partitions= - local i j - for i in $PARTITIONS; do +make_choices () { + local line + descriptions="" + pathmap="" + IFS="$NL" + for line in $(md_list_allowed_free); do + restore_ifs + local dev="${line%%$TAB*}" + line="${line#*$TAB}" + local id="${line%%$TAB*}" + line="${line#*$TAB}" + local size="${line%%$TAB*}" + local path="${line#*$TAB}" + cd $dev + if [ -s "$id/visual_filesystem" ]; then + local visual="$(cat "$id/visual_filesystem")" + output=$(printf "%-30s (%sMB; %s)" "$path" "$(convert_to_megabytes $size)" "$visual") + else + output=$(printf "%-30s (%sMB)" "$path" "$(convert_to_megabytes $size)") + fi + descriptions="${descriptions:+$descriptions, }$output" + pathmap="${pathmap:+$pathmap$NL}$path$TAB$dev//$id" + IFS="$NL" + done + restore_ifs +} + +count_choices () { + echo "$1" | sed -e "s/ *, */\\$NL/g" | wc -l +} + +prune_choices () { + local new_descriptions= + while [ "$descriptions" ]; do + local description="${descriptions%%, *}" + if [ "${descriptions#*, }" = "$descriptions" ]; then + descriptions= + else + descriptions="${descriptions#*, }" + fi + local chosen="$1" local found=0 - for j in $chosen; do - if [ "$i" = "$j" ]; then + while [ "$chosen" ]; do + local choice="${chosen%%, *}" + if [ "${chosen#*, }" = "$chosen" ]; then + chosen= + else + chosen="${chosen#*, }" + fi + if [ "$description" = "$choice" ]; then found=1 break fi done if [ "$found" -eq 0 ]; then - new_partitions="${new_partitions:+$new_partitions }$i" + new_descriptions="${new_descriptions:+$new_descriptions, }$description" + fi + done + descriptions="$new_descriptions" +} + +prepare_devices () { + local devices="$1" + local new_devices= + local need_commit= + while [ "$devices" ]; do + local dev="${devices%%, *}" + if [ "${devices#*, }" = "$devices" ]; then + devices= + else + devices="${devices#*, }" fi + dev="$(echo "$dev" | sed -e 's/ *([^)]*)//g')" + local line + IFS="$NL" + for line in $pathmap; do + restore_ifs + if [ "${line%%$TAB*}" = "$dev" ]; then + local devid="${line#*$TAB}" + local path + if path="$(md_prepare "${devid%//*}" "${devid#*//}")"; then + need_commit=true + fi + new_devices="${new_devices:+$new_devices }$path" + break + fi + IFS="$NL" + done + restore_ifs done - PARTITIONS="$new_partitions" + echo "$new_devices" + + [ "$need_commit" ] } md_create_raid0 () { - db_subst partman-md/raid0devs PARTITIONS "$(ssv_to_csv "$PARTITIONS")" - db_set partman-md/raid0devs '' + db_subst partman-md/raid0devs PARTITIONS "$descriptions" + db_reset partman-md/raid0devs db_input critical partman-md/raid0devs db_go || return db_get partman-md/raid0devs - RET="$(csv_to_ssv "$RET")" - local selected="$(echo "$RET" | wc -w)" - prune_partitions "$RET" + local selected="$(count_choices "$RET")" + prune_choices "$RET" local md_num="$(md_next_device_number)" logger -t partman-md "Number of devices in the RAID0 array md$md_num: $selected" - local raid_devices="$(csv_to_ssv "$RET")" + local raid_devices="$RET" + if raid_devices="$(prepare_devices "$raid_devices")"; then + do_initial_setup + fi + log-output -t partman-md \ mdadm --create /dev/md$md_num --auto=yes --force -R -l raid0 \ -n $selected $raid_devices || return $? @@ -96,8 +175,8 @@ md_create_array () { dev_count="$min_size" fi local required="$(($dev_count + $spare_count))" - if [ "$level" -ne 1 ] && [ "$required" -gt "$NUM_PART" ]; then - db_subst partman-md/notenoughparts NUM_PART "$NUM_PART" + if [ "$level" -ne 1 ] && [ "$required" -gt "$num_parts" ]; then + db_subst partman-md/notenoughparts NUM_PART "$num_parts" db_subst partman-md/notenoughparts REQUIRED "$required" db_input critical partman-md/notenoughparts db_go @@ -112,12 +191,12 @@ md_create_array () { until ([ "$level" -ne 1 ] && [ "$selected" -eq "$dev_count" ]) || \ ([ "$level" -eq 1 ] && [ "$selected" -gt 0 ] && [ "$selected" -le "$dev_count" ]); do db_subst partman-md/raiddevs COUNT "$dev_count" - db_subst partman-md/raiddevs PARTITIONS "$(ssv_to_csv "$PARTITIONS")" + db_subst partman-md/raiddevs PARTITIONS "$descriptions" db_input critical partman-md/raiddevs db_go || return db_get partman-md/raiddevs - selected="$(echo "$RET" | wc -w)" + selected="$(count_choices "$RET")" done local missing_devices= @@ -129,25 +208,25 @@ md_create_array () { done fi - # Remove partitions selected in raiddevs from the PARTITIONS list + # Remove partitions selected in raiddevs from the descriptions list db_get partman-md/raiddevs - prune_partitions "$(csv_to_ssv "$RET")" + prune_choices "$RET" db_set partman-md/raidsparedevs '' selected=0 - if [ "$spare_count" -gt 0 ] && [ -n "$PARTITIONS" ]; then + if [ "$spare_count" -gt 0 ] && [ -n "$descriptions" ]; then local first=1 # Loop until the correct number of devices has been selected. # That means any number less than or equal to the spare count. while [ "$selected" -gt "$spare_count" ] || [ "$first" -eq 1 ]; do first=0 db_subst partman-md/raidsparedevs COUNT "$spare_count" - db_subst partman-md/raidsparedevs PARTITIONS "$(ssv_to_csv "$PARTITIONS")" + db_subst partman-md/raidsparedevs PARTITIONS "$descriptions" db_input critical partman-md/raidsparedevs db_go || return db_get partman-md/raidsparedevs - selected="$(echo "$RET" | wc -w)" + selected="$(count_choices "$RET")" done fi @@ -170,10 +249,21 @@ md_create_array () { local named_spares="$selected" db_get partman-md/raiddevs - local raid_devices="$(csv_to_ssv "$RET")" + local raid_devices="$RET" db_get partman-md/raidsparedevs - local spare_devices="$(csv_to_ssv "$RET")" + local spare_devices="$RET" + + local need_commit= + if raid_devices="$(prepare_devices "$raid_devices")"; then + need_commit=true + fi + if spare_devices="$(prepare_devices "$spare_devices")"; then + need_commit=true + fi + if [ "$need_commit" ]; then + do_initial_setup + fi local missing_spares= local count="$named_spares" @@ -201,13 +291,13 @@ do_create () { db_go || return db_get partman-md/createmain local raid_sel="$RET" - PARTITIONS="$(md_get_partitions)" - if [ -z "$PARTITIONS" ]; then + make_choices + if [ -z "$descriptions" ]; then db_input critical partman-md/noparts db_go return fi - NUM_PART="$(echo "$PARTITIONS" | wc -w)" + num_parts="$(count_choices "$descriptions")" case $raid_sel in RAID10|RAID6|RAID5|RAID1) md_create_array "$raid_sel" ;; === modified file 'lib/md-base.sh' --- lib/md-base.sh 2009-07-17 20:08:26 +0000 +++ lib/md-base.sh 2009-07-20 08:15:53 +0000 @@ -14,66 +14,186 @@ md_devnode () { fi } -md_get_level () { - echo $(mdadm -Q --detail $1 | grep "Raid Level" | sed "s/.*: //") -} - -md_get_devices () { - local device - MD_DEVICES="" - for device in $(grep ^md /proc/mdstat | \ - sed -e 's/^\(md.*\) : .*/\1/'); do - local mddev=$(md_devnode $device) || return 1 - local mdtype=$(md_get_level $mddev) - MD_DEVICES="${MD_DEVICES:+$MD_DEVICES, }${device}_$mdtype" - done -} +# Would this partition be allowed as a RAID physical volume? +md_allowed () { + local dev=$1 + local id=$2 + + cd $dev + + # sparc can not have RAID starting at 0 or it will destroy the partition table + if [ "$(udpkg --print-architecture)" = sparc ] && \ + [ "${id%%-*}" = "0" ]; then + return 1 + fi -md_get_partitions () { - local dev method + local md=no + local fs + open_dialog PARTITION_INFO $id + read_line x1 x2 x3 x4 fs x6 x7 + close_dialog + if [ "$fs" = free ]; then + # parted can't deal with VALID_FLAGS on free space as yet, + # so unfortunately we have to special-case label types. + local label + open_dialog GET_LABEL_TYPE + read_line label + close_dialog + case $label in + amiga|bsd|dasd|gpt|mac|msdos|sun) + # ... by creating a partition + md=yes + ;; + esac + else + local flag + open_dialog VALID_FLAGS $id + while { read_line flag; [ "$flag" ]; }; do + if [ "$flag" = raid ]; then + md=yes + fi + done + close_dialog + fi - PARTITIONS="" + [ $md = yes ] +} +md_list_allowed () { + local IFS + local partitions + local freenum=1 for dev in $DEVICES/*; do - [ -d "$dev" ] || continue + [ -d $dev ] || continue cd $dev + open_dialog PARTITIONS - while { read_line num id size type fs path name; [ "$id" ]; }; do - [ -f $id/method ] || continue - method=$(cat $id/method) - if [ "$method" = raid ]; then - local mappedpath="$(mapdevfs "$path")" - # Exclude partitions that are already part - # of a RAID set - if ! egrep -q "(${path#/dev/}|${mappedpath#/dev/})" /proc/mdstat; then - echo "$mappedpath" + partitions="$(read_paragraph)" + close_dialog + + local id size fs path + IFS="$TAB" + echo "$partitions" | + while { read x1 id size x4 fs path x7; [ "$id" ]; }; do + restore_ifs + if md_allowed "$dev" "$id"; then + if [ "$fs" = free ]; then + printf "%s\t%s\t%s\t%s free #%d\n" "$dev" "$id" "$size" "$(mapdevfs "$(cat "$dev/device")")" "$freenum" + freenum="$(($freenum + 1))" + else + printf "%s\t%s\t%s\t%s\n" "$dev" "$id" "$size" "$(mapdevfs "$path")" fi fi + IFS="$TAB" done - close_dialog + restore_ifs done } -# Converts a list of space (or newline) separated values to comma separated values -# TODO: duplication from partman-lvm -ssv_to_csv () { - local csv value - - csv="" - for value in $1; do - if [ -z "$csv" ]; then - csv="$value" +md_list_allowed_free () { + local line + + IFS="$NL" + for line in $(md_list_allowed); do + restore_ifs + local dev="${line%%$TAB*}" + local rest="${line#$TAB*}" + local id="${rest%%$TAB*}" + if [ -e "$dev/locked" ] || [ -e "$dev/$id/locked" ]; then + continue + fi + local path="${line##*$TAB}" + if [ ! -e "$path" ]; then + echo "$line" else - csv="$csv, $value" + local mappedpath="$(mapdevfs "$path")" + # Exclude partitions that are already part of a RAID + # set + if ! egrep -q "(${path#/dev/}|${mappedpath#/dev/})" /proc/mdstat; then + echo "$line" + fi fi + IFS="$NL" done - echo "$csv" + restore_ifs +} + +# Prepare a partition for use as a RAID physical volume. If this returns +# true, then it did some work and a commit is necessary. Prints the new +# path. +md_prepare () { + local dev="$1" + local id="$2" + local size parttype fs path + + cd "$dev" + open_dialog PARTITION_INFO "$id" + read_line x1 id size freetype fs path x7 + close_dialog + + if [ "$fs" = free ]; then + local newtype + + case $freetype in + primary) + newtype=primary + ;; + logical) + newtype=logical + ;; + pri/log) + local parttype + open_dialog PARTITIONS + while { read_line x1 x2 x3 parttype x5 x6 x7; [ "$parttype" ]; }; do + if [ "$parttype" = primary ]; then + has_primary=yes + fi + done + close_dialog + if [ "$has_primary" = yes ]; then + newtype=logical + else + newtype=primary + fi + ;; + esac + + open_dialog NEW_PARTITION $newtype ext2 $id full $size + read_line x1 id x3 x4 x5 path x7 + close_dialog + fi + + mkdir -p "$id" + local method="$(cat "$id/method" 2>/dev/null || true)" + if [ "$method" = swap ]; then + disable_swap "$id" + fi + if [ "$method" != raid ]; then + echo raid >"$id/method" + rm -f "$id/use_filesystem" + rm -f "$id/format" + update_partition "$dev" "$id" + echo "$path" + return 0 + fi + + echo "$path" + return 1 } -# Converts a list of comma separated values to space separated values -# TODO: duplication from partman-lvm -csv_to_ssv () { - echo "$1" | sed -e 's/ *, */ /g' +md_get_level () { + echo $(mdadm -Q --detail $1 | grep "Raid Level" | sed "s/.*: //") +} + +md_get_devices () { + local device + MD_DEVICES="" + for device in $(grep ^md /proc/mdstat | \ + sed -e 's/^\(md.*\) : .*/\1/'); do + local mddev=$(md_devnode $device) || return 1 + local mdtype=$(md_get_level $mddev) + MD_DEVICES="${MD_DEVICES:+$MD_DEVICES, }${device}_$mdtype" + done } md_db_get_number () {
=== modified file 'choose_partition/crypto/choices' --- choose_partition/crypto/choices 2007-12-28 16:24:47 +0000 +++ choose_partition/crypto/choices 2009-07-17 20:18:02 +0000 @@ -1,26 +1,10 @@ #!/bin/sh -. /lib/partman/lib/base.sh +. /lib/partman/lib/crypto-base.sh -use_crypto=no - -for dev in $DEVICES/*; do - [ -d "$dev" ] || continue - cd $dev - open_dialog PARTITIONS - while { read_line num id size type fs path name; [ "$id" ]; }; do - if [ -f $id/method ]; then - method=$(cat $id/method) - if [ $method = crypto ]; then - use_crypto=yes - fi - fi - done - close_dialog -done - -if [ "$use_crypto" = yes ]; then - db_metaget partman-crypto/text/configure_crypto description - printf "crypto\t%s\n" "$RET" -fi +# Only show menu option if there is at least one partition that can be used +# as a physical volume for encryption +[ $(crypto_list_allowed | wc -l) -gt 0 ] || exit 0 +db_metaget partman-crypto/text/configure_crypto description +printf "crypto\t%s\n" "$RET" === modified file 'choose_partition/crypto/do_option' --- choose_partition/crypto/do_option 2007-12-28 16:24:47 +0000 +++ choose_partition/crypto/do_option 2009-07-17 20:18:02 +0000 @@ -12,8 +12,97 @@ . /lib/partman/lib/crypto-base.sh -crypto_check_setup || exit 1 +done_something= + +do_create () { + local parts line pv output vg pathmap + parts="" + pathmap="" + + # Look for free partitions + IFS="$NL" + for line in $(crypto_list_allowed_free); do + restore_ifs + local dev="${line%%$TAB*}" + line="${line#*$TAB}" + local id="${line%%$TAB*}" + line="${line#*$TAB}" + local size="${line%%$TAB*}" + local path="${line#*$TAB}" + cd $dev + if [ -s "$id/visual_filesystem" ]; then + local visual="$(cat "$id/visual_filesystem")" + output=$(printf "%-30s (%sMB; %s)" "$path" "$(convert_to_megabytes $size)" "$visual") + else + output=$(printf "%-30s (%sMB)" "$path" "$(convert_to_megabytes $size)") + fi + parts="${parts:+$parts, }$output" + pathmap="${pathmap:+$pathmap$NL}$path$TAB$dev//$id" + IFS="$NL" + done + restore_ifs + if [ -z "$parts" ]; then + db_input critical partman-crypto/nothing_to_setup + db_go || true + return + fi + + db_subst partman-crypto/create/partitions PARTITIONS "$parts" + db_reset partman-crypto/create/partitions + db_input critical partman-crypto/create/partitions + db_go || return + db_get partman-crypto/create/partitions + if [ -z "$RET" ]; then + db_input critical partman-crypto/create/nosel + db_go || true + return + fi + parts=$(echo "$RET" | sed -e "s/ *([^)]*) *//g; s/ *, */\\$NL/g") + + local newparts= + local need_commit= + IFS="$NL" + for part in $parts; do + for line in $pathmap; do + restore_ifs + if [ "${line%%$TAB*}" = "$part" ]; then + local devid="${line#*$TAB}" + local path + if path="$(crypto_prepare "${devid%//*}" "${devid#*//}")"; then + need_commit=true + fi + newparts="${newparts:+$newparts }$path" + break + fi + IFS="$NL" + done + IFS="$NL" + done + restore_ifs + parts="$newparts" + + if [ "$need_commit" ]; then + confirm_changes partman-crypto || exit 0 + commit_changes partman-crypto/commit_failed || exit $? + fi +} confirm_changes partman-crypto || exit 0 +commit_changes partman-crypto/commit_failed || exit $? + +while :; do + db_input critical partman-crypto/mainmenu + db_go || exit 10 + db_get partman-crypto/mainmenu + case $RET in + create) do_create ;; + finish) break ;; + *) + logger -t partman-crypto "Unknown selection '$RET'" + break ;; + esac +done + +crypto_check_setup || exit 1 crypto_setup yes || exit 1 === modified file 'debian/partman-crypto.templates' --- debian/partman-crypto.templates 2008-11-10 11:34:12 +0000 +++ debian/partman-crypto.templates 2009-07-17 20:18:02 +0000 @@ -427,3 +427,36 @@ _Description: Proceed to install crypto There does not seem to be sufficient memory available to install additional crypto components. If you choose to go ahead and continue anyway, the installation process could fail. + +Template: partman-crypto/mainmenu +Type: select +Choices-C: create, finish +# Note to translators : Please keep your translations of the choices +# below a 65 columns limit (which means 65 characters +# in single-byte languages) +# :sl3: +__Choices: Create encrypted volumes, Finish +_Description: Encryption configuration actions + This menu allows you to configure encrypted volumes. + +Template: partman-crypto/create/partitions +Type: multiselect +Choices: ${PARTITIONS} +# :sl3: +_Description: Devices to encrypt: + Please select the devices to be encrypted. + . + You can select one or more devices. + +Template: partman-crypto/create/nosel +Type: error +# :sl3: +_Description: No devices selected + No devices were selected for encryption. + +Template: partman-crypto/delete/names +Type: multiselect +Choices: ${VOLUMES} +# :sl3: +_Description: Encrypted volumes to delete: + Please select the encrypted volumes you wish to delete. === modified file 'lib/crypto-base.sh' --- lib/crypto-base.sh 2009-01-06 15:54:32 +0000 +++ lib/crypto-base.sh 2009-07-17 20:18:02 +0000 @@ -1,6 +1,157 @@ . /lib/partman/lib/base.sh . /lib/partman/lib/commit.sh +crypto_list_allowed() { + local IFS + local partitions + local freenum=1 + for dev in $DEVICES/*; do + if [ ! -d "$dev" ] || [ -f "$dev/crypt_realdev" ]; then + continue + fi + cd "$dev" + + open_dialog PARTITIONS + partitions="$(read_paragraph)" + close_dialog + + local id size fs path + IFS="$TAB" + echo "$partitions" | + while { read x1 id size x4 fs path x7; [ "$id" ]; }; do + restore_ifs + if [ "$fs" = free ]; then + printf "%s\t%s\t%s\t%s free #%d\n" "$dev" "$id" "$size" "$(mapdevfs "$(cat "$dev/device")")" "$freenum" + freenum="$(($freenum + 1))" + else + printf "%s\t%s\t%s\t%s\n" "$dev" "$id" "$size" "$(mapdevfs "$path")" + fi + IFS="$TAB" + done + restore_ifs + done +} + +crypto_list_allowed_free() { + local line + + IFS="$NL" + for line in $(crypto_list_allowed); do + restore_ifs + local dev="${line%%$TAB*}" + local rest="${line#*$TAB}" + local id="${rest%%$TAB*}" + if [ -e "$dev/locked" ] || [ -e "$dev/$id/locked" ]; then + continue + fi + echo "$line" + IFS="$NL" + done + restore_ifs +} + +# Prepare a partition for use as a physical volume for encryption. If this +# returns true, then it did some work and a commit is necessary. Prints the +# new path. +crypto_prepare () { + local dev="$1" + local id="$2" + local num size parttype fs path + + cd "$dev" + open_dialog PARTITION_INFO "$id" + read_line num id size freetype fs path x7 + close_dialog + + if [ "$fs" = free ]; then + local newtype + + case $freetype in + primary) + newtype=primary + ;; + logical) + newtype=logical + ;; + pri/log) + local parttype + open_dialog PARTITIONS + while { read_line x1 x2 x3 parttype x5 x6 x7; [ "$parttype" ]; }; do + if [ "$parttype" = primary ]; then + has_primary=yes + fi + done + close_dialog + if [ "$has_primary" = yes ]; then + newtype=logical + else + newtype=primary + fi + ;; + esac + + open_dialog NEW_PARTITION $newtype ext2 $id full $size + read_line num id x3 x4 x5 path x7 + close_dialog + fi + + mkdir -p "$id" + local method="$(cat "$id/method" 2>/dev/null || true)" + if [ "$method" = swap ]; then + disable_swap "$id" + fi + if [ "$method" != crypto ]; then + crypto_prepare_method "$id" dm-crypt || return 1 + rm -f "$id/use_filesystem" + rm -f "$id/format" + echo dm-crypt >"$id/crypto_type" + echo crypto >"$id/method" + + # cloned-and-hacked from + # partman-base/choose_partition/partition_tree/do_option + while true; do + local device="$(humandev $(cat device))" + db_subst partman/active_partition DEVICE "$device" + db_subst partman/active_partition PARTITION "$num" + if [ -f $id/detected_filesystem ]; then + local filesystem=$(cat $id/detected_filesystem) + RET='' + db_metaget partman/filesystem_long/"$filesystem" description || RET='' + if [ "$RET" ]; then + filesystem="$RET" + fi + db_subst partman/text/there_is_detected FILESYSTEM "$filesystem" + db_metaget partman/text/there_is_detected description + else + db_metaget partman/text/none_detected description + fi + db_subst partman/active_partition OTHERINFO "${RET}" + + if [ -f $id/detected_filesystem ] && [ -f $id/format ]; then + db_metaget partman/text/destroyed description + db_subst partman/active_partition DESTROYED "${RET}" + else + db_subst partman/active_partition DESTROYED '' + fi + + ask_user /lib/partman/active_partition "$dev" "$id" + exitcode="$?" + if [ "$exitcode" -ge 128 ] && [ "$exitcode" -lt 192 ]; then + exit "$exitcode" # killed by signal + elif [ "$exitcode" -ge 100 ]; then + break + fi + done + + update_partition "$dev" "$id" + echo "$path" + return 0 + fi + + echo "$path" + return 1 +} + dm_dev_is_safe() { local maj min dminfo deps maj="$1"