Bug#394389: [Yaird-devel] Bug#394389: yaird: Loopback and loop-AES device support

Peter Colberg peterco at gmx.net
Fri Oct 27 16:57:37 CEST 2006


Hello Jonas,

On Sun, Oct 22, 2006 at 03:53:40AM +0200, Jonas Smedegaard wrote:
> Peter Colberg wrote:
> > Package: yaird
> > [..]
> > 
> > Loopback and loop-AES device support for yaird in every possible way.
> 
> 
> Wauw! Looks great!
> 
> 
> I'll let it hang for a bit before applying officially - please bother me
> if/when you feel it has hung for too long.

Regardless of the short time span which has passed since I posted the
first patch, I now consider my loop-AES support patches to be ready
for review.

The following final changes are included below:

=> 1105_mount_readonly_and_unmount_v2.patch

   To prevent code duplication in places where a device depends
   on the temporary mount of another device (e.g. loop-AES device
   depending on a GPG key device), I included an additional argument
   with the subroutines Plan::addBlockDevMount and Plan::addFsTabMount
   to directly pass on the $working devices.
   (This is meant to prevent device dependency loops.)

   In addition, Plan::addFsTabMount now also takes $isRoot
   as an argument for possible future use.


=> 1106_add_loopback_and_loopaes_with_loopaestab_support_v5.patch

   This one includes one major change in the handling of the
   loop= attribute in fstab. Instead of duplicating code to
   test for the existence of the loop= attribute whenever an
   fstab entry is queried (via FsTab::findByMountPoint,
   FsTab::findByDevno or FsTab::findByPath), this is now
   handled in a central place in FsEntry::blockDevPath.

   Thus, further changes to the Plan::addBlockDevMount subroutine
   are no longer needed, and the GPG key device handling in
   Plan::tryLoop is much easier (and now also properly supports
   fstab entries with LABEL or UUID). As an added bonus,
   yaird's test mode correctly interprets all fstab entries
   with loop= attributes.

   This change also means that when querying for an fstab entry via
   a device path or number, one always has to specify the device
   which is *directly* mounted; in our case, this is the loopback
   or loop-AES device.

   Other changes include use of perl's index() function
   (no metaquoting required as in regular expressions), and
   some cleanup in the distinct-configuration-pairs iteration.



In summary, loopback and loop-AES support comprises the following patches:
+ 1101_base_canon_root_path_fix.patch
+ 1102_base_filedev_stat_device_of_file.patch
+ 1103_fstab_findbydevno.patch
+ 1104_templates_quiet_mksymdev_delay.patch
+ 1105_mount_readonly_and_unmount_v2.patch
+ 1106_add_loopback_and_loopaes_with_loopaestab_support_v5.patch



Besides integrating the actual code, what remains to be done now
is mostly coordination with the loop-aes-utils maintainer concerning
the proposed config file for loop-AES devices ('/etc/loopaestab'),
if you consider this the right path to follow.

Finally, there is the question about which further documentation
is needed with the yaird package, e.g. a section about loopback
and loop-AES devices in the man page.

(Btw, I hope it was ok to tag bugs #393363 [loopaestab] and #394650
[vfat mounting] as blocking this bug; if not, please feel free to
revert these correlations.)

Regards,
Peter
-------------- next part --------------
diff -urN yaird-0.0.12.orig/perl/ActiveBlockDev.pm yaird-0.0.12/perl/ActiveBlockDev.pm
--- yaird-0.0.12.orig/perl/ActiveBlockDev.pm	2006-10-26 15:41:37.000000000 +0200
+++ yaird-0.0.12/perl/ActiveBlockDev.pm	2006-10-26 15:45:01.000000000 +0200
@@ -173,6 +173,10 @@
 		Base::assert ($name =~ /^md\d+$/);
 		$self->{yspecial} = "/dev/$name";
 	}
+	elsif ($creator eq "losetup") {
+		Base::assert ($name =~ /^loop\d+$/);
+		$self->{yspecial} = "/dev/$name";
+	}
 	elsif ($creator eq "devmapper") {
 		Base::assert ($name =~ /^dm-\d+$/);
 		my $paths = BlockSpecialFileTab::pathsByDevno ($devno);
diff -urN yaird-0.0.12.orig/perl/Conf.pm.in yaird-0.0.12/perl/Conf.pm.in
--- yaird-0.0.12.orig/perl/Conf.pm.in	2006-10-26 15:41:37.000000000 +0200
+++ yaird-0.0.12/perl/Conf.pm.in	2006-10-26 15:45:01.000000000 +0200
@@ -61,6 +61,7 @@
 	procFs		=> sub { "/proc"; },
 	dev		=> sub { "/dev"; },
 	fstab		=> sub { "/etc/fstab"; },
+	loopaestab	=> sub { "/etc/loopaestab"; },
 	crypttab	=> sub { "/etc/crypttab"; },
 	hotplug		=> sub { "/etc/hotplug"; },
 	appVersion	=> sub { "@VERSION@"; },
diff -urN yaird-0.0.12.orig/perl/FsEntry.pm yaird-0.0.12/perl/FsEntry.pm
--- yaird-0.0.12.orig/perl/FsEntry.pm	2006-10-26 15:41:37.000000000 +0200
+++ yaird-0.0.12/perl/FsEntry.pm	2006-10-26 15:53:47.000000000 +0200
@@ -83,8 +83,8 @@
 	my $msg = undef;
 
 	if ($dev =~  /^\//) {
-		if ($self->opts->exists('loop')) {
-			$msg = "loopback mount for '$dev' not supported ($origin)";
+		if ($self->opts->exists ('loop') && $self->type eq 'swap') {
+			$msg = "loop option for swap device '$dev' not supported ($origin)";
 			return (undef, $msg);
 		}
 		if (-f $dev && $self->type eq "swap") {
@@ -92,6 +92,10 @@
 			return (undef, $msg);
 		}
 
+		if ($self->opts->exists ('loop')) {
+			$dev = $self->opts->get ('loop');
+		}
+
 		if (! -e $dev) {
 			$msg = "'$dev' not found ($origin)";
 			return (undef, $msg);
diff -urN yaird-0.0.12.orig/perl/FsOpts.pm yaird-0.0.12/perl/FsOpts.pm
--- yaird-0.0.12.orig/perl/FsOpts.pm	2006-10-26 15:41:37.000000000 +0200
+++ yaird-0.0.12/perl/FsOpts.pm	2006-10-26 15:45:01.000000000 +0200
@@ -43,6 +43,19 @@
 		next if $key eq 'user';
 		next if $key eq 'users';
 		next if $key eq 'defaults';
+
+		# loop and loop-AES attributes
+		next if $key eq 'loop';
+		next if $key eq 'offset';
+		next if $key eq 'sizelimit';
+		next if $key eq 'encryption';
+		next if $key eq 'pseed';
+		next if $key eq 'phash';
+		next if $key eq 'loinit';
+		next if $key eq 'gpgkey';
+		next if $key eq 'gpghome';
+		next if $key eq 'itercountk';
+
 		my $val = $opts->{$key};
 		if (defined ($val)) {
 			push @cmdLine, "$key=$val";
diff -urN yaird-0.0.12.orig/perl/KConfig.pm yaird-0.0.12/perl/KConfig.pm
--- yaird-0.0.12.orig/perl/KConfig.pm	2006-10-26 15:44:48.000000000 +0200
+++ yaird-0.0.12/perl/KConfig.pm	2006-10-26 15:45:01.000000000 +0200
@@ -208,6 +208,9 @@
 	# Compaq Smart Array controllers
 	'cpqarray' => [ 'BLK_CPQ_DA' ],
 	'cciss' => [ 'BLK_CPQ_CISS_DA' ],
+	
+	# loopback
+	'loop' => [ 'BLK_DEV_LOOP' ],
 };
 
 
diff -urN yaird-0.0.12.orig/perl/LoopAesEntry.pm yaird-0.0.12/perl/LoopAesEntry.pm
--- yaird-0.0.12.orig/perl/LoopAesEntry.pm	1970-01-01 01:00:00.000000000 +0100
+++ yaird-0.0.12/perl/LoopAesEntry.pm	2006-10-26 15:45:01.000000000 +0200
@@ -0,0 +1,48 @@
+#!perl -w
+#
+# LoopAesEntry -- encapsulate a single entry in /etc/loopaestab
+#   Copyright (C) 2006  Peter Colberg
+#
+# Based on CryptEntry -- encapsulate a single entry in /etc/crypttab
+#   Copyright (C) 2005  Erik van Konijnenburg
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+#
+use strict;
+use warnings;
+package LoopAesEntry;
+use base 'Obj';
+
+sub fill {
+	my $self = shift;
+	$self->SUPER::fill();
+	$self->takeArgs ('target', 'source', 'opts', 'origin');
+}
+
+sub target	{ return $_[0]->{target}; }
+sub source	{ return $_[0]->{source}; }
+sub opts	{ return $_[0]->{opts}; }
+sub origin	{ return $_[0]->{origin}; }
+
+sub string {
+	my $self = shift;
+	my $target = $self->target();
+	my $source = $self->source();
+	my $opts = $self->opts()->string();
+	return "$target in $source with $opts";
+}
+
+
+1;
diff -urN yaird-0.0.12.orig/perl/LoopAesTab.pm yaird-0.0.12/perl/LoopAesTab.pm
--- yaird-0.0.12.orig/perl/LoopAesTab.pm	1970-01-01 01:00:00.000000000 +0100
+++ yaird-0.0.12/perl/LoopAesTab.pm	2006-10-26 15:45:01.000000000 +0200
@@ -0,0 +1,120 @@
+#!perl -w
+#
+# LoopAesTab -- encapsulate /etc/loopaestab.
+#   Copyright (C) 2006  Peter Colberg
+#
+# Based on CryptTab -- encapsulate /etc/crypttab.
+#   Copyright (C) 2005  Erik van Konijnenburg
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+#
+use strict;
+use warnings;
+use Base;
+use Conf;
+use LoopAesEntry;
+package LoopAesTab;
+
+
+my $loopAesTab = undef;
+
+
+sub init () {
+	if (defined ($loopAesTab)) {
+		return;
+	}
+	$loopAesTab = [];
+	my $name = Conf::get('loopaestab');
+	if (! -e $name) {
+		#
+		# It's OK if there's no /etc/loopaestab, but if it
+		# exists, it had better be readable.
+		#
+		return;
+	}
+	if (! open (IN, "<", "$name")) {
+		Base::fatal ("can't read $name");
+	}
+	my $lineNo = 0;
+	while (defined (my $line = <IN>)) {
+		$lineNo++;
+		chomp $line;
+
+		$line =~ s/^\s*//;
+		next if $line =~ /^#/;	# comment line
+		next if $line eq "";
+
+		my @fields = split (/\s+/, $line);
+		if (@fields < 2) {
+			Base::fatal ("no source device in $name:$lineNo");
+		}
+		my $target = shift @fields;
+		my $source = shift @fields;
+		my $optString = (shift @fields or '');
+		my $opts = Opts->new (string => $optString);
+
+		my $descr = LoopAesEntry->new(
+			target => $target,
+			source => $source,
+			opts => $opts,
+			origin => "$name:$lineNo",
+			);
+		push @{$loopAesTab}, $descr;
+	}
+	if (! close (IN)) {
+		Base::fatal ("could not read $name");
+	}
+}
+
+sub all () {
+	init;
+	return $loopAesTab;
+}
+
+sub findByTarget ($) {
+	my ($target) = @_;
+	my $result;
+	my $devno = Base::devno ($target);
+	if (! defined ($devno)) {
+		Base::fatal ("cannot find device number for: $target");
+	}
+	return findByDevno ($devno);
+}
+
+sub findByDevno ($) {
+	my ($devno) = @_;
+	my $result = undef;
+
+	for my $entry (@{LoopAesTab::all()}) {
+		my $b2 = $entry->target;
+		my $n2 = Base::devno ($b2);
+		if (! defined ($n2)) {
+			next;
+		}
+
+		if ($n2 eq $devno) {
+			if (defined ($result)) {
+				my $o1 = $entry->origin;
+				my $o2 = $result->origin;
+				Base::fatal ("duplicate device '$b2' in $o1, $o2");
+			}
+			$result = $entry;
+		}
+	}
+	return $result;
+}
+
+
+1;
diff -urN yaird-0.0.12.orig/perl/LoopAesVersion.pm yaird-0.0.12/perl/LoopAesVersion.pm
--- yaird-0.0.12.orig/perl/LoopAesVersion.pm	1970-01-01 01:00:00.000000000 +0100
+++ yaird-0.0.12/perl/LoopAesVersion.pm	2006-10-26 15:45:01.000000000 +0200
@@ -0,0 +1,104 @@
+#!perl -w
+#
+# LoopAesVersion -- utility functions to probe for loop-AES support
+#   Copyright (C) 2006  Peter Colberg
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+#
+use strict;
+use warnings;
+use Base;
+package LoopAesVersion;
+
+my $hasLoopAesModule = undef;
+my $hasLoopAesSetup = undef;
+
+#
+# Probe loop-AES patched loop module
+#
+sub probeLoopAesModule ($) {
+	my ($path) = @_;
+	my @strings = (
+		'loop_compute_sector_iv',
+		'loop_compute_md5_iv_v3',
+	);
+	$hasLoopAesModule = grepBinary ($path, @strings);
+}
+
+#
+# Probe loop-AES patched losetup program
+#
+sub probeLoopAesSetup ($) {
+	my ($path) = @_;
+	my @strings = (
+		'multi-key-v2',
+		'multi-key-v3',
+	);
+	$hasLoopAesSetup = grepBinary ($path, @strings);
+}
+
+#
+# Check if userspace and kernel loopback versions match
+#
+sub matchingVersions () {
+	if (KConfig::isBuiltIn ('loop')) {
+		# You are on your own here...
+		return 1;
+	}
+	if (! defined ($hasLoopAesModule)) {
+		Base::fatal ("unknown loop module version");
+	}
+	if (! defined ($hasLoopAesSetup)) {
+		Base::fatal ("unknown losetup version");
+	}
+	return ($hasLoopAesModule == $hasLoopAesSetup);
+}
+
+#
+# Check if binary file matches given strings
+#
+sub grepBinary ($@) {
+	my ($path, @strings) = @_;
+	my ($len, $buf, $prevbuf);
+	my %unseen = map { $_, 1 } @strings;
+
+	$len = 1024;
+	for my $s (keys (%unseen)) {
+		while (length ($s) > $len) {
+			$len = ($len << 1);
+		}
+	}
+	if (! open (IN, "<", "$path")) {
+		return undef;
+	}
+	binmode (IN);
+	$prevbuf = '';
+	while (read (IN, $buf, $len)) {
+		$_ = $prevbuf . $buf;
+		for my $s (keys (%unseen)) {
+			if (index ($_, $s) >= $[) {
+				delete $unseen{$s};
+			}
+		}
+		last if (! keys (%unseen));
+		$prevbuf = $buf;
+	}
+	close (IN);
+
+	return (! keys (%unseen));
+}
+
+
+1;
diff -urN yaird-0.0.12.orig/perl/LoopDev.pm yaird-0.0.12/perl/LoopDev.pm
--- yaird-0.0.12.orig/perl/LoopDev.pm	1970-01-01 01:00:00.000000000 +0100
+++ yaird-0.0.12/perl/LoopDev.pm	2006-10-26 15:45:01.000000000 +0200
@@ -0,0 +1,49 @@
+#!perl -w
+#
+# LoopDev -- the probed values for a loopback device, as found by losetup.
+#   Copyright (C) 2006  Peter Colberg
+#
+# Based on RaidDev -- the probed values for a raid device, as found by mdadm.
+#   Copyright (C) 2005  Erik van Konijnenburg
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+#
+use strict;
+use warnings;
+package LoopDev;
+use base 'Obj';
+
+sub fill {
+	my $self = shift;
+	$self->SUPER::fill();
+	$self->takeArgs ('target', 'devno', 'opts', 'source');
+}
+
+sub target	{ return $_[0]->{target}; }
+sub devno	{ return $_[0]->{devno}; }
+sub opts	{ return $_[0]->{opts}; }
+sub source	{ return $_[0]->{source}; }
+
+sub string {
+	my $self = shift;
+	my $target = $self->target;
+	my $devno = $self->devno;
+	my $opts = $self->opts->string;
+	my $source = $self->source;
+	return "$target($devno) on $source" . ($opts ? " with $opts" : "");
+}
+
+
+1;
diff -urN yaird-0.0.12.orig/perl/LoopTab.pm yaird-0.0.12/perl/LoopTab.pm
--- yaird-0.0.12.orig/perl/LoopTab.pm	1970-01-01 01:00:00.000000000 +0100
+++ yaird-0.0.12/perl/LoopTab.pm	2006-10-26 15:45:01.000000000 +0200
@@ -0,0 +1,178 @@
+#!perl -w
+#
+# LoopTab -- encapsulate losetup output
+#   Copyright (C) 2006  Peter Colberg
+#
+# Based on RaidTab -- encapsulate mdadm output
+#   Copyright (C) 2005  Erik van Konijnenburg
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+#
+#
+use strict;
+use warnings;
+use ActiveBlockDevTab;
+use Base;
+use BlockSpecialFileTab;
+use LoopAesVersion;
+use LoopDev;
+package LoopTab;
+
+
+my $loopTab = undef;
+
+
+#
+# init -- initialise table of all known loopback devices.
+#
+sub init () {
+	if (defined ($loopTab)) {
+		return;
+	}
+
+	$loopTab = [];
+
+	for my $abd (@{ActiveBlockDevTab::all ()}) {
+		next if (! ($abd->name =~ /^loop\d+$/));
+		my $paths = BlockSpecialFileTab::pathsByDevno ($abd->devno);
+		next if (! defined ($paths));
+		my ($rc, $lines) = Base::runCmd (missingOk => 1, failOk => 1,
+			cmd => ['/sbin/losetup', $paths->[0]]);
+		if ($rc && defined ($lines) && @{$lines} == 1) {
+			processLine ($lines->[0]);
+		}
+	}
+	if (@{$loopTab}) {
+		LoopAesVersion::probeLoopAesSetup ('/sbin/losetup');
+	}
+}
+
+#
+# processLine -- parse losetup output for a single loopback device
+#
+# Both native and loop-AES patched losetup syntax is supported.
+#
+# Example native syntax:
+# /dev/loop0: [0900]:57004 (/dev/md0), offset 1000000
+#
+# Example loop-AES syntax:
+# /dev/loop1: [000e]:3762 (/dev/vg/swap) offset=1024 sizelimit=20971520 encryption=AES256 multi-key-v3 loinit=123
+#
+# Parsing expressions were derived from the function 'show_loop'
+# in file 'mount/lomount.c' of the unpatched and loop-AES (v3.1d)
+# patched util-linux package (v2.12r).
+#
+sub processLine ($) {
+	my ($line) = @_;
+
+	my ($target, $source, @attributes, @opts);
+
+	# common device description
+	if ($line =~ /^(\S+): \[[[:xdigit:]]+\]:\d+ \((\S+)\)(?:(,? )(.*))?$/) {
+		$target = $1;
+		$source = $2;
+		if (defined ($4)) {
+			@attributes = split (/$3/, $4);
+		}
+	}
+	else {
+		Base::fatal ("could not parse losetup output: '$line'");
+	}
+
+	for my $attr (@attributes) {
+		# common attribute
+		if ($attr =~ /^offset[ =](@?\d+)$/) {
+			push @opts, "offset=$1";
+		}
+		# loop-AES attribute
+		elsif ($attr =~ /^sizelimit=(\d+)$/) {
+			push @opts, "sizelimit=$1";
+		}
+		# non-loop-AES attribute
+		elsif ($attr =~ /^sizelimit (\d+)$/) {
+			# info-only attribute
+		}
+		# loop-AES attribute
+		elsif ($attr =~ /^encryption=(\S+)$/) {
+			push @opts, "encryption=$1";
+		}
+		# non-loop-AES attribute
+		elsif ($attr =~ /^encryption /) {
+			Base::fatal ("cryptoloop device ('$target') not supported");
+		}
+		# loop-AES attribute
+		elsif ($attr =~ /^loinit=(\d+)$/) {
+			push @opts, "loinit=$1";
+		}
+		# loop-AES attribute
+		elsif ($attr =~ /^read-only$/) {
+			Base::fatal ("read-only loopback device ('$target') not supported");
+		}
+		# loop-AES attribute
+		elsif ($attr =~ /^multi-key-(v[23])$/) {
+			#
+			# Note: If a loopback device is in multi-key mode,
+			# a GPG encryption key is definitely required.
+			#
+			push @opts, "multikey=$1";
+		}
+		else {
+			Base::fatal ("unknown attribute '$attr' in losetup output");
+		}
+	}
+	my $optString = join (",", @opts);
+	my $opts = Opts->new (string => $optString);
+
+	my $devno = Base::devno ($target);
+	if (! defined($devno)) {
+		Base::fatal ("device '$target' in losetup output: can't find device major/minor number");
+	}
+
+	my $descr = LoopDev->new (
+		target => $target,
+		devno => $devno,
+		opts => $opts,
+		source => $source,
+	);
+	push @{$loopTab}, $descr;
+}
+
+sub all () {
+	init;
+	return $loopTab;
+}
+
+sub findByTarget ($) {
+	my ($target) = @_;
+	for my $ld (@{all()}) {
+		if ($ld->target() eq $target) {
+			return $ld;
+		}
+	}
+	return undef;
+}
+
+sub findByDevno ($) {
+	my ($devno) = @_;
+	for my $ld (@{all()}) {
+		if ($ld->devno() eq $devno) {
+			return $ld;
+		}
+	}
+	return undef;
+}
+
+
+1;
diff -urN yaird-0.0.12.orig/perl/Makefile.am yaird-0.0.12/perl/Makefile.am
--- yaird-0.0.12.orig/perl/Makefile.am	2006-10-26 15:41:37.000000000 +0200
+++ yaird-0.0.12/perl/Makefile.am	2006-10-26 15:45:01.000000000 +0200
@@ -71,6 +71,11 @@
 	LabeledPartition.pm \
 	LabeledPartitionTab.pm \
 	LogicalVolume.pm \
+	LoopAesEntry.pm \
+	LoopAesTab.pm \
+	LoopAesVersion.pm \
+	LoopDev.pm \
+	LoopTab.pm \
 	LvmTab.pm \
 	ModProbe.pm \
 	NetDev.pm \
diff -urN yaird-0.0.12.orig/perl/Makefile.in yaird-0.0.12/perl/Makefile.in
--- yaird-0.0.12.orig/perl/Makefile.in	2006-10-26 15:41:37.000000000 +0200
+++ yaird-0.0.12/perl/Makefile.in	2006-10-26 15:45:01.000000000 +0200
@@ -229,6 +229,11 @@
 	LabeledPartition.pm \
 	LabeledPartitionTab.pm \
 	LogicalVolume.pm \
+	LoopAesEntry.pm \
+	LoopAesTab.pm \
+	LoopAesVersion.pm \
+	LoopDev.pm \
+	LoopTab.pm \
 	LvmTab.pm \
 	ModProbe.pm \
 	NetDev.pm \
diff -urN yaird-0.0.12.orig/perl/ModProbe.pm yaird-0.0.12/perl/ModProbe.pm
--- yaird-0.0.12.orig/perl/ModProbe.pm	2006-10-26 15:41:37.000000000 +0200
+++ yaird-0.0.12/perl/ModProbe.pm	2006-10-26 15:45:01.000000000 +0200
@@ -90,6 +90,7 @@
 use ActionList;
 use Blacklist;
 use KConfig;
+use LoopAesVersion;
 package ModProbe;
 
 
@@ -147,6 +148,15 @@
 			Base::fatal ("modprobe shows that module $m needs an external program; this is not supported.  The offending line is: install $1");
 		}
 		elsif ($line =~ /^insmod (\S+)$/) {
+			if ($m eq 'loop' ) {
+				#
+				# There exist two different loop modules both
+				# named 'loop': a native version and a loop-AES
+				# patched version.
+				#
+				LoopAesVersion::probeLoopAesModule ($1);
+			}
+
 			$actionList->add ("insmod", $1,
 				optionList => '');
 		}
diff -urN yaird-0.0.12.orig/perl/Plan.pm yaird-0.0.12/perl/Plan.pm
--- yaird-0.0.12.orig/perl/Plan.pm	2006-10-26 15:44:48.000000000 +0200
+++ yaird-0.0.12/perl/Plan.pm	2006-10-26 16:09:02.000000000 +0200
@@ -33,6 +33,9 @@
 use ActionList;
 use CryptTab;
 use NetDevTab;
+use LoopAesTab;
+use LoopTab;
+use LoopAesVersion;
 
 package Plan;
 
@@ -89,6 +92,7 @@
 	$ok || ($ok = tryDmCrypt ($actions,$device,[$device,@{$working}]));
 	$ok || ($ok = tryLvm ($actions,$device,[$device,@{$working}]));
 	$ok || ($ok = tryRaid ($actions,$device,[$device,@{$working}]));
+	$ok || ($ok = tryLoop ($actions,$device,[$device,@{$working}]));
 	$ok || ($ok = tryHardware ($actions,$device,[$device,@{$working}]));
 	if (! $ok) {
 		Base::fatal ("unsupported device required: $name");
@@ -427,6 +431,412 @@
 	return 1;
 }
 
+
+#
+# tryLoop -- To start a loopback device, start the underlying hardware,
+# optionally make available a GPG encryption key by mounting (and
+# later on unmounting) the respective filesystem at boot time or
+# copying it to the image at build time, load the loop module and
+# optional encryption modules, and setup the loopback device.
+#
+sub tryLoop ($$$) {
+	my ($actions, $device, $working) = @_;
+
+	my $name = $device->name;
+	if ($name !~ /^loop\d+$/) {
+		return 0;
+	}
+	my $devno = $device->devno;
+	my ($major, $minor) = ($devno =~ /(\d+):(\d+)/);
+
+	my $loopdev = LoopTab::findByDevno ($devno);
+	if (! defined ($loopdev)) {
+		Base::fatal ("can't find Loop info for $name");
+	}
+
+	# start the underlying block device
+	my $subdev = ActiveBlockDevTab::findByPath ($loopdev->source);
+	if (! defined ($subdev)) {
+		my $source = $loopdev->source;
+		Base::fatal ("invalid block device '$source' for $name");
+	}
+	addDevicePlan ($actions, $subdev, $working);
+
+
+	#
+	# In most cases, the losetup status output only provides
+	# a partial set of the attributes required to setup a
+	# loop-AES device. For a complete set of attributes, we
+	# therefore need to parse fstab and loopaestab in addition,
+	# and all given attribute sets have to be compared and
+	# merged in a nit-picky fashion.
+	#
+
+	# complete loopback device config
+	my %loopOpts;
+
+	#
+	# Full set of possible attributes for each loop device
+	# config source, i.e. losetup info, fstab and loopaestab.
+	#
+	# For the purpose of comparison, an attribute is either
+	# marked as case sensitive (e.g. 'gpgkey' => 1) or case
+	# insensitive (e.g. 'encryption' => 0), or undefined if
+	# not having a value at all (e.g. 'gpgmount' => undef).
+	#
+	my $commonSupportedOpts = {
+		'offset' => 0,
+		'sizelimit' => 0,
+		'encryption' => 0,
+		'loinit' => 0,
+	};
+	my $losetupSupportedOpts = {
+		%{$commonSupportedOpts},
+		'multikey' => 0,
+	};
+	my $fsTabSupportedOpts = {
+		%{$commonSupportedOpts},
+		'gpgkey' => 1,
+		'gpghome' => 1,
+		'pseed' => 0,
+		'phash' => 0,
+		'itercountk' => 0,
+	};
+	my $loopAesTabSupportedOpts = {
+		%{$fsTabSupportedOpts},
+		'gpgmount' => undef,
+	};
+
+	# available loop device config entries
+	my @conf;
+
+	#
+	# All attributes shown by losetup describe the actual
+	# loopback device, so this set of attributes is made
+	# the highest-priority config source.
+	#
+	push @conf, {
+		supportedOpts => $losetupSupportedOpts,
+		opts => $loopdev->opts,
+		origin => 'losetup status',
+	};
+
+	# query fstab entry for the loopback device
+	my $fsTabEntry = FsTab::findByDevno ($loopdev->devno);
+	# ensure that fstab entry has been found via loop= option
+	if (defined ($fsTabEntry) && $fsTabEntry->opts->exists ('loop')) {
+		#
+		# The forbidden case of a loopback swap device with a
+		# random encryption key is handled within FsTab,
+		# so this does not have to be reconsidered here.
+		#
+
+		# check if underlying block device is set correctly
+		my $n = Base::devno ($fsTabEntry->dev);
+		if ((! defined ($n)) || (! ($n eq $subdev->devno))) {
+			my $origin = $fsTabEntry->origin;
+			Base::fatal ("invalid source device for $name in $origin");
+		}
+		push @conf, {
+			supportedOpts => $fsTabSupportedOpts,
+			opts => $fsTabEntry->opts,
+			origin => $fsTabEntry->origin,
+		};
+	}
+
+	# query loopaestab entry for the loopback device
+	my $loopAesTabEntry = LoopAesTab::findByDevno ($loopdev->devno);
+	if (defined ($loopAesTabEntry)) {
+		# check if underlying block device is set correctly
+		my $n = Base::devno ($loopAesTabEntry->source);
+		if ((! defined ($n)) || (! ($n eq $subdev->devno))) {
+			my $origin = $loopAesTabEntry->origin;
+			Base::fatal ("invalid source device for $name in $origin");
+		}
+		push @conf, {
+			supportedOpts => $loopAesTabSupportedOpts,
+			opts => $loopAesTabEntry->opts,
+			origin => $loopAesTabEntry->origin,
+		};
+	}
+
+	#
+	# For every distinct pair of config entries, all attributes
+	# supported by both config sources strictly have to match.
+	#
+	my @confpairs;
+	for my $i (0 .. ($#conf - 1)) {
+		for my $j (($i + 1) .. $#conf) {
+			push @confpairs, [ $conf[$i], $conf[$j] ];
+		}
+	}
+	for my $p (@confpairs) {
+		my (%count, @intersect, @conflicts);
+		my @suppOpts = map { keys (%{$_->{supportedOpts}}) } @{$p};
+
+		# determine common set of supported attributes
+		for my $opt (@suppOpts) {
+			if (++$count{$opt} == 2) {
+				push @intersect, $opt;
+			}
+		}
+
+		# basically do a case (in)sensitive hash comparison
+		for my $opt (@intersect) {
+			my @exists = map { $_->{opts}->exists ($opt) } @{$p};
+			my @val = map { $_->{opts}->get ($opt) } @{$p};
+			# case (in)sensitivity of an attribute value
+			my $case = $p->[0]{supportedOpts}{$opt};
+
+			if ($exists[0] xor $exists[1]) {
+				push @conflicts, $opt;
+			}
+			elsif (defined ($val[0]) xor defined ($val[1])) {
+				push @conflicts, $opt;
+			}
+			elsif (defined ($val[0]) && defined ($val[1])) {
+				if ($case) {
+					if ($val[0] ne $val[1]) {
+						push @conflicts, $opt;
+					}
+				}
+				else {
+					if (lc ($val[0]) ne lc ($val[1])) {
+						push @conflicts, $opt;
+					}
+				}
+			}
+		}
+		if (@conflicts) {
+			my $opt = join (", ", @conflicts);
+			my $origin = join (" and ", map { $_->{origin} } @{$p});
+			Base::fatal ("conflicting option(s) '$opt' in $origin");
+		}
+	}
+	#
+	# At this point, all config entries strictly match pertaining
+	# to their respective supported attributes, and can therefore
+	# be merged to provide a complete loopback device config.
+	#
+	for my $c (@conf) {
+		my @suppOpts = keys (%{$c->{supportedOpts}});
+
+		for my $opt (@suppOpts) {
+			my $exists = $c->{opts}->exists ($opt);
+			my $val = $c->{opts}->get ($opt);
+			my $case = $c->{supportedOpts}{$opt};
+
+			next if (! $exists);
+			if (defined ($case) && (! defined ($val))) {
+				my $origin = $c->{origin};
+				Base::fatal ("missing value for option '$opt' in $origin");
+			}
+			elsif ((! defined ($case)) && defined ($val)) {
+				my $origin = $c->{origin};
+				Base::fatal ("surplus value for option '$opt' in $origin");
+			}
+			if (! exists ($loopOpts{$opt})) {
+				$loopOpts{$opt} = $val;
+			}
+		}
+	}
+
+	# require fstab or loopaestab entry for loop-AES devices
+	if (@conf < 2 && defined ($loopOpts{encryption})) {
+		Base::fatal ("can't find fstab or loopaestab entry for loop-AES device: $name");
+	}
+
+	#
+	# Loopback device attributes specified manually in fstab
+	# or loopaestab must be checked for mutual compatibility
+	# and plausible attribute values.
+	#
+	if (! defined ($loopOpts{encryption})) {
+		my @opts = ('gpgkey', 'gpghome', 'gpgmount', 'pseed', 'phash', 'itercountk');
+		if (@opts = grep { exists ($loopOpts{$_}) } @opts) {
+			my $opt = join (', ', @opts);
+			Base::fatal ("can't use option(s) '$opt' with unencrypted loopback device: $name");
+		}
+	}
+	if (! defined ($loopOpts{gpgkey})) {
+		my @opts = ('gpghome', 'gpgmount');
+		if (@opts = grep { exists ($loopOpts{$_}) } @opts) {
+			my $opt = join (', ', @opts);
+			Base::fatal ("can't use option(s) '$opt' with password encrypted loop-AES device: $name");
+		}
+		if (exists ($loopOpts{multikey})) {
+			Base::fatal ("multi-key mode loop-AES device requires GPG key: $name");
+		}
+	}
+	if (defined ($loopOpts{phash})) {
+		if (! ($loopOpts{phash} =~ /^(sha256|sha384|sha512|rmd160)$/i)) {
+			my $value = $loopOpts{phash};
+			Base::fatal ("unsupported value 'phash=$value' for loop-AES device: $name");
+		}
+	}
+	if (defined ($loopOpts{itercountk})) {
+		if (! ($loopOpts{itercountk} =~ /^\d+$/)) {
+			my $value = $loopOpts{itercountk};
+			Base::fatal ("unsupported value 'itercountk=$value' for loop-AES device: $name");
+		}
+	}
+
+	# check existence of GPG key (and optionally GPG home directory)
+	my $gpgkey = $loopOpts{gpgkey};
+	if (defined ($gpgkey)) {
+		if (! Base::isAbsolute ($gpgkey)) {
+			Base::fatal ("GPG key file ($gpgkey) not absolute: $name");
+		}
+		if (! (-f $gpgkey)) {
+			Base::fatal ("GPG key file ($gpgkey) not a regular file: $name");
+		}
+		$gpgkey = Base::canon ($gpgkey);
+	}
+	my $gpghome = $loopOpts{gpghome};
+	if (defined ($gpghome)) {
+		if (! Base::isAbsolute ($gpghome)) {
+			Base::fatal ("GPG home directory ($gpghome) not absolute: $name");
+		}
+		if (! (-d $gpghome)) {
+			Base::fatal ("GPG home directory ($gpghome) not a directory: $name");
+		}
+		$gpghome = Base::canon ($gpghome);
+	}
+
+	#
+	# In case of encryption with a GPG key, there exist
+	# two different modes of operation:
+	#
+	# * If the option 'gpgmount' has been specified, the
+	#   filesystem containing the GPG key file (and optionally
+	#   the GPG home directory) will be mounted at boot time.
+	#
+	# * Otherwise, the GPG key file (and optionally the GPG
+	#   home directory) will be copied to the image.
+	#
+
+	my $gpgkey_mnt = undef;
+	if (exists ($loopOpts{gpgmount})) {
+		# choose a unique mount point for each loop device
+		$gpgkey_mnt = "/key-$name";
+	}
+
+	if (defined ($gpgkey) && defined ($gpgkey_mnt)) {
+		# create GPG key mount point on the image
+		$actions->add ("gpgmount", $gpgkey_mnt);
+
+		#
+		# Determine block device of GPG key file, and ensure
+		# that an optional GPG home directory lies on the same
+		# filesystem.
+		#
+		my $gpgdevno = Base::filedev ($gpgkey);
+		if (! defined ($gpgdevno)) {
+			Base::fatal ("can't determine device of GPG key file ($gpgkey): $name");
+		}
+		if (defined ($gpghome)) {
+			my $n = Base::filedev ($gpghome);
+			if (! defined ($n)) {
+				Base::fatal ("can't determine device of GPG home directory ($gpghome): $name");
+			}
+			if (! ($n eq $gpgdevno)) {
+				Base::fatal ("GPG key file ($gpgkey) and GPG home directory ($gpghome) not on the same filesystem: $name");
+			}
+		}
+
+		#
+		# Determine the fstab mount point of the block device
+		# and ensure that the GPG key file (and optionally the
+		# GPG home directory) really lies on this filesystem.
+		#
+		my $gpgfs = FsTab::findByDevno ($gpgdevno);
+		if (! defined ($gpgfs)) {
+			Base::fatal ("GPG key ($gpgkey) device not in fstab: $name");
+		}
+		# special handling of root (/) filesystem
+		(my $mnt = $gpgfs->mnt) =~ s!^/$!!;
+		if (index ($gpgkey, $mnt . "/") != $[) {
+			Base::fatal ("GPG key file ($gpgkey) filesystem not found: $name");
+		}
+		if (defined ($gpghome)) {
+			if (index ($gpghome, $mnt . "/") != $[) {
+				Base::fatal ("GPG home directory ($gpghome) filesystem not found: $name");
+			}
+		}
+
+		# replace mount points as found at boot time
+		substr ($gpgkey, $[, length ($mnt), $gpgkey_mnt);
+		if (defined ($gpghome)) {
+			substr ($gpghome, $[, length ($mnt), $gpgkey_mnt);
+		}
+
+		#
+		# Start and (read-only) mount GPG key device
+		#
+		my ($gpgkey_dev, $msg) = $gpgfs->blockDevPath();
+		if (! defined ($gpgkey_dev)) {
+			Base::fatal ($msg);
+		}
+		addBlockDevMount ($actions, $gpgkey_dev, $working, $gpgkey_mnt, 0);
+	}
+	if (defined ($gpgkey) && (! defined ($gpgkey_mnt))) {
+		# copy GPG key file (and optionally GPG home directory) to image
+		if (defined ($gpghome)) {
+			$actions->add ("gpgpublic", $gpgkey, gpghome => $gpghome);
+		}
+		else {
+			$actions->add ("gpgkey", $gpgkey);
+		}
+	}
+
+	#
+	# Load required modules, ensuring matching kernel and
+	# userspace loopback support (i.e. native or loop-AES).
+	#
+	ModProbe::addModules ($actions, [ "loop" ]);
+	if (! LoopAesVersion::matchingVersions) {
+		Base::fatal ("loop kernel module and losetup program versions mismatch");
+	}
+	my $encryption = $loopOpts{encryption};
+	if (defined ($encryption)) {
+		if ($encryption =~ /^(twofish|blowfish|serpent)\d+$/i) {
+			ModProbe::addModules ($actions, [ "loop_" . lc ($1) ]);
+		}
+		elsif (! ($encryption =~ /^(aes\d*|xor)$/i)) {
+			Base::fatal ("unsupported encryption type ($encryption): '$name'");
+		}
+	}
+
+	# load keymap to allow proper password entry
+	if (defined ($encryption)) {
+		$actions->add ("loadkeys", "loadkeys");
+	}
+
+	# setup loopback device
+	$device->setCreator ("losetup");
+	$actions->add ("losetup", $device->yspecial,
+		major => $major,
+		minor => $minor,
+		offset => $loopOpts{offset},
+		sizelimit => $loopOpts{sizelimit},
+		encryption => $loopOpts{encryption},
+		loinit => $loopOpts{loinit},
+		gpgkey => $gpgkey,
+		gpghome => $gpghome,
+		pseed => $loopOpts{pseed},
+		phash => $loopOpts{phash},
+		itercountk => $loopOpts{itercountk},
+		device => $subdev->yspecial,
+	);
+
+	# unmount GPG key device if appropriate
+	if (defined ($gpgkey_mnt)) {
+		addUnmount ($actions, $gpgkey_mnt);
+	}
+
+	return 1;
+}
+
 #
 # tryEvms Look if the device could be an evms one
 #
diff -urN yaird-0.0.12.orig/perl/TestSet.pm yaird-0.0.12/perl/TestSet.pm
--- yaird-0.0.12.orig/perl/TestSet.pm	2006-10-26 15:41:37.000000000 +0200
+++ yaird-0.0.12/perl/TestSet.pm	2006-10-26 15:45:01.000000000 +0200
@@ -29,6 +29,7 @@
 use LvmTab;
 use Hardware;
 use RaidTab;
+use LoopTab;
 use EvmsTab;
 use InputTab;
 use Image;
@@ -132,6 +133,14 @@
 	}
 }
 
+sub testLoopDevices () {
+	print "Loopback devices:\n";
+	for my $ld (@{LoopTab::all()}) {
+		my $str = $ld->string;
+		print "\t$str\n";
+	}
+}
+
 sub testEvms () {
 	print "Evms devices:\n";
 	for my $ev (@{EvmsTab::all()}) {
@@ -231,6 +240,7 @@
 	testLvm ();
 	testHardware ();
 	testRaidDevices();
+	testLoopDevices();
 	testInterpretation ();
 	testInput ();
 	testKconfig ();
diff -urN yaird-0.0.12.orig/templates/Debian-initrd.cfg yaird-0.0.12/templates/Debian-initrd.cfg
--- yaird-0.0.12.orig/templates/Debian-initrd.cfg	2006-10-26 15:44:48.000000000 +0200
+++ yaird-0.0.12/templates/Debian-initrd.cfg	2006-10-26 15:45:01.000000000 +0200
@@ -300,6 +300,124 @@
 		END SCRIPT
 	END TEMPLATE
 
+
+	#
+	# Load keymap upon boot, allowing proper password entry
+	# for encrypted devices.
+	#
+	TEMPLATE loadkeys
+	BEGIN
+		FILE "/bin/loadkeys"
+		FILE "/bin/gunzip"
+		FILE "/etc/console/boottime.kmap.gz"
+		SCRIPT "/sbin/init"
+		BEGIN
+			!# loadkeys from the kbd package has problems
+			!# uncompressing the keymap on-the-fly when
+			!# run from initramfs, don't know why
+			!gunzip /etc/console/boottime.kmap.gz
+			!loadkeys /etc/console/boottime.kmap
+		END SCRIPT
+	END TEMPLATE
+
+
+	#
+	# Include GPG program and GPG key file.
+	#
+	TEMPLATE gpgkey
+	BEGIN
+		FILE "/usr/bin/gpg"
+		FILE "<TMPL_VAR NAME=target>"
+	END TEMPLATE
+
+
+	#
+	# Include GPG program, GPG key file and GPG home directory.
+	#
+	TEMPLATE gpgpublic
+	BEGIN
+		FILE "/usr/bin/gpg"
+		FILE "<TMPL_VAR NAME=target>"
+		TREE "<TMPL_VAR NAME=gpghome>"
+	END TEMPLATE
+
+
+	#
+	# Include GPG program and create GPG key device mount point.
+	#
+	TEMPLATE gpgmount
+	BEGIN
+		FILE "/usr/bin/gpg"
+		DIRECTORY "<TMPL_VAR NAME=target>"
+	END TEMPLATE
+
+
+	#
+	# Setup plain loopback or loop-AES encrypted device.
+	#
+	# common losetup arguments:
+	# - offset
+	# - device (underlying block device)
+	#
+	# loop-AES specific losetup arguments:
+	# - encryption
+	# - loinit
+	# - sizelimit
+	# - gpgkey
+	# - gpghome
+	# - pseed
+	# - phash
+	# - itercountk
+	#
+	TEMPLATE losetup
+	BEGIN
+		FILE "/sbin/losetup"
+		SCRIPT "/sbin/init"
+		BEGIN
+			!mknod <TMPL_VAR NAME=target> b <TMPL_VAR NAME=major> <TMPL_VAR NAME=minor>
+			!DOCRYPT=1
+			!while [ "$DOCRYPT" != "0" ]; do
+			!	<TMPL_IF NAME=encryption>
+			!		echo "Encrypted device ('<TMPL_VAR NAME=device>'), please supply passphrase"
+			!	</TMPL_IF>
+			!	losetup \
+			!		<TMPL_IF NAME=encryption> \
+			!			-e <TMPL_VAR NAME=encryption> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=loinit> \
+			!			-I <TMPL_VAR NAME=loinit> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=offset> \
+			!			-o <TMPL_VAR NAME=offset> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=sizelimit> \
+			!			-s <TMPL_VAR NAME=sizelimit> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=gpgkey> \
+			!			-K <TMPL_VAR NAME=gpgkey> \
+			!			<TMPL_IF NAME=gpghome> \
+			!				-G <TMPL_VAR NAME=gpghome> \
+			!			<TMPL_ELSE> \
+			!				-G /nonexistent \
+			!			</TMPL_IF> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=pseed> \
+			!			-S <TMPL_VAR NAME=pseed> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=phash> \
+			!			-H <TMPL_VAR NAME=phash> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=itercountk> \
+			!			-C <TMPL_VAR NAME=itercountk> \
+			!		</TMPL_IF> \
+			!		<TMPL_VAR NAME=target> \
+			!		<TMPL_VAR NAME=device>
+			!	DOCRYPT=$?
+			!done
+		END SCRIPT
+	END TEMPLATE
+
+
 	#
 	# cryptsetup arguments:
 	# - target
diff -urN yaird-0.0.12.orig/templates/Debian.cfg yaird-0.0.12/templates/Debian.cfg
--- yaird-0.0.12.orig/templates/Debian.cfg	2006-10-26 15:44:48.000000000 +0200
+++ yaird-0.0.12/templates/Debian.cfg	2006-10-26 15:45:01.000000000 +0200
@@ -329,6 +329,123 @@
 
 
 	#
+	# Load keymap upon boot, allowing proper password entry
+	# for encrypted devices.
+	#
+	TEMPLATE loadkeys
+	BEGIN
+		FILE "/bin/loadkeys"
+		FILE "/bin/gunzip"
+		FILE "/etc/console/boottime.kmap.gz"
+		SCRIPT "/init"
+		BEGIN
+			!# loadkeys from the kbd package has problems
+			!# uncompressing the keymap on-the-fly when
+			!# run from initramfs, don't know why
+			!gunzip /etc/console/boottime.kmap.gz
+			!loadkeys /etc/console/boottime.kmap
+		END SCRIPT
+	END TEMPLATE
+
+
+	#
+	# Include GPG program and GPG key file.
+	#
+	TEMPLATE gpgkey
+	BEGIN
+		FILE "/usr/bin/gpg"
+		FILE "<TMPL_VAR NAME=target>"
+	END TEMPLATE
+
+
+	#
+	# Include GPG program, GPG key file and GPG home directory.
+	#
+	TEMPLATE gpgpublic
+	BEGIN
+		FILE "/usr/bin/gpg"
+		FILE "<TMPL_VAR NAME=target>"
+		TREE "<TMPL_VAR NAME=gpghome>"
+	END TEMPLATE
+
+
+	#
+	# Include GPG program and create GPG key device mount point.
+	#
+	TEMPLATE gpgmount
+	BEGIN
+		FILE "/usr/bin/gpg"
+		DIRECTORY "<TMPL_VAR NAME=target>"
+	END TEMPLATE
+
+
+	#
+	# Setup plain loopback or loop-AES encrypted device.
+	#
+	# common losetup arguments:
+	# - offset
+	# - device (underlying block device)
+	#
+	# loop-AES specific losetup arguments:
+	# - encryption
+	# - loinit
+	# - sizelimit
+	# - gpgkey
+	# - gpghome
+	# - pseed
+	# - phash
+	# - itercountk
+	#
+	TEMPLATE losetup
+	BEGIN
+		FILE "/sbin/losetup"
+		SCRIPT "/init"
+		BEGIN
+			!mknod <TMPL_VAR NAME=target> b <TMPL_VAR NAME=major> <TMPL_VAR NAME=minor>
+			!DOCRYPT=1
+			!while [ "$DOCRYPT" != "0" ]; do
+			!	<TMPL_IF NAME=encryption>
+			!		echo "Encrypted device ('<TMPL_VAR NAME=device>'), please supply passphrase"
+			!	</TMPL_IF>
+			!	losetup \
+			!		<TMPL_IF NAME=encryption> \
+			!			-e <TMPL_VAR NAME=encryption> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=loinit> \
+			!			-I <TMPL_VAR NAME=loinit> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=offset> \
+			!			-o <TMPL_VAR NAME=offset> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=sizelimit> \
+			!			-s <TMPL_VAR NAME=sizelimit> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=gpgkey> \
+			!			-K <TMPL_VAR NAME=gpgkey> \
+			!			<TMPL_IF NAME=gpghome> \
+			!				-G <TMPL_VAR NAME=gpghome> \
+			!			<TMPL_ELSE> \
+			!				-G /nonexistent \
+			!			</TMPL_IF> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=pseed> \
+			!			-S <TMPL_VAR NAME=pseed> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=phash> \
+			!			-H <TMPL_VAR NAME=phash> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=itercountk> \
+			!			-C <TMPL_VAR NAME=itercountk> \
+			!		</TMPL_IF> \
+			!		<TMPL_VAR NAME=target> \
+			!		<TMPL_VAR NAME=device>
+			!	DOCRYPT=$?
+			!done
+		END SCRIPT
+	END TEMPLATE
+
+
+	#
 	# cryptsetup arguments:
 	# - target
 	# - cipher
diff -urN yaird-0.0.12.orig/templates/Fedora.cfg yaird-0.0.12/templates/Fedora.cfg
--- yaird-0.0.12.orig/templates/Fedora.cfg	2006-10-26 15:44:48.000000000 +0200
+++ yaird-0.0.12/templates/Fedora.cfg	2006-10-26 15:45:01.000000000 +0200
@@ -340,6 +340,123 @@
 
 
 	#
+	# Load keymap upon boot, allowing proper password entry
+	# for encrypted devices.
+	#
+	TEMPLATE loadkeys
+	BEGIN
+		FILE "/bin/loadkeys"
+		FILE "/bin/gunzip"
+		FILE "/etc/console/boottime.kmap.gz"
+		SCRIPT "/init"
+		BEGIN
+			!# loadkeys from the kbd package has problems
+			!# uncompressing the keymap on-the-fly when
+			!# run from initramfs, don't know why
+			!gunzip /etc/console/boottime.kmap.gz
+			!loadkeys /etc/console/boottime.kmap
+		END SCRIPT
+	END TEMPLATE
+
+
+	#
+	# Include GPG program and GPG key file.
+	#
+	TEMPLATE gpgkey
+	BEGIN
+		FILE "/usr/bin/gpg"
+		FILE "<TMPL_VAR NAME=target>"
+	END TEMPLATE
+
+
+	#
+	# Include GPG program, GPG key file and GPG home directory.
+	#
+	TEMPLATE gpgpublic
+	BEGIN
+		FILE "/usr/bin/gpg"
+		FILE "<TMPL_VAR NAME=target>"
+		TREE "<TMPL_VAR NAME=gpghome>"
+	END TEMPLATE
+
+
+	#
+	# Include GPG program and create GPG key device mount point.
+	#
+	TEMPLATE gpgmount
+	BEGIN
+		FILE "/usr/bin/gpg"
+		DIRECTORY "<TMPL_VAR NAME=target>"
+	END TEMPLATE
+
+
+	#
+	# Setup plain loopback or loop-AES encrypted device.
+	#
+	# common losetup arguments:
+	# - offset
+	# - device (underlying block device)
+	#
+	# loop-AES specific losetup arguments:
+	# - encryption
+	# - loinit
+	# - sizelimit
+	# - gpgkey
+	# - gpghome
+	# - pseed
+	# - phash
+	# - itercountk
+	#
+	TEMPLATE losetup
+	BEGIN
+		FILE "/sbin/losetup"
+		SCRIPT "/init"
+		BEGIN
+			!mknod <TMPL_VAR NAME=target> b <TMPL_VAR NAME=major> <TMPL_VAR NAME=minor>
+			!DOCRYPT=1
+			!while [ "$DOCRYPT" != "0" ]; do
+			!	<TMPL_IF NAME=encryption>
+			!		echo "Encrypted device ('<TMPL_VAR NAME=device>'), please supply passphrase"
+			!	</TMPL_IF>
+			!	losetup \
+			!		<TMPL_IF NAME=encryption> \
+			!			-e <TMPL_VAR NAME=encryption> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=loinit> \
+			!			-I <TMPL_VAR NAME=loinit> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=offset> \
+			!			-o <TMPL_VAR NAME=offset> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=sizelimit> \
+			!			-s <TMPL_VAR NAME=sizelimit> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=gpgkey> \
+			!			-K <TMPL_VAR NAME=gpgkey> \
+			!			<TMPL_IF NAME=gpghome> \
+			!				-G <TMPL_VAR NAME=gpghome> \
+			!			<TMPL_ELSE> \
+			!				-G /nonexistent \
+			!			</TMPL_IF> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=pseed> \
+			!			-S <TMPL_VAR NAME=pseed> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=phash> \
+			!			-H <TMPL_VAR NAME=phash> \
+			!		</TMPL_IF> \
+			!		<TMPL_IF NAME=itercountk> \
+			!			-C <TMPL_VAR NAME=itercountk> \
+			!		</TMPL_IF> \
+			!		<TMPL_VAR NAME=target> \
+			!		<TMPL_VAR NAME=device>
+			!	DOCRYPT=$?
+			!done
+		END SCRIPT
+	END TEMPLATE
+
+
+	#
 	# cryptsetup arguments:
 	# - target
 	# - cipher
-------------- next part --------------
diff -urN yaird-0.0.12.orig/perl/Plan.pm yaird-0.0.12/perl/Plan.pm
--- yaird-0.0.12.orig/perl/Plan.pm	2006-10-26 19:30:45.000000000 +0200
+++ yaird-0.0.12/perl/Plan.pm	2006-10-26 19:32:13.000000000 +0200
@@ -623,8 +623,8 @@
 # addBlockDevMount -- add list of actions to mount named device
 # at mountPoint: activate device, activate fstype, do mount.
 #
-sub addBlockDevMount ($$$) {
-	my ($actions, $rootDevName, $mountPoint) = @_;
+sub addBlockDevMount ($$$$$) {
+	my ($actions, $rootDevName, $working, $mountPoint, $isRoot) = @_;
 
 	#
 	# Device must be in fstab, to determine options
@@ -644,7 +644,7 @@
 		Base::fatal ("block device '$rootDevName' unavailable ($origin)");
 	}
 
-	addDevicePlan ($actions, $abd, []);
+	addDevicePlan ($actions, $abd, $working);
 
 	#
 	# Do the file system type.  This may be a list of types, to be tried
@@ -687,11 +687,12 @@
 	my $yspecial = $abd->yspecial();
 	my $opts = $root->opts->cmdLineVersion();
 
-	# XXX - isRoot should be readOnly, and configurable.
+	# mount devices explicitly specified as non-root read-only
 	$actions->add ("mount", $mountPoint,
 		options => $opts,
 		fsType => $fsType,
-		isRoot => 1,
+		isRoot => $isRoot,
+		readOnly => (! $isRoot),
 		device => $yspecial);
 }
 
@@ -702,8 +703,8 @@
 # on the initial file system.  the fsTabEntry must correspond
 # to a block device: NFS not supported here.
 #
-sub addFsTabMount ($$$) {
-	my ($actions, $fsTabEntry, $mountPoint) = @_;
+sub addFsTabMount ($$$$$) {
+	my ($actions, $fsTabEntry, $working, $mountPoint, $isRoot) = @_;
 	my $root = FsTab::findByMountPoint($fsTabEntry);
 	if (! defined ($root)) {
 		Base::fatal ("can't find $fsTabEntry in fstab");
@@ -713,7 +714,15 @@
 	if (! defined ($blockDevName)) {
 		Base::fatal ($msg);
 	}
-	addBlockDevMount ($actions, $blockDevName, $mountPoint);
+	addBlockDevMount ($actions, $blockDevName, $working, $mountPoint, $isRoot);
+}
+
+#
+# addUnmount -- detach device mounted at mountPoint
+#
+sub addUnmount ($$) {
+	my ($actions, $mountPoint) = @_;
+	$actions->add ("umount", $mountPoint);
 }
 
 #
@@ -787,12 +796,12 @@
 		elsif ($type eq 'mountdir') {
 			my $mountPoint = $goal->{mountPoint};
 			Base::assert (defined ($mountPoint));
-			addFsTabMount ($actions, $value, $mountPoint);
+			addFsTabMount ($actions, $value, [], $mountPoint, 1);
 		}
 		elsif ($type eq 'mountdev') {
 			my $mountPoint = $goal->{mountPoint};
 			Base::assert (defined ($mountPoint));
-			addBlockDevMount ($actions, $value, $mountPoint);
+			addBlockDevMount ($actions, $value, [], $mountPoint, 1);
 		}
 		else {
 			Base::fatal ("Unknown goal");
diff -urN yaird-0.0.12.orig/templates/Debian-initrd.cfg yaird-0.0.12/templates/Debian-initrd.cfg
--- yaird-0.0.12.orig/templates/Debian-initrd.cfg	2006-10-26 19:30:45.000000000 +0200
+++ yaird-0.0.12/templates/Debian-initrd.cfg	2006-10-26 19:30:55.000000000 +0200
@@ -392,6 +392,7 @@
 		BEGIN
 			!/bin/mount -n \
 			!	<TMPL_IF NAME=isRoot>$ro</TMPL_IF> \
+			!	<TMPL_IF NAME=readOnly>-r</TMPL_IF> \
 			!	-t <TMPL_VAR NAME=fsType> \
 			!	<TMPL_VAR NAME=options> \
 			!	'<TMPL_VAR NAME=device>' \
@@ -399,6 +400,14 @@
 		END SCRIPT
 	END TEMPLATE
 
+	TEMPLATE umount
+	BEGIN
+		SCRIPT "/sbin/init"
+		BEGIN
+			!/bin/umount -n <TMPL_VAR NAME=target>
+		END SCRIPT
+	END TEMPLATE
+
 	TEMPLATE nfsstart
 	BEGIN
 		FILE "<TMPL_VAR NAME=auxDir>/trynfs"
diff -urN yaird-0.0.12.orig/templates/Debian.cfg yaird-0.0.12/templates/Debian.cfg
--- yaird-0.0.12.orig/templates/Debian.cfg	2006-10-26 19:30:45.000000000 +0200
+++ yaird-0.0.12/templates/Debian.cfg	2006-10-26 19:30:55.000000000 +0200
@@ -457,6 +457,7 @@
 		BEGIN
 			!/bin/mount -n \
 			!	<TMPL_IF NAME=isRoot>$ro</TMPL_IF> \
+			!	<TMPL_IF NAME=readOnly>-r</TMPL_IF> \
 			!	-t <TMPL_VAR NAME=fsType> \
 			!	<TMPL_VAR NAME=options> \
 			!	'<TMPL_VAR NAME=device>' \
@@ -464,6 +465,14 @@
 		END SCRIPT
 	END TEMPLATE
 
+	TEMPLATE umount
+	BEGIN
+		SCRIPT "/init"
+		BEGIN
+			!/bin/umount -n <TMPL_VAR NAME=target>
+		END SCRIPT
+	END TEMPLATE
+
 	TEMPLATE nfsstart
 	BEGIN
 		FILE "<TMPL_VAR NAME=auxDir>/trynfs"
diff -urN yaird-0.0.12.orig/templates/Fedora.cfg yaird-0.0.12/templates/Fedora.cfg
--- yaird-0.0.12.orig/templates/Fedora.cfg	2006-10-26 19:30:45.000000000 +0200
+++ yaird-0.0.12/templates/Fedora.cfg	2006-10-26 19:30:55.000000000 +0200
@@ -468,6 +468,7 @@
 		BEGIN
 			!/bin/mount -n \
 			!	<TMPL_IF NAME=isRoot>$ro</TMPL_IF> \
+			!	<TMPL_IF NAME=readOnly>-r</TMPL_IF> \
 			!	-t <TMPL_VAR NAME=fsType> \
 			!	<TMPL_VAR NAME=options> \
 			!	'<TMPL_VAR NAME=device>' \
@@ -476,6 +477,15 @@
 	END TEMPLATE
 
 
+	TEMPLATE umount
+	BEGIN
+		SCRIPT "/init"
+		BEGIN
+			!/bin/umount -n <TMPL_VAR NAME=target>
+		END SCRIPT
+	END TEMPLATE
+
+
 	TEMPLATE nfsstart
 	BEGIN
 		FILE "<TMPL_VAR NAME=auxDir>/trynfs"
-------------- next part --------------
diff -u yaird-0.0.12/perl/FsEntry.pm yaird-0.0.12/perl/FsEntry.pm
--- yaird-0.0.12/perl/FsEntry.pm	2006-10-25 10:25:51.000000000 +0200
+++ yaird-0.0.12/perl/FsEntry.pm	2006-10-26 15:53:47.000000000 +0200
@@ -83,7 +83,7 @@
 	my $msg = undef;
 
 	if ($dev =~  /^\//) {
-		if ($self->opts->exists('loop') && $self->type eq 'swap') {
+		if ($self->opts->exists ('loop') && $self->type eq 'swap') {
 			$msg = "loop option for swap device '$dev' not supported ($origin)";
 			return (undef, $msg);
 		}
@@ -92,6 +92,10 @@
 			return (undef, $msg);
 		}
 
+		if ($self->opts->exists ('loop')) {
+			$dev = $self->opts->get ('loop');
+		}
+
 		if (! -e $dev) {
 			$msg = "'$dev' not found ($origin)";
 			return (undef, $msg);
diff -u yaird-0.0.12/perl/Plan.pm yaird-0.0.12/perl/Plan.pm
--- yaird-0.0.12/perl/Plan.pm	2006-10-25 10:25:51.000000000 +0200
+++ yaird-0.0.12/perl/Plan.pm	2006-10-26 16:09:02.000000000 +0200
@@ -521,24 +521,21 @@
 		origin => 'losetup status',
 	};
 
-	# query fstab entry for the underlying block device
-	my $fsTabEntry = FsTab::findByDevno ($subdev->devno);
-	if (defined ($fsTabEntry)) {
+	# query fstab entry for the loopback device
+	my $fsTabEntry = FsTab::findByDevno ($loopdev->devno);
+	# ensure that fstab entry has been found via loop= option
+	if (defined ($fsTabEntry) && $fsTabEntry->opts->exists ('loop')) {
 		#
 		# The forbidden case of a loopback swap device with a
 		# random encryption key is handled within FsTab,
 		# so this does not have to be reconsidered here.
 		#
 
-		# check if loopback device is set correctly
-		if (! $fsTabEntry->opts->exists ("loop")) {
-			my $origin = $fsTabEntry->origin;
-			Base::fatal ("missing loop option in $origin");
-		}
-		my $n = Base::devno ($fsTabEntry->opts->get ("loop"));
-		if ((! defined ($n)) || (! ($n eq $devno))) {
+		# check if underlying block device is set correctly
+		my $n = Base::devno ($fsTabEntry->dev);
+		if ((! defined ($n)) || (! ($n eq $subdev->devno))) {
 			my $origin = $fsTabEntry->origin;
-			Base::fatal ("invalid loop option in $origin");
+			Base::fatal ("invalid source device for $name in $origin");
 		}
 		push @conf, {
 			supportedOpts => $fsTabSupportedOpts,
@@ -554,7 +551,7 @@
 		my $n = Base::devno ($loopAesTabEntry->source);
 		if ((! defined ($n)) || (! ($n eq $subdev->devno))) {
 			my $origin = $loopAesTabEntry->origin;
-			Base::fatal ("invalid source device in $origin");
+			Base::fatal ("invalid source device for $name in $origin");
 		}
 		push @conf, {
 			supportedOpts => $loopAesTabSupportedOpts,
@@ -567,54 +564,53 @@
 	# For every distinct pair of config entries, all attributes
 	# supported by both config sources strictly have to match.
 	#
+	my @confpairs;
 	for my $i (0 .. ($#conf - 1)) {
 		for my $j (($i + 1) .. $#conf) {
-			my (%count, @intersect, @conflicts);
-			my @suppOpts1 = keys (%{$conf[$i]->{supportedOpts}});
-			my @suppOpts2 = keys (%{$conf[$j]->{supportedOpts}});
-
-			# determine common set of supported attributes
-			for my $opt (@suppOpts1, @suppOpts2) {
-				if (++$count{$opt} == 2) {
-					push @intersect, $opt;
-				}
-			}
+			push @confpairs, [ $conf[$i], $conf[$j] ];
+		}
+	}
+	for my $p (@confpairs) {
+		my (%count, @intersect, @conflicts);
+		my @suppOpts = map { keys (%{$_->{supportedOpts}}) } @{$p};
 
-			# basically do a case (in)sensitive hash comparison
-			for my $opt (@intersect) {
-				my $exists1 = $conf[$i]->{opts}->exists ($opt);
-				my $exists2 = $conf[$j]->{opts}->exists ($opt);
-				my $val1 = $conf[$i]->{opts}->get ($opt);
-				my $val2 = $conf[$j]->{opts}->get ($opt);
+		# determine common set of supported attributes
+		for my $opt (@suppOpts) {
+			if (++$count{$opt} == 2) {
+				push @intersect, $opt;
+			}
+		}
 
-				# case (in)sensitivity of an attribute value
-				my $case = $conf[$i]->{supportedOpts}{$opt};
+		# basically do a case (in)sensitive hash comparison
+		for my $opt (@intersect) {
+			my @exists = map { $_->{opts}->exists ($opt) } @{$p};
+			my @val = map { $_->{opts}->get ($opt) } @{$p};
+			# case (in)sensitivity of an attribute value
+			my $case = $p->[0]{supportedOpts}{$opt};
 
-				if ($exists1 xor $exists2) {
-					push @conflicts, $opt;
-				}
-				elsif (defined ($val1) xor defined ($val2)) {
-					push @conflicts, $opt;
-				}
-				elsif (defined ($val1) && defined ($val2)) {
-					if ($case) {
-						if ($val1 ne $val2) {
-							push @conflicts, $opt;
-						}
+			if ($exists[0] xor $exists[1]) {
+				push @conflicts, $opt;
+			}
+			elsif (defined ($val[0]) xor defined ($val[1])) {
+				push @conflicts, $opt;
+			}
+			elsif (defined ($val[0]) && defined ($val[1])) {
+				if ($case) {
+					if ($val[0] ne $val[1]) {
+						push @conflicts, $opt;
 					}
-					else {
-						if (lc ($val1) ne lc ($val2)) {
-							push @conflicts, $opt;
-						}
+				}
+				else {
+					if (lc ($val[0]) ne lc ($val[1])) {
+						push @conflicts, $opt;
 					}
 				}
 			}
-			if (@conflicts) {
-				my $opt = join (', ', @conflicts);
-				my $origin1 = $conf[$i]->{origin};
-				my $origin2 = $conf[$j]->{origin};
-				Base::fatal ("conflicting option(s) '$opt' in $origin1 and $origin2");
-			}
+		}
+		if (@conflicts) {
+			my $opt = join (", ", @conflicts);
+			my $origin = join (" and ", map { $_->{origin} } @{$p});
+			Base::fatal ("conflicting option(s) '$opt' in $origin");
 		}
 	}
 	#
@@ -637,7 +633,7 @@
 			}
 			elsif ((! defined ($case)) && defined ($val)) {
 				my $origin = $c->{origin};
-				Base::fatal ("found value for keyword option '$opt' in $origin");
+				Base::fatal ("surplus value for option '$opt' in $origin");
 			}
 			if (! exists ($loopOpts{$opt})) {
 				$loopOpts{$opt} = $val;
@@ -719,8 +715,11 @@
 	#   home directory) will be copied to the image.
 	#
 
-	# choose a unique mount point for each loop device
-	my $gpgkey_mnt = (exists ($loopOpts{gpgmount}) ? "/.$name" : undef);
+	my $gpgkey_mnt = undef;
+	if (exists ($loopOpts{gpgmount})) {
+		# choose a unique mount point for each loop device
+		$gpgkey_mnt = "/key-$name";
+	}
 
 	if (defined ($gpgkey) && defined ($gpgkey_mnt)) {
 		# create GPG key mount point on the image
@@ -754,47 +753,31 @@
 		if (! defined ($gpgfs)) {
 			Base::fatal ("GPG key ($gpgkey) device not in fstab: $name");
 		}
-		my $gpgkey_dev = $gpgfs->dev;
-		#
-		# If it's mounted via a loop device, use that as GPG key device
-		#
-		if ($gpgfs->opts->exists ('loop')) {
-			$gpgkey_dev = $gpgfs->opts->get ('loop');
-		}
-		my $gk = $gpgkey;
-		my $gh = $gpghome;
-		my $fsmnt = $gpgfs->mnt;
-		$gk =~ s/^$fsmnt//;
-		if (defined ($gh)) {
-			$gh =~ s/^$fsmnt//;
-		}
-		if (! ($gpgkey eq Base::canon ($fsmnt . "/" . $gk))) {
+		# special handling of root (/) filesystem
+		(my $mnt = $gpgfs->mnt) =~ s!^/$!!;
+		if (index ($gpgkey, $mnt . "/") != $[) {
 			Base::fatal ("GPG key file ($gpgkey) filesystem not found: $name");
 		}
-		if (defined ($gpghome) && (! ($gpghome eq Base::canon ($fsmnt . "/" . $gh)))) {
-			Base::fatal ("GPG home directory ($gpghome) filesystem not found: $name");
+		if (defined ($gpghome)) {
+			if (index ($gpghome, $mnt . "/") != $[) {
+				Base::fatal ("GPG home directory ($gpghome) filesystem not found: $name");
+			}
 		}
 
-		# construct absolute paths as found at boot time.
-		$gpgkey = Base::canon ($gpgkey_mnt . "/" . $gk);
+		# replace mount points as found at boot time
+		substr ($gpgkey, $[, length ($mnt), $gpgkey_mnt);
 		if (defined ($gpghome)) {
-			$gpghome = Base::canon ($gpgkey_mnt . "/" . $gh);
+			substr ($gpghome, $[, length ($mnt), $gpgkey_mnt);
 		}
 
 		#
 		# Start and (read-only) mount GPG key device
 		#
-		# (Manually adding the device plan before mounting ensures
-		# that potential loops are detected. A loop might occur
-		# if the GPG key file erroneously lies on the encrypted
-		# loopback device itself.)
-		#
-		my $gpgdev = ActiveBlockDevTab::findByPath ($gpgkey_dev);
-		if (! defined ($gpgdev)) {
-			Base::fatal ("can't find block device '$gpgkey_dev' in fstab");
+		my ($gpgkey_dev, $msg) = $gpgfs->blockDevPath();
+		if (! defined ($gpgkey_dev)) {
+			Base::fatal ($msg);
 		}
-		addDevicePlan ($actions, $gpgdev, $working);
-		addBlockDevMount ($actions, $gpgkey_dev, $gpgkey_mnt, 0);
+		addBlockDevMount ($actions, $gpgkey_dev, $working, $gpgkey_mnt, 0);
 	}
 	if (defined ($gpgkey) && (! defined ($gpgkey_mnt))) {
 		# copy GPG key file (and optionally GPG home directory) to image
@@ -1062,13 +1045,6 @@
 	}
 
 	#
-	# If it's mounted via a loop device, use that as root device
-	#
-	if ($root->opts->exists ('loop')) {
-		$rootDevName = $root->opts->get ('loop');
-	}
-
-	#
 	# and device must be in /dev, to determine whether
 	# it's raid, lvm, scsi or whatever.
 	#
-------------- next part --------------
diff -u yaird-0.0.12/perl/Plan.pm yaird-0.0.12/perl/Plan.pm
--- yaird-0.0.12/perl/Plan.pm	2006-10-15 21:25:09.000000000 +0200
+++ yaird-0.0.12/perl/Plan.pm	2006-10-26 19:32:13.000000000 +0200
@@ -623,8 +623,8 @@
 # addBlockDevMount -- add list of actions to mount named device
 # at mountPoint: activate device, activate fstype, do mount.
 #
-sub addBlockDevMount ($$$$) {
-	my ($actions, $rootDevName, $mountPoint, $isRoot) = @_;
+sub addBlockDevMount ($$$$$) {
+	my ($actions, $rootDevName, $working, $mountPoint, $isRoot) = @_;
 
 	#
 	# Device must be in fstab, to determine options
@@ -644,7 +644,7 @@
 		Base::fatal ("block device '$rootDevName' unavailable ($origin)");
 	}
 
-	addDevicePlan ($actions, $abd, []);
+	addDevicePlan ($actions, $abd, $working);
 
 	#
 	# Do the file system type.  This may be a list of types, to be tried
@@ -703,8 +703,8 @@
 # on the initial file system.  the fsTabEntry must correspond
 # to a block device: NFS not supported here.
 #
-sub addFsTabMount ($$$) {
-	my ($actions, $fsTabEntry, $mountPoint) = @_;
+sub addFsTabMount ($$$$$) {
+	my ($actions, $fsTabEntry, $working, $mountPoint, $isRoot) = @_;
 	my $root = FsTab::findByMountPoint($fsTabEntry);
 	if (! defined ($root)) {
 		Base::fatal ("can't find $fsTabEntry in fstab");
@@ -714,7 +714,7 @@
 	if (! defined ($blockDevName)) {
 		Base::fatal ($msg);
 	}
-	addBlockDevMount ($actions, $blockDevName, $mountPoint, 1);
+	addBlockDevMount ($actions, $blockDevName, $working, $mountPoint, $isRoot);
 }
 
 #
@@ -796,12 +796,12 @@
 		elsif ($type eq 'mountdir') {
 			my $mountPoint = $goal->{mountPoint};
 			Base::assert (defined ($mountPoint));
-			addFsTabMount ($actions, $value, $mountPoint);
+			addFsTabMount ($actions, $value, [], $mountPoint, 1);
 		}
 		elsif ($type eq 'mountdev') {
 			my $mountPoint = $goal->{mountPoint};
 			Base::assert (defined ($mountPoint));
-			addBlockDevMount ($actions, $value, $mountPoint, 1);
+			addBlockDevMount ($actions, $value, [], $mountPoint, 1);
 		}
 		else {
 			Base::fatal ("Unknown goal");


More information about the Yaird-devel mailing list