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

Bug#396023: #396023 - Allow encrypted partitions to be removed



The attached patch is my first stab at allowing a more complete removal of configured device-mapper devices (crypto / lvm). It will work in a recursive manner so even crazy setups like crypto-on-lvm-on-crypto-on-something should work.

Before removing a LVM setup or crypto volume, it'll prompt the user. This allows both partman-auto to remove crypto devices and it also allows them to be deallocated in the manual partitioning screen by selecting the crypto device (i.e. the same operation that wipes the partition table of real hard drives).

The changes are fairly big though (diffstat below), so a review would be appreciated.

partman-auto-lvm/auto-lvm_tools.sh | 2 partman-auto/auto-shared.sh | 259 +++++++++++++---- partman-auto/autopartition | 2 partman-auto/debian/partman-auto.templates | 28 +
partman-base/choose_partition/partition_tree/do_option |   16 +
5 files changed, 252 insertions(+), 55 deletions(-)

--
David Härdeman
Index: partman-auto/debian/partman-auto.templates
===================================================================
--- partman-auto/debian/partman-auto.templates	(revision 43266)
+++ partman-auto/debian/partman-auto.templates	(working copy)
@@ -33,14 +33,32 @@
 Template: partman-auto/purge_lvm_from_device
 Type: boolean
 Default: false
-_Description: Remove existing logical volume data?
- The selected device already contains logical volumes and/or
- volume groups from a previous LVM installation, which must be removed
- from the disk before creating any partitions.
+_Description: Remove existing LVM data?
+ The selected device already contains the following LVM logical volumes,
+ volume groups and physical volumes which are about to be removed:
  .
- Note that this will also permanently erase any data currently on the
+ Logical volume(s) to be removed: ${LVTARGETS}
+ .
+ Volume group(s) to be removed: ${VGTARGETS}
+ .
+ Physical volume(s) to be removed: ${PVTARGETS}
+ .
+ Note that this will permanently erase any data currently on the
  logical volumes.
 
+Template: partman-auto/purge_crypto_from_device
+Type: boolean
+Default: false
+_Description: Remove existing crypto volume data?
+ The selected device already contains a crypto volume which is about
+ to be removed.
+ .
+ The following crypto volume will be removed:
+ ${TARGET}
+ .
+ Note that this will also permanently erase any data currently on the
+ crypto volume.
+
 Template: partman-auto/cannot_purge_lvm_from_device
 Type: error
 _Description: LVM physical volume already exists on the selected device
Index: partman-auto/auto-shared.sh
===================================================================
--- partman-auto/auto-shared.sh	(revision 43266)
+++ partman-auto/auto-shared.sh	(working copy)
@@ -1,27 +1,128 @@
 ## this file contains a bunch of shared code between partman-auto
 ## and partman-auto-lvm.
 
