Skip to content
Commits on Source (7)
......@@ -17,6 +17,7 @@ WriteMakefile(
'Archive::Cpio' => 0, # required to pass tests
'Archive::Zip' => 0,
'Getopt::Long' => 0,
'Monkey::Patch' => 0,
},
LICENSE => "gpl",
dist => { COMPRESS => 'gzip -9nf', SUFFIX => 'gz', },
......
strip-nondeterminism (1.1.3-1) experimental; urgency=medium
* Workaround Archive::Zip's incorrect handling of the localExtraField field
by monkey-patching the accessor methods to always return normalised values.
This fixes the normalisation of Unix ownership (uid/gid) within .zip
archives, .epub files, etc.
(Closes: #858431, reproducible-builds/strip-nondeterminism#4)
* Check the return status from Archive::Zip when writing file to disk.
* Catch an edgecase where/if we can't parse the provided length of an
invalid field within .zip files.
-- Chris Lamb <lamby@debian.org> Sun, 28 Apr 2019 17:30:27 +0100
strip-nondeterminism (1.1.2-1) unstable; urgency=medium
* Call file(1) with the "--" argument placeholder to fix normalisation of
......
......@@ -8,6 +8,7 @@ Build-Depends-Indep:
libarchive-cpio-perl <!nocheck>,
libarchive-zip-perl,
libdevel-cover-perl,
libmonkey-patch-perl,
perl,
Maintainer: Reproducible builds folks <reproducible-builds@lists.alioth.debian.org>
Uploaders:
......@@ -26,6 +27,7 @@ Section: perl
Architecture: all
Depends:
libarchive-zip-perl,
libmonkey-patch-perl,
${misc:Depends},
${perl:Depends},
Recommends:
......
......@@ -25,7 +25,7 @@ use POSIX qw(tzset);
our($VERSION, $canonical_time, $clamp_time);
$VERSION = '1.1.2'; # <https://semver.org/>
$VERSION = '1.1.3'; # <https://semver.org/>
sub init() {
$ENV{'TZ'} = 'UTC';
......
......@@ -24,6 +24,7 @@ use warnings;
use File::Temp;
use File::StripNondeterminism;
use Archive::Zip qw/:CONSTANTS :ERROR_CODES/;
use Monkey::Patch qw/patch_class/;
# A magic number from Archive::Zip for the earliest timestamp that
# can be represented by a Zip file. From the Archive::Zip source:
......@@ -145,6 +146,10 @@ sub normalize_extra_fields($$) {
} else {
# Catch invalid field lengths by calculating whether we would
# read beyond the end of the file.
if (!defined($len)) {
warn "strip-nondeterminism: unknown extra field length";
return;
}
if ($pos + $len >= length($field)) {
warn "strip-nondeterminism: invalid extra field length ($len)";
return;
......@@ -213,14 +218,34 @@ sub normalize {
? oct(755)
: oct(644));
}
foreach my $x (qw(cdExtraField localExtraField)) {
my $result = normalize_extra_fields($canonical_time, $member->$x);
return 0 unless defined $result;
$member->$x($result);
}
}
my $old_perms = (stat($zip_filename))[2] & oct(7777);
$zip->overwrite();
# Archive::Zip::Member does not handle the localExtraField field (used for
# uid/gids) correctly or consistently.
#
# It does not populate localExtraField in the class upon initial reading of
# the file whilst it does for cdExtraField. One can workaround this
# manually with calls to _seekToLocalHeader and _readLocalFileHeader but
# upon writing to a file back to the disk Archive::Zip will ignore any
# stored value of localExtraField (!) and reload it again from the existing
# file handle in/around rewindData.
#
# We therefore monkey-patch the accessor methods of the Member class to
# ensure that normalised values are used in this final save.
#
# <https://salsa.debian.org/reproducible-builds/strip-nondeterminism/issues/4>
my @patches = map {
patch_class('Archive::Zip::Member', $_, sub {
my $fn = shift;
my $result = $fn->(@_);
return defined($result) ?
normalize_extra_fields($canonical_time, $result) : $result;
});
} qw(cdExtraField localExtraField);
return 0 unless $zip->overwrite() == AZ_OK;
undef @patches; # Remove our monkey patches
chmod($old_perms, $zip_filename);
return 1;
}
......