Browse Source

dpkg-gencontrol, dpkg-distaddfile: protect update of debian/files with a lock

The lock is taken on debian/control as this is a file that we know to
always exist. Without this lock, it's possible that the file is updated
concurrently by two processes when parallel building is enabled (leading
to one of them failing unexpectedly).

Reported-by: James Vega <jamessan@debian.org>
Raphaël Hertzog 14 years ago
parent
commit
d834b77b5d
4 changed files with 32 additions and 1 deletions
  1. 5 0
      debian/changelog
  2. 1 1
      debian/control
  3. 13 0
      scripts/dpkg-distaddfile.pl
  4. 13 0
      scripts/dpkg-gencontrol.pl

+ 5 - 0
debian/changelog

@@ -32,6 +32,11 @@ dpkg (1.16.2) UNRELEASED; urgency=low
     too. Closes: #595144
     too. Closes: #595144
   * Rewrite architecture.mk with explicit loops instead of duplicating many
   * Rewrite architecture.mk with explicit loops instead of duplicating many
     similar lines. Based on a patch by Thorsten Glaser <tg@mirbsd.de>.
     similar lines. Based on a patch by Thorsten Glaser <tg@mirbsd.de>.
+  * Modify dpkg-gencontrol and dpkg-distaddfile to grab a write lock
+    on debian/control before updating debian/files to avoid simultaneous
+    updates. Closes: #642608
+    Add libfile-fcntllock-perl to dpkg-dev's Depends since we use this module
+    to handle the locking.
 
 
   [ Jonathan Nieder ]
   [ Jonathan Nieder ]
   * Bump po4a version in Build-Depends to 0.41, since earlier versions do
   * Bump po4a version in Build-Depends to 0.41, since earlier versions do

+ 1 - 1
debian/control

@@ -49,7 +49,7 @@ Package: dpkg-dev
 Section: utils
 Section: utils
 Priority: optional
 Priority: optional
 Architecture: all
 Architecture: all
-Depends: libdpkg-perl (= ${source:Version}), bzip2, xz-utils,
+Depends: libdpkg-perl (= ${source:Version}), libfile-fcntllock-perl, bzip2, xz-utils,
  patch, make, binutils, base-files (>= 5.0.0), ${misc:Depends}
  patch, make, binutils, base-files (>= 5.0.0), ${misc:Depends}
 Recommends: gcc | c-compiler, build-essential, fakeroot, gnupg, gpgv, libalgorithm-merge-perl
 Recommends: gcc | c-compiler, build-essential, fakeroot, gnupg, gpgv, libalgorithm-merge-perl
 Suggests: debian-keyring
 Suggests: debian-keyring

+ 13 - 0
scripts/dpkg-distaddfile.pl

@@ -18,6 +18,7 @@
 use strict;
 use strict;
 use warnings;
 use warnings;
 
 
+use File::FcntlLock;
 use POSIX;
 use POSIX;
 use POSIX qw(:errno_h :signal_h);
 use POSIX qw(:errno_h :signal_h);
 use Dpkg;
 use Dpkg;
@@ -75,6 +76,15 @@ my ($file, $section, $priority) = @ARGV;
 ($file =~ m/\s/ || $section =~ m/\s/ || $priority =~ m/\s/) &&
 ($file =~ m/\s/ || $section =~ m/\s/ || $priority =~ m/\s/) &&
     error(_g("filename, section and priority may contain no whitespace"));
     error(_g("filename, section and priority may contain no whitespace"));
 
 
+# Obtain a lock on debian/control to avoid simultaneous updates
+# of debian/files when parallel building is in use
+my $fs = File::FcntlLock->new(l_type => F_WRLCK);
+my $lockfh;
+sysopen($lockfh, "debian/control", O_WRONLY) ||
+    syserr(_g("cannot write %s"), "debian/control");
+$fs->lock($lockfh, F_SETLKW) ||
+    syserr(_("failed to get a write lock on %s"), "debian/control");
+
 $fileslistfile="./$fileslistfile" if $fileslistfile =~ m/^\s/;
 $fileslistfile="./$fileslistfile" if $fileslistfile =~ m/^\s/;
 open(Y, "> $fileslistfile.new") || syserr(_g("open new files list file"));
 open(Y, "> $fileslistfile.new") || syserr(_g("open new files list file"));
 if (open(X,"< $fileslistfile")) {
 if (open(X,"< $fileslistfile")) {
@@ -91,3 +101,6 @@ print(Y "$file $section $priority\n")
 close(Y) || syserr(_g("close new files list file"));
 close(Y) || syserr(_g("close new files list file"));
 rename("$fileslistfile.new", $fileslistfile) ||
 rename("$fileslistfile.new", $fileslistfile) ||
     syserr(_g("install new files list file"));
     syserr(_g("install new files list file"));
+
+# Release the lock
+close($lockfh) || syserr(_g("cannot close %s"), "debian/control");

+ 13 - 0
scripts/dpkg-gencontrol.pl

@@ -18,6 +18,7 @@
 use strict;
 use strict;
 use warnings;
 use warnings;
 
 
+use File::FcntlLock;
 use POSIX;
 use POSIX;
 use POSIX qw(:errno_h);
 use POSIX qw(:errno_h);
 use Dpkg;
 use Dpkg;
@@ -333,6 +334,15 @@ for my $f (keys %remove) {
     delete $fields->{$f};
     delete $fields->{$f};
 }
 }
 
 
+# Obtain a lock on debian/control to avoid simultaneous updates
+# of debian/files when parallel building is in use
+my $fs = File::FcntlLock->new(l_type => F_WRLCK);
+my $lockfh;
+sysopen($lockfh, "debian/control", O_WRONLY) ||
+    syserr(_g("cannot write %s"), "debian/control");
+$fs->lock($lockfh, F_SETLKW) ||
+    syserr(_("failed to get a write lock on %s"), "debian/control");
+
 $fileslistfile="./$fileslistfile" if $fileslistfile =~ m/^\s/;
 $fileslistfile="./$fileslistfile" if $fileslistfile =~ m/^\s/;
 open(Y, ">", "$fileslistfile.new") || syserr(_g("open new files list file"));
 open(Y, ">", "$fileslistfile.new") || syserr(_g("open new files list file"));
 binmode(Y);
 binmode(Y);
@@ -363,6 +373,9 @@ print(Y $substvars->substvars(sprintf("%s %s %s\n", $forcefilename,
 close(Y) || syserr(_g("close new files list file"));
 close(Y) || syserr(_g("close new files list file"));
 rename("$fileslistfile.new", $fileslistfile) || syserr(_g("install new files list file"));
 rename("$fileslistfile.new", $fileslistfile) || syserr(_g("install new files list file"));
 
 
+# Release the lock
+close($lockfh) || syserr(_g("cannot close %s"), "debian/control");
+
 my $cf;
 my $cf;
 my $fh_output;
 my $fh_output;
 if (!$stdout) {
 if (!$stdout) {