[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Automatic partition setup in partman-{lvm,md,crypto}



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                                       [cjwatson@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"


Reply to: