buildEnv: Check the content of colliding paths.

Originally wanted to include ignoreCollisions in cups-progs, but I think
it's better if we use ignoreCollisions only if there are _real_
collisions between files with different contents.

Of course, we also check whether the file permissions match, so you get
a collision if contents are the same but the permissions are different.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
This commit is contained in:
aszlig 2014-11-23 20:42:14 +01:00
parent 85dd89f6eb
commit 4529ed1259
No known key found for this signature in database
GPG key ID: D0EBD0EC8C2DC961
2 changed files with 39 additions and 10 deletions

View file

@ -5,6 +5,7 @@ use Cwd 'abs_path';
use IO::Handle; use IO::Handle;
use File::Path; use File::Path;
use File::Basename; use File::Basename;
use File::Compare;
use JSON::PP; use JSON::PP;
STDOUT->autoflush(1); STDOUT->autoflush(1);
@ -38,7 +39,7 @@ for my $p (@pathsToLink) {
sub findFiles; sub findFiles;
sub findFilesInDir { sub findFilesInDir {
my ($relName, $target, $ignoreCollisions, $priority) = @_; my ($relName, $target, $ignoreCollisions, $checkCollisionContents, $priority) = @_;
opendir DIR, "$target" or die "cannot open `$target': $!"; opendir DIR, "$target" or die "cannot open `$target': $!";
my @names = readdir DIR or die; my @names = readdir DIR or die;
@ -46,12 +47,28 @@ sub findFilesInDir {
foreach my $name (@names) { foreach my $name (@names) {
next if $name eq "." || $name eq ".."; next if $name eq "." || $name eq "..";
findFiles("$relName/$name", "$target/$name", $name, $ignoreCollisions, $priority); findFiles("$relName/$name", "$target/$name", $name, $ignoreCollisions, $checkCollisionContents, $priority);
} }
} }
sub checkCollision {
my ($path1, $path2) = @_;
my $stat1 = (stat($path1))[2];
my $stat2 = (stat($path2))[2];
if ($stat1 != $stat2) {
warn "different permissions in `$path1' and `$path2': "
. sprintf("%04o", $stat1 & 07777) . " <-> "
. sprintf("%04o", $stat2 & 07777);
return 0;
}
return compare($path1, $path2) == 0;
}
sub findFiles { sub findFiles {
my ($relName, $target, $baseName, $ignoreCollisions, $priority) = @_; my ($relName, $target, $baseName, $ignoreCollisions, $checkCollisionContents, $priority) = @_;
# Urgh, hacky... # Urgh, hacky...
return if return if
@ -79,7 +96,9 @@ sub findFiles {
} }
unless (-d $target && ($oldTarget eq "" || -d $oldTarget)) { unless (-d $target && ($oldTarget eq "" || -d $oldTarget)) {
if ($ignoreCollisions) { if ($checkCollisionContents && checkCollision($oldTarget, $target)) {
return;
} elsif ($ignoreCollisions) {
warn "collision between `$target' and `$oldTarget'\n" if $ignoreCollisions == 1; warn "collision between `$target' and `$oldTarget'\n" if $ignoreCollisions == 1;
return; return;
} else { } else {
@ -87,8 +106,8 @@ sub findFiles {
} }
} }
findFilesInDir($relName, $oldTarget, $ignoreCollisions, $oldPriority) unless $oldTarget eq ""; findFilesInDir($relName, $oldTarget, $ignoreCollisions, $checkCollisionContents, $oldPriority) unless $oldTarget eq "";
findFilesInDir($relName, $target, $ignoreCollisions, $priority); findFilesInDir($relName, $target, $ignoreCollisions, $checkCollisionContents, $priority);
$symlinks{$relName} = ["", $priority]; # denotes directory $symlinks{$relName} = ["", $priority]; # denotes directory
} }
@ -98,12 +117,12 @@ my %done;
my %postponed; my %postponed;
sub addPkg { sub addPkg {
my ($pkgDir, $ignoreCollisions, $priority) = @_; my ($pkgDir, $ignoreCollisions, $checkCollisionContents, $priority) = @_;
return if (defined $done{$pkgDir}); return if (defined $done{$pkgDir});
$done{$pkgDir} = 1; $done{$pkgDir} = 1;
findFiles("", $pkgDir, "", $ignoreCollisions, $priority); findFiles("", $pkgDir, "", $ignoreCollisions, $checkCollisionContents, $priority);
my $propagatedFN = "$pkgDir/nix-support/propagated-user-env-packages"; my $propagatedFN = "$pkgDir/nix-support/propagated-user-env-packages";
if (-e $propagatedFN) { if (-e $propagatedFN) {
@ -132,7 +151,11 @@ if (exists $ENV{"pkgsPath"}) {
# user. # user.
for my $pkg (@{decode_json $pkgs}) { for my $pkg (@{decode_json $pkgs}) {
for my $path (@{$pkg->{paths}}) { for my $path (@{$pkg->{paths}}) {
addPkg($path, $ENV{"ignoreCollisions"} eq "1", $pkg->{priority}) if -e $path; addPkg($path,
$ENV{"ignoreCollisions"} eq "1",
$ENV{"checkCollisionContents"} eq "1",
$pkg->{priority})
if -e $path;
} }
} }

View file

@ -16,6 +16,10 @@
, # Whether to ignore collisions or abort. , # Whether to ignore collisions or abort.
ignoreCollisions ? false ignoreCollisions ? false
, # If there is a collision, check whether the contents and permissions match
# and only if not, throw a collision error.
checkCollisionContents ? true
, # The paths (relative to each element of `paths') that we want to , # The paths (relative to each element of `paths') that we want to
# symlink (e.g., ["/bin"]). Any file not inside any of the # symlink (e.g., ["/bin"]). Any file not inside any of the
# directories in the list is not symlinked. # directories in the list is not symlinked.
@ -39,7 +43,9 @@
}: }:
runCommand name runCommand name
rec { inherit manifest ignoreCollisions passthru meta pathsToLink extraPrefix postBuild buildInputs; rec {
inherit manifest ignoreCollisions checkCollisionContents passthru
meta pathsToLink extraPrefix postBuild buildInputs;
pkgs = builtins.toJSON (map (drv: { pkgs = builtins.toJSON (map (drv: {
paths = paths =
[ drv ] [ drv ]