testers.testEqualContents: Fix diffoscope false positive#393381
testers.testEqualContents: Fix diffoscope false positive#393381roberth wants to merge 1 commit intoNixOS:masterfrom
Conversation
|
@ofborg build tests.testers.testEqualContents |
| printf '%s\n' "$assertion" | ||
| if ! diffoscope --no-progress --text-color=always --exclude-directory-metadata=no -- "$actual" "$expected" | ||
| # --exclude-directory-metadata=yes: This reports false positives such as the internal directory sizes, | ||
| # so we work around this by excluding the directory metadata, and then checking the executable bits later. |
There was a problem hiding this comment.
Do we need to check executable bit for directories? I assume directories should always have executable bit set in Nix store since it cannot be represented in NAR.
Hm, though this doesn’t seem to be the case now…
nix-repl> :b pkgs.runCommand "foo" { } "mkdir -p $out/foo && chmod -x $out/foo"
This derivation produced the following outputs:
out -> /nix/store/1xnsf9n8ar37vhbb0w6fjciaf8bkz7w1-foo
$ ls -al /nix/store/1xnsf9n8ar37vhbb0w6fjciaf8bkz7w1-foo
total 0
dr-xr-xr-x 1 root root 6 Jan 1 1970 ./
drwxrwxr-t 1 root nixbld 11M Mar 26 15:41 ../
dr--r--r-- 1 root root 0 Jan 1 1970 foo/
$ ls -al /nix/store/1xnsf9n8ar37vhbb0w6fjciaf8bkz7w1-foo/foo
ls: cannot access '/nix/store/1xnsf9n8ar37vhbb0w6fjciaf8bkz7w1-foo/foo/.': Permission denied
ls: cannot access '/nix/store/1xnsf9n8ar37vhbb0w6fjciaf8bkz7w1-foo/foo/..': Permission denied
total 0
d????????? ? ? ? ? ? ./
d????????? ? ? ? ? ? ../
$ nix nar pack /nix/store/1xnsf9n8ar37vhbb0w6fjciaf8bkz7w1-foo >foo.nar
$ nix nar ls --json --recursive foo.nar /
{"entries":{"foo":{"entries":{},"type":"directory"}},"type":"directory"}
nix-repl> :b pkgs.runCommand "foo" { } "mkdir -p $out/foo && touch $out/foo/bar && chmod -x $out/foo"
This derivation produced the following outputs:
out -> /nix/store/6nbxwp349lfbk3yrd2mgv3nya78s7bp8-foo
$ ls -al /nix/store/6nbxwp349lfbk3yrd2mgv3nya78s7bp8-foo
total 0
dr-xr-xr-x 1 root root 6 Jan 1 1970 ./
drwxrwxr-t 1 root nixbld 11M Mar 26 15:49 ../
dr--r--r-- 1 root root 6 Jan 1 1970 foo/
$ ls -al /nix/store/6nbxwp349lfbk3yrd2mgv3nya78s7bp8-foo/foo
ls: cannot access '/nix/store/6nbxwp349lfbk3yrd2mgv3nya78s7bp8-foo/foo/.': Permission denied
ls: cannot access '/nix/store/6nbxwp349lfbk3yrd2mgv3nya78s7bp8-foo/foo/..': Permission denied
ls: cannot access '/nix/store/6nbxwp349lfbk3yrd2mgv3nya78s7bp8-foo/foo/bar': Permission denied
total 0
d????????? ? ? ? ? ? ./
d????????? ? ? ? ? ? ../
-????????? ? ? ? ? ? bar
$ nix nar pack /nix/store/6nbxwp349lfbk3yrd2mgv3nya78s7bp8-foo >foo.nar
error: getting status of '/nix/store/6nbxwp349lfbk3yrd2mgv3nya78s7bp8-foo/foo/bar': Permission denied
There was a problem hiding this comment.
IIUC diffoscope considers the executable bit of a file to be part of the directory, and not part of a file, similarly to the typical unix file system data model or even that of Git. This is different from the NAR data model.
directory entry:
name -> mode, content/inode
^^^^^^^^^^^^^--- diffoscope unit of composition
NAR item ("File System Object")
mode, content
^^^^^^^^^^^^^^--------- NAR unit of composition
store path content root
Do we need to check executable bit for directories?
Indeed NAR does not record this bit, so we shouldn't encounter any differences there.
This seems to be a bug in Nix store path canonicalization.
What kind of store did you use to achieve this - what does nix store ping say?
There was a problem hiding this comment.
What kind of store did you use to achieve this - what does nix store ping say?
This is a default NixOS installation, user is trusted.
$ nix store ping
warning: 'nix store ping' is a deprecated alias for 'nix store info'
Store URL: daemon
Version: 2.25.5
Trusted: 1
cat /etc/nix/nix.conf
# WARNING: this file is generated from the nix.* options in
# your NixOS configuration, typically
# /etc/nixos/configuration.nix. Do not edit it!
allowed-users = *
auto-optimise-store = false
builders =
cores = 0
experimental-features = nix-command flakes
max-jobs = auto
require-sigs = true
sandbox = true
sandbox-fallback = false
substituters = https://cache.nixos.org/
system-features = nixos-test benchmark big-parallel kvm
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
trusted-substituters =
trusted-users = root @wheel
use-xdg-base-directories = true
warn-dirty = false
extra-sandbox-paths =
I can also reproduce this on stable Nix version from nixos-24.11:
Details
$ nix store info
Store URL: daemon
Version: 2.24.12
Trusted: 1
$ nix repl
Nix 2.24.12
Type :? for help.
nix-repl> :lf nixpkgs
Added 16 variables.
nix-repl> lib.version
"24.11.20250327.b9c013a"
nix-repl> pkgs = legacyPackages.x86_64-linux
nix-repl> :b pkgs.runCommand "foo" { } "mkdir -p $out/foo && touch $out/foo/bar && chmod -x $out/foo"
This derivation produced the following outputs:
out -> /nix/store/q4xw50il9f6fi6xs644wisqdx0vr6x1f-foo
[1 built, 0.0 MiB DL]
$ nix nar pack /nix/store/q4xw50il9f6fi6xs644wisqdx0vr6x1f-foo >/dev/null
error: getting status of '/nix/store/q4xw50il9f6fi6xs644wisqdx0vr6x1f-foo/foo/bar': Permission denied
$ stat /nix/store/q4xw50il9f6fi6xs644wisqdx0vr6x1f-foo/foo
File: /nix/store/q4xw50il9f6fi6xs644wisqdx0vr6x1f-foo/foo
Size: 6 Blocks: 0 IO Block: 4096 directory
Device: 0,43 Inode: 161713277 Links: 1
Access: (0444/dr--r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2025-03-27 19:51:13.000000000 +0300
Modify: 1970-01-01 03:00:01.000000000 +0300
Change: 2025-03-27 19:51:13.297001247 +0300
Birth: 2025-03-27 19:51:13.292001218 +0300
Alternatively,
nix build --impure --expr 'with import ./. { };
let
script = "mkdir -p $out/foo && touch $out/foo/bar && chmod -x $out/foo";
badDir = runCommand "bad-directory-permissions" { } script;
in runCommand "cat-file" { inherit badDir; } "stat $badDir/foo && cat $badDir/foo/bar"'
cat-file> File: /nix/store/99swczlgqpmkqvsd8vzzlqygxw56giqz-bad-directory-permissions/foo
cat-file> Size: 6 Blocks: 0 IO Block: 4096 directory
cat-file> Device: 0,33 Inode: 242781643 Links: 1
cat-file> Access: (0444/dr--r--r--) Uid: (65534/ nobody) Gid: (65534/ nogroup)
cat-file> Access: 2025-03-27 16:59:12.000000000 +0000
cat-file> Modify: 1970-01-01 00:00:01.000000000 +0000
cat-file> Change: 2025-03-27 16:59:12.461890171 +0000
cat-file> Birth: 2025-03-27 16:59:12.461890171 +0000
cat-file> cat: /nix/store/99swczlgqpmkqvsd8vzzlqygxw56giqz-bad-directory-permissions/foo/bar: Permission denied
There was a problem hiding this comment.
Looks like permissions are set in canonicaliseTimestampAndPermissions and 0444 is not updated to 0555 for directories.
E.g. 0554 is canonicalized, but not 0444.
Details
$ nix build -L --impure --expr 'with import ./. { };
let
script = "mkdir -p $out/foo && touch $out/foo/bar && chmod o-x $out/foo";
badDir = runCommand "bad-directory-permissions" { } script;
in runCommand "cat-file" { inherit badDir; } "stat $badDir/foo && cat $badDir/foo/bar && touch $out"'
cat-file> File: /nix/store/42fsalzfn075vhhxbmrvbma37v3wf5wz-bad-directory-permissions/foo
cat-file> Size: 6 Blocks: 0 IO Block: 4096 directory
cat-file> Device: 0,33 Inode: 242782051 Links: 1
cat-file> Access: (0555/dr-xr-xr-x) Uid: (65534/ nobody) Gid: (65534/ nogroup)
cat-file> Access: 2025-03-27 17:18:14.000000000 +0000
cat-file> Modify: 1970-01-01 00:00:01.000000000 +0000
cat-file> Change: 2025-03-27 17:18:14.682332844 +0000
cat-file> Birth: 2025-03-27 17:18:14.682332844 +0000
Perhaps
-if (mode != 0444 && mode != 0555) {
+bool is_dir = S_ISDIR(st.st_mode);
+if ((mode != 0444 || is_dir) && mode != 0555) {
mode = (st.st_mode & S_IFMT)
| 0444
- | (st.st_mode & S_IXUSR ? 0111 : 0);
+ | (st.st_mode & S_IXUSR || is_dir ? 0111 : 0);
if (chmod(path.c_str(), mode) == -1)
throw SysError("changing mode of '%1%' to %2$o", path, mode);
}There was a problem hiding this comment.
Thank you. I've forwarded this to nix, NixOS/nix#12786
It’s fine as a temporary workaround, but I think we should submit a patch that removes
Note: I don’t have Debian Salsa account and registration confirmation can take up to a few days (see https://wiki.debian.org/Salsa/Doc#Users). |
|
This issue should be fixed upstream in https://diffoscope.org/news/diffoscope-292-released (starting with version 293). |
The underlying bug seems to have been fixed in diffoscope 293 [1] [2]. Our nixpkgs input has 295. [1]: NixOS/nixpkgs#393381 (comment) [2]: https://diffoscope.org/news/diffoscope-292-released/
philiptaron
left a comment
There was a problem hiding this comment.
Now that both diffoscope and Nix have fixes, we can close this without merging I believe.
|
Thanks again, tie! |
Not excited about this solution, but it gets the job done.
Ideally diffoscope could implement NAR-style content equality semantics for files and directories, but until it does, we need a fix.
Things done
nix.conf? (See Nix manual)sandbox = relaxedsandbox = truenix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage./result/bin/)Add a 👍 reaction to pull requests you find important.