package fs::dmcrypt; use diagnostics; use strict; #-###################################################################################### #- misc imports #-###################################################################################### use common; use fs::type; use fs::get; use run_program; =head1 SYNOPSYS Manage encrypted file systems using cryptsetup =head1 Functions =over =cut sub _crypttab() { "$::prefix/etc/crypttab" } =item init() Load kernel modules and init device mapper. =cut sub init() { whereis_binary('cryptsetup') or die "cryptsetup not installed"; eval { modules::load('dm-crypt', list_modules::category2modules('various/crypto')) }; devices::init_device_mapper(); 1; } my $initialized; sub _ensure_initialized() { $initialized++ or init(); } sub read_crypttab_ { my ($all_hds, $crypttab) = @_; -e $crypttab or return; my @raw_parts = grep { fs::type::isRawLUKS($_) } fs::get::really_all_fstab($all_hds); foreach (cat_($crypttab)) { next if /^#/; my ($dm_name, $dev) = split; my $raw_part = fs::get::device2part($dev, \@raw_parts) or log::l("crypttab: unknown device $dev for $dm_name"), next; $raw_part->{dm_name} = $dm_name; _get_existing_one_with_state($raw_part); } } =item read_crypttab($all_hds) Read /etc/crypttab =cut sub read_crypttab { my ($all_hds) = @_; read_crypttab_($all_hds, _crypttab()); } sub save_crypttab_ { my ($all_hds, $crypttab) = @_; my @raw_parts = grep { $_->{dm_name} } fs::get::really_all_fstab($all_hds) or return; my %names = map { $_->{dm_name} => fs::wild_device::from_part('', $_) } @raw_parts; substInFile { my ($name, $_dev) = split; if (my $new_dev = delete $names{$name}) { $_ = "$name $new_dev\n"; } if (eof) { $_ .= join('', map { "$_ $names{$_}\n" } sort keys %names); } } $crypttab; } =item save_crypttab($all_hds) Save /etc/crypttab =cut sub save_crypttab { my ($all_hds) = @_; save_crypttab_($all_hds, _crypttab()); } sub format_part { my ($part) = @_; my $tmp_key_file = "/tmp/.dmcrypt_key-$$"; common::with_private_tmp_file($tmp_key_file, $part->{dmcrypt_key}, sub { _run_or_die('--cipher=aes-xts-benbi', '--key-size=512', 'luksFormat', '--batch-mode', devices::make($part->{device}), $_[0]); }); fs::format::after_formatting($part); } sub open_part { my ($dmcrypts, $part) = @_; my $tmp_key_file = "/tmp/.dmcrypt_key-$$"; common::with_private_tmp_file($tmp_key_file, $part->{dmcrypt_key}, sub { _run_or_die('luksOpen', devices::make($part->{device}), $part->{dm_name}, '--key-file', $_[0]); }); run_program::run('udevadm', 'settle'); push @$dmcrypts, _get_existing_one_with_state($part); } sub _get_existing_one_with_state { my ($part) = @_; my $active_dmcrypt = _parse_dmsetup_table($part->{dm_name}, run_program::get_stdout('dmsetup', 'table', $part->{dm_name})); _get_existing_one([$part], $active_dmcrypt); } sub close_part { my ($dmcrypts, $part) = @_; my $dm_part = fs::get::device2part("mapper/$part->{dm_name}", $dmcrypts); _run_or_die('luksClose', devices::make($dm_part->{device})); $part->{dm_active} = 0; @$dmcrypts = grep { $_ != $dm_part } @$dmcrypts; # remove partition from /etc/crypttab when deleted (mga#25891) substInFile { my ($name, $_dev) = split; undef $_ if $name eq $part->{dm_name}; } _crypttab(); } sub _run_or_die { my ($command, @para) = @_; _ensure_initialized(); run_program::run_or_die('cryptsetup', $command, @para); } sub get_existing { my $fstab = \@_; map { _get_existing_one($fstab, $_) } active_dmcrypts(); } sub _get_existing_one { my ($fstab, $active_dmcrypt) = @_; my $p = fs::wild_device::to_subpart("/dev/mapper/$active_dmcrypt->{name}"); my $part = { device => "mapper/$active_dmcrypt->{name}", size => $active_dmcrypt->{size}, options => 'noatime', dmcrypt_name => $active_dmcrypt->{name}, major => $p->{major}, minor => $p->{minor} }; if (my $raw_part = find { fs::get::is_same_hd($active_dmcrypt, $_) } @$fstab) { $part->{rootDevice} = $raw_part->{device}; $raw_part->{dm_name} = $active_dmcrypt->{name}; $raw_part->{dm_active} = 1; } else { log::l("could not find the device $active_dmcrypt->{major}:$active_dmcrypt->{minor} for $part->{device}"); } if (my $type = fs::type::type_subpart_from_magic($part)) { put_in_hash($part, $type); } fs::type::set_isFormatted($part, to_bool($part->{fs_type})); unless (fs::type::cannotBeMountable($part)) { $part->{fs_type} or fs::type::set_fs_type($part, defaultFS()); } log::l("dmcrypt: found $part->{device} type $part->{fs_type} with rootDevice $part->{rootDevice}"); $part; } sub active_dmcrypts() { grep { $_->{type} eq 'crypt' } active_dm(); } sub _parse_dmsetup_table { my ($name, $s) = @_; my @l = split(' ', $s); my ($major, $minor) = split(':', $l[6]); { name => $name, size => $l[1], type => $l[2], major => $major, minor => $minor }; } sub active_dm() { run_program::run('udevadm', 'settle'); map { my $name = s/(.*?):\s*// && $1; _parse_dmsetup_table($name, $_); } run_program::get_stdout('dmsetup', 'table'); } =back =cut 1;