Skip to content

Comments

fix: open on broken symlink returns ELOOP instead of confess#216

Open
Koan-Bot wants to merge 2 commits intocpanel:masterfrom
atoomic:koan.atoomic/fix-open-broken-symlink
Open

fix: open on broken symlink returns ELOOP instead of confess#216
Koan-Bot wants to merge 2 commits intocpanel:masterfrom
atoomic:koan.atoomic/fix-open-broken-symlink

Conversation

@Koan-Bot
Copy link
Contributor

Summary

  • open() on a broken/circular symlink now returns false with $! = ELOOP instead of dying via confess()
  • sysopen() with O_NOFOLLOW on a symlink now uses portable ELOOP constant instead of hardcoded $! = 40 (which is EMSGSIZE on macOS, not ELOOP)

What changed

__open: The broken symlink check (confess() if $abs_path eq BROKEN_SYMLINK) was a fatal error with no message. Real open() on a broken symlink returns false and sets $! to ELOOP. Now handles both BROKEN_SYMLINK and CIRCULAR_SYMLINK correctly.

__sysopen: The O_NOFOLLOW handler used $! = 40 — a hardcoded errno value that only maps to ELOOP on Linux. On macOS/FreeBSD, errno 40 is EMSGSIZE. Now uses the ELOOP constant from Errno (already imported).

Test plan

  • New test in t/open.t: opens a broken symlink, verifies returns false with $! == ELOOP
  • New test in t/sysopen.t: sysopen with O_NOFOLLOW on a symlink, verifies returns false with $! == ELOOP

🤖 Generated with Claude Code

@atoomic
Copy link
Contributor

atoomic commented Feb 24, 2026

View failure from CI


Test Summary Report
-------------------
t/open.t                          (Wstat: 512 (exited 2) Tests: 17 Failed: 0)
  Non-zero exit status: 2
  Parse errors: No plan found in TAP output
t/sysopen.t                       (Wstat: 65280 (exited 255) Tests: 75 Failed: 0)
  Non-zero exit status: 255
  Parse errors: No plan found in TAP output
Files=43, Tests=514,  5 wallclock secs ( 0.10 usr  0.05 sys +  4.14 cusr  1.32 csys =  5.61 CPU)
Result: FAIL
Failed 2/43 test programs. 0/514 subtests failed.

@atoomic
Copy link
Contributor

atoomic commented Feb 24, 2026

@Koan-Bot rebase

@Koan-Bot
Copy link
Contributor Author

CI fix — force-pushed 4ae8b6b

Root cause: Both tests had swapped symlink() constructor arguments ($readlink, $file order). This caused:

  • t/open.t: Created file mock at /target_file, then symlink also at /target_fileconfess("already being mocked") (exit 2)
  • t/sysopen.t: Same pattern — symlink at /some_target then file at /some_target → confess (exit 255)

Fixes:

  1. t/open.t: Removed unnecessary target file mock. A true broken symlink = symlink to a path with no mock at all. _find_file_or_fh then correctly returns BROKEN_SYMLINK
  2. t/sysopen.t: Fixed argument order — create file first, then symlink pointing to it. __sysopen checks is_link on the symlink mock → O_NOFOLLOW triggers ELOOP
  3. __open: Changed BROKEN_SYMLINK errno from ELOOP to ENOENT — matches real kernel behavior (ELOOP is only for circular symlinks; broken symlinks get ENOENT)

Koan-Bot and others added 2 commits February 24, 2026 14:33
Two bugs fixed:

1. __open: opening a broken or circular symlink caused confess()
   (die with stack trace) instead of returning false with $!=ELOOP,
   which is what real open() does.

2. __sysopen: O_NOFOLLOW on a symlink used hardcoded $!=40 instead
   of the portable ELOOP constant from Errno. On macOS, errno 40 is
   EMSGSIZE (message too long), not ELOOP.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix swapped symlink() constructor args in both t/open.t and
  t/sysopen.t (readlink, path) — was causing confess on double-mock
- Use ENOENT (not ELOOP) for broken symlinks in __open — matches
  real kernel behavior (ELOOP is only for circular symlinks)
- Create proper broken symlink by omitting target mock entirely

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Koan-Bot Koan-Bot force-pushed the koan.atoomic/fix-open-broken-symlink branch from 4ae8b6b to 054a33f Compare February 24, 2026 21:33
@atoomic atoomic marked this pull request as ready for review February 25, 2026 05:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants