Skip to content

Test262: accept ENOTEMPTY and EEXIST in fs/rename.t.mjs - #1104

Merged
VadimZhestikov merged 1 commit into
masterfrom
fix-rename-test-errno-ubuntu2604
Jul 29, 2026
Merged

Test262: accept ENOTEMPTY and EEXIST in fs/rename.t.mjs#1104
VadimZhestikov merged 1 commit into
masterfrom
fix-rename-test-errno-ubuntu2604

Conversation

@VadimZhestikov

@VadimZhestikov VadimZhestikov commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Problem

The test/fs/rename.t.mjs Test262 test started failing on the
ubuntu-26.04 CI runners (both amd64 and arm64), e.g.
run #228:

test/fs/rename.t.mjs:
Test262:AsyncTestFailure:Error: fs.unlinkSync error 1
test/fs/rename.t.mjs FAILED
TOTAL: FAILED [155/156]
make: *** [build/Makefile:970: test262_njs] Error 1

All 6047 njs unit tests pass and the build succeeds; only this single
Test262 case fails, and only on ubuntu-26.04 (the newest distro in the
matrix). The commit that triggered the run only touched SECURITY.md, so
the failure is environmental rather than a code regression.

Root cause

The test renames a regular file onto its (non-empty) parent directory and
expects the syscall to fail with ENOTDIR or EISDIR:

} catch (e) {
    if (e.syscall != 'rename'
        || (e.code != 'ENOTDIR' && e.code != 'EISDIR'))
    {
        reject(new Error('fs.unlinkSync error 1'));
    }
}

Renaming a file onto an existing directory is permitted by POSIX to fail
with EISDIR, ENOTDIR, ENOTEMPTY or EEXIST, and the exact errno
depends on the kernel and filesystem. Older kernels return ENOTDIR here,
but the newer kernel on the ubuntu-26.04 runners returns ENOTEMPTY (or
EEXIST), which the test did not accept, so the promise rejects.

The test file has not changed since 2024-12 (dfec49b1), confirming this
is a kernel behavior difference rather than an njs bug.

Fix

Accept ENOTEMPTY and EEXIST in addition to ENOTDIR/EISDIR in all
three variants of the test (sync, callback and promise based). Genuinely
unexpected errors are still rejected.

Verification

  • Built njs with the patch and ran the full Test262 suite locally:
    TOTAL: PASSED [156/156].
  • Verified the accept-list logic with the njs binary: ENOTEMPTY/EEXIST
    are now accepted while an unrelated code such as ENOENT is still
    rejected.
  • Verified in sandbox https://github.com/nginx/njs-sandbox/actions/runs/30495040595
  • The authoritative check is the buildbot CI on this PR / after merge,
    which exercises the real ubuntu-26.04 runners.

Root cause confirmed: kernel behavior change (not build flags).

 A temporary diagnostic in nginx/njs-sandbox captured the actual errno plus environment on the ubuntu-26.04
 runners (amd64 + arm64, all three variants):

 code=ENOTEMPTY errno=39
 kernel: 7.0.0-1008-aws
 fstype: tmpfs

 So rename(file, "dir/") (file onto its non-empty parent dir, trailing slash) returns ENOTEMPTY (39) on Linux 7.
 0, where older kernels return ENOTDIR (20). Reproducing on the same filesystem type (tmpfs) under an older
 kernel still gives ENOTDIR, isolating the difference to the kernel version. CI build flags were ruled out —
 they're ordinary codegen/hardening flags with no feature macros that could redirect rename(); a syscall's
 errno is decided by the kernel, not the compiler.

 Both codes are POSIX-legal for renaming onto a non-empty directory, so accepting ENOTEMPTY/EEXIST here is the
 correct fix.

Pinned the exact upstream kernel commit behind the behavior change.

 The errno flip comes from Linux commit 5c8752729970 ("VFS/nfsd/ovl: introduce start_renaming() and
 end_renaming()", NeilBrown, 2025-11-13), first released in v7.0 (not in v6.18). The AWS runner kernel 7.0.0-
 1008-aws includes it; my local 6.18 does not.

 Our test renames a file onto its own non-empty parent dir with a trailing slash, so two error conditions apply
 at once — ENOTDIR (trailing slash on a non-dir target) and ENOTEMPTY (target is an ancestor of source). The
 winner depends on check ordering in fs/namei.c:

 - ≤ v6.18 (do_renameat2): the trailing-slash -ENOTDIR check runs first → ENOTDIR (20).
 - v7.0+: the commit moved the "target is an ancestor of source" check into the new __start_renaming() lookup
   helper, which runs before the caller's trailing-slash check → ENOTEMPTY (39):
   if (d2 == trap) {
       /* target is an ancestor of source */
       ...
       err = -ENOTEMPTY;
       goto out_dput_d2;
   }

 This looks like an unintended side effect of a lookup/lock refactor (the commit's goal was factoring code for
 nfsd/overlayfs), not a deliberate errno change. Both codes are POSIX-legal here, so accepting ENOTEMPTY/EEXIST
 remains the correct, kernel-independent fix.

 Commit: https://github.com/torvalds/linux/commit/5c8752729970cc2323ba86817254749f7f21f163

Renaming a regular file onto an existing directory is permitted by POSIX
to fail with EISDIR, ENOTDIR, ENOTEMPTY or EEXIST, and the exact errno
depends on the kernel and filesystem.  The test only accepted ENOTDIR
and EISDIR, so it began failing on the ubuntu-26.04 CI runners whose
newer kernel returns ENOTEMPTY (or EEXIST) for this case.

Accept ENOTEMPTY and EEXIST in all three variants (sync, callback and
promise based) to make the test robust across kernels while still
rejecting genuinely unexpected errors.
@VadimZhestikov
VadimZhestikov force-pushed the fix-rename-test-errno-ubuntu2604 branch from 55e4bd4 to a0248b3 Compare July 29, 2026 22:05
@VadimZhestikov VadimZhestikov self-assigned this Jul 29, 2026

@route443 route443 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Maryna-f5 Maryna-f5 assigned Maryna-f5 and unassigned Maryna-f5 Jul 29, 2026
@Maryna-f5
Maryna-f5 requested review from Maryna-f5 and removed request for Maryna-f5 July 29, 2026 22:53
@VadimZhestikov
VadimZhestikov merged commit f109134 into master Jul 29, 2026
2 checks passed
@VadimZhestikov
VadimZhestikov deleted the fix-rename-test-errno-ubuntu2604 branch July 30, 2026 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants