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
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
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