-# Wipes any traces of LVM from a disk
-# Normally you wouldn't want to use this function, 
-# but wipe_disk() which will also call this function.
-lvm_wipe_disk() {
-	local dev realdev vg pvs pv lv tmpdev restart
+# Gets the major and minor device numbers of a device passed as $1
+# and puts them in $MAJOR and $MINOR.
+dev_get_major_minor() {
+	local dev stats
 	dev="$1"
-	cd $dev
 
-	if [ ! -e /lib/partman/lvm_tools.sh ]; then
+	if [ -z "$dev" ]; then
+		return 1
+	fi
+
+	stats=$(ls -al "$dev" | sed -e 's/[[:space:],]\+/ /g')
+	MAJOR=$(echo "$stats" | cut -d' ' -f5)
+	MINOR=$(echo "$stats" | cut -d' ' -f6)
+
+	if [ -z "$MAJOR" ] || [ -z "$MINOR" ]; then
+		return 1
+	fi
+
+	return 0
+}
+
+# Makes sure that parted has no stale info
+dm_update_parted() {
+	local restart tmpdev tmppart realdev
+
+	restart="0"
+	for tmpdev in $DEVICES/*; do
+		for tmppart in $tmpdev/*; do
+			if [ ! -d "$tmppart" ]; then
+				continue
+			fi
+
+			if [ ! -e "$tmppart/crypt_active" ]; then
+				continue
+			fi
+
+			if [ ! -e "$(cat "$tmppart/crypt_active")" ]; then
+				rm -f "$tmppart/crypt_active"
+				rm -f "$tmppart/locked"
+				restart="1"
+			fi
+		done
+
+		realdev=$(cat $tmpdev/device)
+		if ! $(echo "$realdev" | grep -q "/dev/mapper/"); then
+			continue
+		fi
+
+		if [ -b "$realdev" ]; then
+			continue
+		fi
+
+		rm -rf $tmpdev
+		restart="1"
+	done
+
+	if [ $restart ]; then
+		stop_parted_server
+		restart_partman || return 1
+	fi
+
+	return 0
+}
+
+# Given a lvm device (excluding the /dev/mapper/ part), will gather data on
+# the LVM config, prompt the user and then remove the VG along with all the
+# LV's and PV's which belong to that VG.
+dm_wipe_lvm() {
+	local dev vg pv pvs lv lvs vgtext lvtext pvtext
+	dev="$1"
+
+	# First check that the device exists, it's not an error if it doesn't
+	# since one call to this function can wipe several LV's
+	if [ ! -b "/dev/mapper/$dev" ]; then
 		return 0
 	fi
 
+	if [ ! -e /lib/partman/lvm_tools.sh ]; then
+		return 1
+	fi
+
 	. /lib/partman/lvm_tools.sh
 
-	# Check if the device already contains any physical volumes
-	realdev=$(mapdevfs "$(cat $dev/device)")
-	if ! pv_on_device "$realdev"; then
-		return 0
+	# We get a device like mainvg-swaplv as $1, let's figure out the VG
+
+	# Make sure that the device includes at least one dash
+        if [ "$(echo -n "$dev" | tr -d -)" = "$dev" ]; then
+                return 1
 	fi
 
+        # Split volume group from logical volume.
+        vg=$(echo ${dev} | sed -e 's#\(.*\)\([^-]\)-[^-].*#\1\2#')
+        # Reduce padded --'s to -'s
+        vg=$(echo ${vg} | sed -e 's#--#-#g')
+	if [ -z "$vg" ]; then
+		return 1
+	fi
+	pvs=$(vg_list_pvs $vg)
+	lvs=$(vg_list_lvs $vg)
+
 	# Ask for permission to erase LVM volumes 
+	vgtext="$vg"
+	pvtext=""
+	for pv in $pvs; do
+		if [ -z "$pvtext" ]; then
+			pvtext="$pv"
+		else
+			pvtext="$pvtext, $pv"
+		fi
+	done
+	lvtext=""
+	for lv in $lvs; do
+		if [ -z "$lvtext" ]; then
+			lvtext="$lv"
+		else
+			lvtext="$lvtext, $lv"
+		fi
+	done
+
+	db_subst partman-auto/purge_lvm_from_device VGTARGETS "$vgtext"
+	db_subst partman-auto/purge_lvm_from_device LVTARGETS "$lvtext"
+	db_subst partman-auto/purge_lvm_from_device PVTARGETS "$pvtext"
 	db_input critical partman-auto/purge_lvm_from_device
 	db_go || return 1
 	db_get partman-auto/purge_lvm_from_device
@@ -29,65 +130,127 @@
 		return 1
 	fi
 
-	# Check all VG's
-	for vg in $(vg_list); do
-		pvs=$(vg_list_pvs $vg)
-		
-		# Only deal with VG's on the selected disk
-		if ! $(echo "$pvs" | grep -q "$realdev"); then
+	# Remove the LV's
+	for lv in $lvs; do
+		lv_delete $vg $lv
+	done
+
+	# Remove the VG
+	vg_delete $vg
+
+	# Remove the PV's
+	for pv in $pvs; do
+		pv_delete $pv
+		partman_unlock_unit $pv
+	done
+
+	return 0
+}
+
+# Given a cryptodev (minus the /dev/mapper/ part), will prompt the user and
+# then remove the device.
+dm_wipe_crypto() {
+	local dev
+	dev="$1"
+
+	# Ask for permission to erase crypto volumes 
+	db_subst partman-auto/purge_crypto_from_device TARGET "$dev"
+	db_input critical partman-auto/purge_crypto_from_device
+	db_go || return 1
+	db_get partman-auto/purge_crypto_from_device
+	if [ "$RET" != "true" ]; then
+		return 1
+	fi
+
+	dmsetup remove "$dev"
+	return $?
+}
+
+# Given a dm device (minus the /dev/mapper/ part), will recursively remove
+# all devices which depend on it and then the device itself.
+dm_wipe_dev() {
+	local dev deps dep name type
+	dev="$1"
+
+	if [ ! -e "/dev/mapper/$dev" ]; then
+		return 0
+	fi
+
+	dev_get_major_minor "/dev/mapper/$dev" || return 1
+	deps=$(dmsetup deps | grep "($MAJOR, $MINOR)" | cut -d: -f1)
+	for dep in $deps; do
+		if [ ! -e "/dev/mapper/$dep" ]; then
 			continue
 		fi
 
-		# Make sure the VG don't span any other disks
-		if $(echo -n "$pvs" | grep -q -v "$realdev"); then
-			log-output -t partman-auto-lvs vgs
-			db_input critical partman-auto/cannot_purge_lvm_from_device || true
-			db_go || true
+		dev_get_major_minor "/dev/mapper/$dep" || return 1
+		name=$(dmsetup info -j$MAJOR -m$MINOR | grep "Name:" | \
+			cut -d: -f2 | sed -e 's/[[:space:]]//g')
+		if [ -z "$name" ]; then
 			return 1
 		fi
 
-		# Remove LV's from the VG
-		for lv in $(vg_list_lvs $vg); do
-			lv_delete $vg $lv
-		done
-
-		# Remove the VG and its PV's 
-		vg_delete $vg
-		for pv in $pvs; do
-			pv_delete $pv
-		done
+		dm_wipe_dev "$name" || return 1
 	done
 
-	# Make sure that parted has no stale LVM info
-	restart="0"
-	for tmpdev in $DEVICES/*; do
-		realdev=$(cat $tmpdev/device)
+	type=$(dmsetup table | grep "^$dev:" | cut -d' ' -f4)
+	if [ -z "$type" ]; then
+		return 1
+	elif [ "$type" = crypt ]; then
+		dm_wipe_crypto "$dev" || return 1
+	else
+		dm_wipe_lvm "$dev" || return 1
+	fi
 
-		if ! $(echo "$realdev" | grep -q "/dev/mapper/"); then
+	return 0
+}
+
+# Wipes any dm devices from a disk (LVM and/or crypto)
+# Given an argument like /dev/hda, /dev/hda1 and /dev/hda2 are also wiped...
+# Normally you wouldn't want to use this function, 
+# but wipe_disk() which will also call this function.
+dm_wipe_disk() {
+	local dev realdev devs check checkdev provide provides restart tmpdev tmppart
+	dev="$1"
+
+	if [ -z "$dev" ]; then
+		return 1
+	fi
+
+	realdev=$(mapdevfs "$(cat $dev/device)")
+	devs=$(ls ${realdev}* 2>/dev/null)
+
+	# Given e.g. /dev/hda we check /dev/hda, /dev/hda1, etc...
+	for check in $devs; do
+		checkdev=$(readlink -f "$check")
+		if [ ! -e "$checkdev" ]; then
 			continue
 		fi
 
-		if [ -b "$realdev" ]; then
+		dev_get_major_minor "$checkdev" || return 1
+
+		provides=$(dmsetup deps | grep "($MAJOR, $MINOR)" | cut -d: -f1)
+
+		if [ -z "$provides" ]; then
 			continue
 		fi
 
-		rm -rf $tmpdev
-		restart="1"
+		for provide in $provides; do
+			dm_wipe_dev $provide || return 1
+		done
 	done
-
-	if [ $restart ]; then
-		stop_parted_server
-		restart_partman || return 1
-	fi
-
+	
+	dm_update_parted || return 1
 	return 0
 }
 
 wipe_disk() {
-	cd $dev
+	local dev
+	dev="$1"
 
-	lvm_wipe_disk "$dev" || return 1
+	dm_wipe_disk "$dev" || return 1
 
+	cd $dev
 	open_dialog LABEL_TYPES
 	types=$(read_list)
 	close_dialog
Index: partman-auto/autopartition
===================================================================
--- partman-auto/autopartition	(revision 43266)
+++ partman-auto/autopartition	(working copy)
@@ -19,7 +19,7 @@
 
 	choose_recipe default "$target" "$free_size" || exit $?
 
-	wipe_disk || exit $?
+	wipe_disk "$dev" || exit $?
 else
 	# Two parameters, being run on selected free space.
 	free_space=$2
Index: partman-auto-lvm/auto-lvm_tools.sh
===================================================================
--- partman-auto-lvm/auto-lvm_tools.sh	(revision 43266)
+++ partman-auto-lvm/auto-lvm_tools.sh	(working copy)
@@ -31,7 +31,7 @@
 
 	choose_recipe lvm "$target" "$free_size" || return $?
 
-	wipe_disk || return $?
+	wipe_disk "$dev" || return $?
 
 	# Check if partition is usable; use existing partman-auto template as we depend on it
 	if [ "$free_type" = unusable ]; then
Index: partman-base/choose_partition/partition_tree/do_option
===================================================================
--- partman-base/choose_partition/partition_tree/do_option	(revision 43266)
+++ partman-base/choose_partition/partition_tree/do_option	(working copy)
@@ -53,6 +53,22 @@
     close_dialog
     # do not try to create partition table on sw RAID device or LVM LV
     if [ "$x" = loop ]; then
+    	# But allow crypto devices to be deleted
+	if [ -e "$dev/crypt_realdev" ]; then
+		if [ ! -e "/lib/partman/auto-shared.sh" ] ; then
+			exit 0
+		fi
+
+		device="$(cat "$dev/device")"
+		dmdevice="${device##/dev/mapper/}"
+		if [ "$device" = "$dmdevice" ]; then
+			exit 0
+		fi
+
+		. /lib/partman/auto-shared.sh
+		dm_wipe_dev "$dmdevice" || exit 1
+		dm_update_parted || exit 1
+	fi
         exit 0
     fi
     mklabel=$(echo /lib/partman/storage_device/[0-9][0-9]label/do_option)

Reply to: