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

Peter Colberg peterco at gmx.net
Wed Oct 25 12:26:44 CEST 2006


One should never promise a patch to be the last one...

Following up with a few more changes to see how long *this* patch
will be able to hang around here without being altered again, and
to keep me from tinkering with it any more.

=> 1106_add_loopback_and_loopaes_with_loopaestab_support_v4.patch

   - LoopAesEntry, LoopAesTab, LoopDev, LoopTab:
     Add original based-upon source file to copyright notice.

   - LoopAesVersion::probeLoopAesModule:
     Remove unnecessary test for string 'loop_compute_md5_iv', as
     it's a substring of 'loop_compute_md5_iv_v3'.

   - LoopAesVersion::grepBinary:
     Natively implement search for strings in binary file.

   - LoopTab::init:
     Only call LoopAesVersion::probeLoopAesSetup if an active loopback
     device has actually been detected, ensuring that a valid, working
     losetup binary is available.

   - LoopTab::processLine:
     Do not fail on parsing non-device-backed loopback devices.
     File-backed devices could be available at image built time
     without actually being used.

   - Plan::tryLoop:
     Check if the loop device source is an active block device.
     This is the correct place to prevent use of file-backed devices.

     Improve option processing to differentiate between keyword and
     non-keyword attributes, and check if a value to an option is
     missing (non-keyword case) or not allowed (keyword case).

   - Formatting and whitespace...


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	2005-12-08 23:42:33.000000000 +0100
+++ yaird-0.0.12/perl/ActiveBlockDev.pm	2006-10-25 10:25:51.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);
--- yaird-0.0.12.orig/perl/Conf.pm.in	2005-12-08 23:42:33.000000000 +0100
+++ yaird-0.0.12/perl/Conf.pm.in	2006-10-25 10:25:51.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@"; },
--- yaird-0.0.12.orig/perl/FsEntry.pm	2005-12-08 23:42:33.000000000 +0100
+++ yaird-0.0.12/perl/FsEntry.pm	2006-10-25 10:25:51.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") {
--- yaird-0.0.12.orig/perl/FsOpts.pm	2005-12-08 23:42:33.000000000 +0100
+++ yaird-0.0.12/perl/FsOpts.pm	2006-10-25 10:25:51.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";
--- yaird-0.0.12.orig/perl/KConfig.pm	2006-10-25 10:25:15.000000000 +0200
+++ yaird-0.0.12/perl/KConfig.pm	2006-10-25 10:25:51.000000000 +0200
@@ -208,6 +208,9 @@
 	# Compaq Smart Array controllers
 	'cpqarray' => [ 'BLK_CPQ_DA' ],
 	'cciss' => [ 'BLK_CPQ_CISS_DA' ],
+	
+	# loopback
+	'loop' => [ 'BLK_DEV_LOOP' ],
 };
 
 
--- 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-25 10:25:51.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;
--- 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-25 10:25:51.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;
--- 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-25 10:25:51.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;
--- 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-25 10:25:51.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;
--- 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-25 10:25:51.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;
--- yaird-0.0.12.orig/perl/Makefile.am	2005-12-08 23:42:33.000000000 +0100
+++ yaird-0.0.12/perl/Makefile.am	2006-10-25 10:25:51.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 \
--- yaird-0.0.12.orig/perl/Makefile.in	2005-12-08 23:42:36.000000000 +0100
+++ yaird-0.0.12/perl/Makefile.in	2006-10-25 10:25:51.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 \
--- yaird-0.0.12.orig/perl/ModProbe.pm	2005-12-08 23:42:33.000000000 +0100
+++ yaird-0.0.12/perl/ModProbe.pm	2006-10-25 10:25:51.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 => '');
 		}
--- yaird-0.0.12.orig/perl/Plan.pm	2006-10-25 10:25:43.000000000 +0200
+++ yaird-0.0.12/perl/Plan.pm	2006-10-25 10:25:51.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,429 @@
 	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 underlying block device
+	my $fsTabEntry = FsTab::findByDevno ($subdev->devno);
+	if (defined ($fsTabEntry)) {
+		#
+		# 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))) {
+			my $origin = $fsTabEntry->origin;
+			Base::fatal ("invalid loop option 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 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.
+	#
+	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;
+				}
+			}
+
+			# 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);
+
+				# case (in)sensitivity of an attribute value
+				my $case = $conf[$i]->{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;
+						}
+					}
+					else {
+						if (lc ($val1) ne lc ($val2)) {
+							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");
+			}
+		}
+	}
+	#
+	# 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 ("found value for keyword 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.
+	#
+
+	# choose a unique mount point for each loop device
+	my $gpgkey_mnt = (exists ($loopOpts{gpgmount}) ? "/.$name" : undef);
+
+	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");
+		}
+		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))) {
+			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");
+		}
+
+		# construct absolute paths as found at boot time.
+		$gpgkey = Base::canon ($gpgkey_mnt . "/" . $gk);
+		if (defined ($gpghome)) {
+			$gpghome = Base::canon ($gpgkey_mnt . "/" . $gh);
+		}
+
+		#
+		# 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");
+		}
+		addDevicePlan ($actions, $gpgdev, $working);
+		addBlockDevMount ($actions, $gpgkey_dev, $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
 #
@@ -635,6 +1062,13 @@
 	}
 
 	#
+	# 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.
 	#
--- yaird-0.0.12.orig/perl/TestSet.pm	2005-12-08 23:42:33.000000000 +0100
+++ yaird-0.0.12/perl/TestSet.pm	2006-10-25 10:25:51.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 ();
--- yaird-0.0.12.orig/templates/Debian-initrd.cfg	2006-10-25 10:25:43.000000000 +0200
+++ yaird-0.0.12/templates/Debian-initrd.cfg	2006-10-25 10:25:51.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
--- yaird-0.0.12.orig/templates/Debian.cfg	2006-10-25 10:25:43.000000000 +0200
+++ yaird-0.0.12/templates/Debian.cfg	2006-10-25 10:25:52.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
--- yaird-0.0.12.orig/templates/Fedora.cfg	2006-10-25 10:25:43.000000000 +0200
+++ yaird-0.0.12/templates/Fedora.cfg	2006-10-25 10:25:52.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 -u yaird-0.0.12/perl/LoopAesEntry.pm yaird-0.0.12/perl/LoopAesEntry.pm
--- yaird-0.0.12/perl/LoopAesEntry.pm	2006-10-20 16:53:15.000000000 +0200
+++ yaird-0.0.12/perl/LoopAesEntry.pm	2006-10-25 10:25:51.000000000 +0200
@@ -1,9 +1,11 @@
 #!perl -w
 #
 # LoopAesEntry -- encapsulate a single entry in /etc/loopaestab
-#   Copyright (C) 2005  Erik van Konijnenburg
 #   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
@@ -20,7 +22,6 @@
 #
 use strict;
 use warnings;
-use LabeledPartitionTab;
 package LoopAesEntry;
 use base 'Obj';
 
@@ -30,8 +31,8 @@
 	$self->takeArgs ('target', 'source', 'opts', 'origin');
 }
 
-sub target		{ return $_[0]->{target}; }
-sub source		{ return $_[0]->{source}; }
+sub target	{ return $_[0]->{target}; }
+sub source	{ return $_[0]->{source}; }
 sub opts	{ return $_[0]->{opts}; }
 sub origin	{ return $_[0]->{origin}; }
 
diff -u yaird-0.0.12/perl/LoopAesTab.pm yaird-0.0.12/perl/LoopAesTab.pm
--- yaird-0.0.12/perl/LoopAesTab.pm	2006-10-20 16:53:15.000000000 +0200
+++ yaird-0.0.12/perl/LoopAesTab.pm	2006-10-25 10:25:51.000000000 +0200
@@ -1,9 +1,11 @@
 #!perl -w
 #
 # LoopAesTab -- encapsulate /etc/loopaestab.
-#   Copyright (C) 2005  Erik van Konijnenburg
 #   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
@@ -93,7 +95,7 @@
 
 sub findByDevno ($) {
 	my ($devno) = @_;
-	my $result = undef; 
+	my $result = undef;
 
 	for my $entry (@{LoopAesTab::all()}) {
 		my $b2 = $entry->target;
@@ -116,2 +118,3 @@
 
+
 1;
diff -u yaird-0.0.12/perl/LoopAesVersion.pm yaird-0.0.12/perl/LoopAesVersion.pm
--- yaird-0.0.12/perl/LoopAesVersion.pm	2006-10-20 16:53:15.000000000 +0200
+++ yaird-0.0.12/perl/LoopAesVersion.pm	2006-10-25 10:25:51.000000000 +0200
@@ -29,16 +29,12 @@
 # Probe loop-AES patched loop module
 #
 sub probeLoopAesModule ($) {
-        my ($path) = @_;
-
-        for my $s ('loop_compute_sector_iv', 'loop_compute_md5_iv', 'loop_compute_md5_iv_v3') {
-                my ($rc, $lines) = Base::runCmd (failOk => 1, cmd => ['/bin/grep', $s, $path]);
-                if (! $rc) {
-                        $hasLoopAesModule = 0;
-                        return;
-                }
-        }
-        $hasLoopAesModule = 1;
+	my ($path) = @_;
+	my @strings = (
+		'loop_compute_sector_iv',
+		'loop_compute_md5_iv_v3',
+	);
+	$hasLoopAesModule = grepBinary ($path, @strings);
 }
 
 #
@@ -46,15 +42,11 @@
 #
 sub probeLoopAesSetup ($) {
 	my ($path) = @_;
-
-	for my $s ('multi-key-v2', 'multi-key-v3') {
-		my ($rc, $lines) = Base::runCmd (failOk => 1, cmd => ['/bin/grep', $s, $path]);
-		if (! $rc) {
-			$hasLoopAesSetup = 0;
-			return;
-		}
-	}
-	$hasLoopAesSetup = 1;
+	my @strings = (
+		'multi-key-v2',
+		'multi-key-v3',
+	);
+	$hasLoopAesSetup = grepBinary ($path, @strings);
 }
 
 #
@@ -75,4 +67,38 @@
 }
 
+#
+# 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 -u yaird-0.0.12/perl/LoopDev.pm yaird-0.0.12/perl/LoopDev.pm
--- yaird-0.0.12/perl/LoopDev.pm	2006-10-20 16:53:15.000000000 +0200
+++ yaird-0.0.12/perl/LoopDev.pm	2006-10-25 10:25:51.000000000 +0200
@@ -1,9 +1,11 @@
 #!perl -w
 #
 # LoopDev -- the probed values for a loopback device, as found by losetup.
-#   Copyright (C) 2005  Erik van Konijnenburg
 #   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
@@ -26,21 +28,21 @@
 sub fill {
 	my $self = shift;
 	$self->SUPER::fill();
-	$self->takeArgs ('path', 'devno', 'opts', 'device');
+	$self->takeArgs ('target', 'devno', 'opts', 'source');
 }
 
-sub path	{ return $_[0]->{path}; }
+sub target	{ return $_[0]->{target}; }
 sub devno	{ return $_[0]->{devno}; }
 sub opts	{ return $_[0]->{opts}; }
-sub device	{ return $_[0]->{device}; }
+sub source	{ return $_[0]->{source}; }
 
 sub string {
 	my $self = shift;
-	my $path = $self->path;
+	my $target = $self->target;
 	my $devno = $self->devno;
 	my $opts = $self->opts->string;
-	my $device = $self->device;
-	return "$path($devno) on $device" . ($opts ? " with $opts" : "");
+	my $source = $self->source;
+	return "$target($devno) on $source" . ($opts ? " with $opts" : "");
 }
 
 
diff -u yaird-0.0.12/perl/LoopTab.pm yaird-0.0.12/perl/LoopTab.pm
--- yaird-0.0.12/perl/LoopTab.pm	2006-10-20 16:53:15.000000000 +0200
+++ yaird-0.0.12/perl/LoopTab.pm	2006-10-25 10:25:51.000000000 +0200
@@ -1,9 +1,11 @@
 #!perl -w
 #
 # LoopTab -- encapsulate losetup output
-#   Copyright (C) 2005  Erik van Konijnenburg
 #   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
@@ -46,12 +48,15 @@
 		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]]);
+		my ($rc, $lines) = Base::runCmd (missingOk => 1, failOk => 1,
+			cmd => ['/sbin/losetup', $paths->[0]]);
 		if ($rc && defined ($lines) && @{$lines} == 1) {
-			processLine (${$lines}[0]);
+			processLine ($lines->[0]);
 		}
 	}
-	LoopAesVersion::probeLoopAesSetup ('/sbin/losetup');
+	if (@{$loopTab}) {
+		LoopAesVersion::probeLoopAesSetup ('/sbin/losetup');
+	}
 }
 
 #
@@ -72,12 +77,12 @@
 sub processLine ($) {
 	my ($line) = @_;
 
-	my ($path, $device, @attributes, @opts);
+	my ($target, $source, @attributes, @opts);
 
 	# common device description
 	if ($line =~ /^(\S+): \[[[:xdigit:]]+\]:\d+ \((\S+)\)(?:(,? )(.*))?$/) {
-		$path = $1;
-		$device = $2;
+		$target = $1;
+		$source = $2;
 		if (defined ($4)) {
 			@attributes = split (/$3/, $4);
 		}
@@ -105,7 +110,7 @@
 		}
 		# non-loop-AES attribute
 		elsif ($attr =~ /^encryption /) {
-			Base::fatal ("cryptoloop ('$path') not supported");
+			Base::fatal ("cryptoloop device ('$target') not supported");
 		}
 		# loop-AES attribute
 		elsif ($attr =~ /^loinit=(\d+)$/) {
@@ -113,7 +118,7 @@
 		}
 		# loop-AES attribute
 		elsif ($attr =~ /^read-only$/) {
-			Base::fatal ("read-only loopback device ('$path') not supported");
+			Base::fatal ("read-only loopback device ('$target') not supported");
 		}
 		# loop-AES attribute
 		elsif ($attr =~ /^multi-key-(v[23])$/) {
@@ -130,21 +135,16 @@
 	my $optString = join (",", @opts);
 	my $opts = Opts->new (string => $optString);
 
-	my $devno = Base::devno ($path);
+	my $devno = Base::devno ($target);
 	if (! defined($devno)) {
-		Base::fatal ("Device '$path' in losetup output: can't find device major/minor number");
-	}
-
-	my $n = Base::devno ($device);
-	if (! defined ($n)) {
-		Base::fatal ("Device '$device' in losetup output: can't find device major/minor number");
+		Base::fatal ("device '$target' in losetup output: can't find device major/minor number");
 	}
 
 	my $descr = LoopDev->new (
-		path => $path,
+		target => $target,
 		devno => $devno,
 		opts => $opts,
-		device => $device,
+		source => $source,
 	);
 	push @{$loopTab}, $descr;
 }
@@ -154,10 +154,10 @@
 	return $loopTab;
 }
 
-sub findByPath ($) {
-	my ($path) = @_;
+sub findByTarget ($) {
+	my ($target) = @_;
 	for my $ld (@{all()}) {
-		if ($ld->path() eq $path) {
+		if ($ld->target() eq $target) {
 			return $ld;
 		}
 	}
diff -u yaird-0.0.12/perl/ModProbe.pm yaird-0.0.12/perl/ModProbe.pm
--- yaird-0.0.12/perl/ModProbe.pm	2006-10-20 16:53:15.000000000 +0200
+++ yaird-0.0.12/perl/ModProbe.pm	2006-10-25 10:25:51.000000000 +0200
@@ -153,7 +153,7 @@
 				# There exist two different loop modules both
 				# named 'loop': a native version and a loop-AES
 				# patched version.
-				# 
+				#
 				LoopAesVersion::probeLoopAesModule ($1);
 			}
 
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-20 16:54:13.000000000 +0200
+++ yaird-0.0.12/perl/Plan.pm	2006-10-25 10:25:51.000000000 +0200
@@ -455,7 +455,11 @@
 	}
 
 	# start the underlying block device
-	my $subdev = ActiveBlockDevTab::findByPath ($loopdev->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);
 
 
@@ -476,9 +480,10 @@
 	# 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).
-	# 
+	# 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,
@@ -499,7 +504,7 @@
 	};
 	my $loopAesTabSupportedOpts = {
 		%{$fsTabSupportedOpts},
-		'gpgmount' => 0,
+		'gpgmount' => undef,
 	};
 
 	# available loop device config entries
@@ -509,7 +514,7 @@
 	# 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,
@@ -565,8 +570,8 @@
 	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}});
+			my @suppOpts1 = keys (%{$conf[$i]->{supportedOpts}});
+			my @suppOpts2 = keys (%{$conf[$j]->{supportedOpts}});
 
 			# determine common set of supported attributes
 			for my $opt (@suppOpts1, @suppOpts2) {
@@ -577,13 +582,13 @@
 
 			# 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);
+				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);
 
 				# case (in)sensitivity of an attribute value
-				my $case = ${${$conf[$i]}{supportedOpts}}{$opt};
+				my $case = $conf[$i]->{supportedOpts}{$opt};
 
 				if ($exists1 xor $exists2) {
 					push @conflicts, $opt;
@@ -606,8 +611,8 @@
 			}
 			if (@conflicts) {
 				my $opt = join (', ', @conflicts);
-				my $origin1 = ${$conf[$i]}{origin};
-				my $origin2 = ${$conf[$j]}{origin};
+				my $origin1 = $conf[$i]->{origin};
+				my $origin2 = $conf[$j]->{origin};
 				Base::fatal ("conflicting option(s) '$opt' in $origin1 and $origin2");
 			}
 		}
@@ -618,13 +623,23 @@
 	# be merged to provide a complete loopback device config.
 	#
 	for my $c (@conf) {
-		my @suppOpts = keys (%{${$c}{supportedOpts}});
+		my @suppOpts = keys (%{$c->{supportedOpts}});
 
 		for my $opt (@suppOpts) {
-			my $exists = ${$c}{opts}->exists ($opt);
-			my $val = ${$c}{opts}->get ($opt);
-
-			if ($exists && (! exists ($loopOpts{$opt}))) {
+			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 ("found value for keyword option '$opt' in $origin");
+			}
+			if (! exists ($loopOpts{$opt})) {
 				$loopOpts{$opt} = $val;
 			}
 		}
@@ -768,7 +783,7 @@
 
 		#
 		# 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


More information about the Yaird-devel mailing list