On 08/23/2011 03:19 PM, Raphael Hertzog wrote:
So it looks ok for me.
Great. Thanks for taking the time to review this. Here's the final debdiff (equivalent to patch 1 from message #132 plus patches 2-6 from message #112).
Are you willing to get it uploaded? Anders
diff -Nru debsums-2.0.48+nmu3/debian/changelog debsums-2.0.48+nmu4/debian/changelog --- debsums-2.0.48+nmu3/debian/changelog 2010-11-17 16:47:01.000000000 -0500 +++ debsums-2.0.48+nmu4/debian/changelog 2011-08-23 13:36:28.000000000 -0400 @@ -1,3 +1,14 @@ +debsums (2.0.48+nmu4) unstable; urgency=low + + * Non-maintainer upload. + * Use dpkg-query instead of reading /var/lib/dpkg/status directly. We + still use /var/lib/dpkg/info directly to find the paths to .md5sums + files, but now at least we try to verify our guess by checking that + the corresponding .lists file exist. (Closes: #616066) + * Add multiarch support. + + -- Anders Kaseorg <ande...@mit.edu> Tue, 16 Aug 2011 23:00:14 -0400 + debsums (2.0.48+nmu3) unstable; urgency=low * Non-maintainer upload. diff -Nru debsums-2.0.48+nmu3/debsums debsums-2.0.48+nmu4/debsums --- debsums-2.0.48+nmu3/debsums 2010-11-17 16:18:37.000000000 -0500 +++ debsums-2.0.48+nmu4/debsums 2011-08-23 13:36:28.000000000 -0400 @@ -116,6 +116,29 @@ } } +sub parse_dpkg { + my ($command_cb, $field_names) = @_; + + local $/ = "\n\n"; # Separator that cannot appear in dpkg status format + my @command = &$command_cb('--showformat=' . + (join '', map {"\${$_}$/"} @$field_names)); + open DPKG, '-|', @command + or die "$self: can't run dpkg-query ($!)\n"; + + my @ret; + while (!eof DPKG) + { + my %field = map {$_, scalar <DPKG>} @$field_names; + chomp @field{@$field_names}; + push @ret, \%field; + } + + close DPKG or die "$self: @command failed (", + $! ? $! : $? >> 8 ? "exit status " . ($? >> 8) : "signal " . ($? & 127), + ")\n"; + return @ret; +} + $root ||= ''; $admindir ||= '/var/lib/dpkg'; my $DPKG = $root . $admindir; @@ -151,6 +174,8 @@ @debpath = map +(length) ? $_ : '.', split /:/, $debpath, -1 if $debpath; my $arch; +chomp ($arch = `/usr/bin/dpkg --print-architecture`); + my %generate; if ($gen_opt) { @@ -172,8 +197,6 @@ $generate{missing}++ unless $generate{all} or $generate{missing}; $generate{keep}++ if $generate{nocheck}; - chomp ($arch = `/usr/bin/dpkg --print-architecture`); - # ensure generated files are world readable umask 022; } @@ -181,38 +204,31 @@ my %installed; my %replaced; { - open STATUS, "$DPKG/status" or die "$self: can't open $DPKG/status ($!)\n"; - local $/ = ''; - - while (<STATUS>) - { - chomp; - my %field = map /^(\S+):\s+(.*)/ms, split /\n(?!\s)/; - next unless exists $field{Package} - and exists $field{Version} - and exists $field{Status} + for my $fields (parse_dpkg(sub {'dpkg-query', "--admindir=$DPKG", @_, '--show'}, + [qw(Package PackageSpec Version Status Conffiles Replaces)])) { + my %field = %$fields; + $field{PackageSpec} = $field{Package} if $field{PackageSpec} eq ''; + next unless $field{PackageSpec} ne '' + and $field{Version} ne '' and $field{Status} =~ /\sinstalled$/; - $installed{$field{Package}}{Version} = $field{Version}; - $installed{$field{Package}}{Conffiles} = { + $installed{$field{PackageSpec}}{Version} = $field{Version}; + $installed{$field{PackageSpec}}{Conffiles} = { map m!^\s*/(\S+)\s+([\da-f]+)!, split /\n/, $field{Conffiles} } if $field{Conffiles}; - next unless exists $field{Replaces}; for (split /,\s*/, $field{Replaces}) { my ($pack, $ver) = /^(\S+)(?:\s+\(([^)]+)\))?$/; unless ($pack) { - warn "$self: invalid Replaces for $field{Package} '$_'\n"; + warn "$self: invalid Replaces for $field{PackageSpec} '$_'\n"; next; } - push @{$replaced{$pack}{$ver || 'all'}}, $field{Package}; + push @{$replaced{$pack}{$ver || 'all'}}, $field{PackageSpec}; } } - - close STATUS; } my %diversion; @@ -254,15 +270,33 @@ !system '/usr/bin/dpkg', '--compare-versions', $ver, $op, $testver; } +sub md5sums_path +{ + # Calling dpkg-query --control-path for every package is too slow, + # so we cheat a little bit. + + my ($pack) = @_; + if (-e "$DPKG/info/$pack.list") { + return "$DPKG/info/$pack.md5sums"; + } elsif ($pack !~ /:/ and -e "$DPKG/info/$pack:$arch.list") { + return "$DPKG/info/$pack:$arch.md5sums"; + } elsif ($pack =~ /^(.*):/ and -e "$DPKG/info/$1.list") { + return "$DPKG/info/$1.md5sums"; + } else { + die "Cannot find md5sums path for $pack\n"; + } +} + sub is_replaced { my ($pack, $path, $sum) = @_; unless ($installed{$pack}{ReplacedBy}) { - return 0 unless $replaced{$pack}; + (my $name = $pack) =~ s/:[^:]*$//; + return 0 unless $replaced{$name}; - while (my ($ver, $p) = each %{$replaced{$pack}}) + while (my ($ver, $p) = each %{$replaced{$name}}) { next unless $ver eq 'all' or dpkg_cmp $installed{$pack}{Version}, $ver; @@ -273,7 +307,7 @@ for my $p (@{$installed{$pack}{ReplacedBy} || []}) { - open S, "$DPKG/info/$p.md5sums" or next; + open S, md5sums_path($p) or next; while (<S>) { if ($_ eq "$sum $path\n") @@ -414,7 +448,7 @@ my $conffiles; # looks like a package name - unless (/[^a-z\d+.-]/ or /\.deb$/) + unless (/[^a-z\d+.:-]/ or /\.deb$/) { $pack = $_; unless (exists $installed{$pack}) @@ -438,8 +472,11 @@ { # look for <pack>_<ver>_<arch>.deb or <pack>_<ver>.deb # where <ver> may or may not contain an epoch - if (($deb) = grep -f, map +(glob "$dir/${pack}_$_.deb"), - map +("${_}_$arch", "${_}_all", $_), @v) + my ($debname, $debarch); + ($debname, $debarch) = ($pack =~ /^(.*):([^:]*)$/) + or ($debname, $debarch) = ($pack, $arch); + if (($deb) = grep -f, map +(glob "$dir/${debname}_$_.deb"), + map +("${_}_$debarch", "${_}_all", $_), @v) { $deb =~ s!^\./+!!; last; @@ -460,7 +497,7 @@ } else { - $sums = "$DPKG/info/$pack.md5sums"; + $sums = md5sums_path($pack); unless (-f $sums or $config) { if ($missing) @@ -501,17 +538,24 @@ } my $deb = $_; - my %field = map /^(\S+):\s+(.*)/ms, split /\n(?!\s)/, - `dpkg --field '$deb' Package Version Conffiles 2>/dev/null`; + my ($fields) = parse_dpkg(sub {'dpkg-deb', @_, '--show', $deb}, + [qw(Package PackageSpec Version Conffiles)]) + or do { + warn "$self: $deb does not seem to be a valid debian archive\n"; + $status |= 1; + next; + }; + my %field = %$fields; + $field{PackageSpec} = $field{Package} if $field{PackageSpec} eq ''; - unless (exists $field{Package} and $field{Version}) + unless ($field{PackageSpec} ne '' and $field{Version} ne '') { warn "$self: $deb does not seem to be a valid debian archive\n"; $status |= 1; next; } - $pack = $field{Package}; + $pack = $field{PackageSpec}; unless (exists $installed{$pack}) { warn "$self: package $pack is not installed\n"; @@ -626,7 +670,7 @@ if ($generate{keep}) { - my $target = "$DPKG/info/$pack.md5sums"; + my $target = md5sums_path($pack); copy $sums, $target or die "$self: can't copy sums to $target ($!)\n"; }