Skip to content

fix(libc): keep a trailing slash on paths under a mount alias - #4681

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
milescoviello:fix/alias-trailing-slash
Oct 14, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
milescoviello:fix/alias-trailing-slash

Conversation

@milescoviello

@milescoviello milescoviello commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What

Guest path resolution trimmed the trailing slash of a path under a mount alias (/savedata0, /download0, /app0 and the others), so the host saw file for file/. On main, with a regular file f under an alias:

call on <alias>/f/ main FreeBSD 11.4, and the same path outside an alias
unlink, remove, sceKernelUnlink 0, the file is deleted ENOTDIR
rename("f/", "g") 0, the file is moved ENOTDIR
open(O_RDONLY) a descriptor for the file ENOTDIR
chmod, access 0 ENOTDIR

FreeBSD 11.4 rejects it in lookup() (sys/kern/vfs_lookup.c):

	/*
	 * If we're processing a path with a trailing slash,
	 * check that the end result is a directory.
	 */
	if ((cnp->cn_flags & TRAILINGSLASH) && dp->v_type != VDIR) {
		error = ENOTDIR;
		goto bad2;
	}

#2300 made stat follow this; the other calls still acted on the file because ResolveAlias drops the slash before the host sees it, while a path outside an alias keeps it.

On Linux and macOS a path under an alias now keeps its trailing slash on the host path, exactly as paths outside an alias already do, so the host fails with ENOTDIR for a file while directories with a trailing slash (stat, mkdir, rmdir) keep working. Windows keeps the previous behaviour, since its trailing-separator handling differs (#4513) and I could not test it; an entry in docs/dev/debt/libc.md records that.

Tested

Ubuntu 24.04, GCC 13.3 (CI's compiler), Release with CI's configure flags, on main 1acfa7f. guest_filesystem gained checks under its parent-test-mount alias: unlink, remove, rename, open and sceKernelUnlink on file/ fail with ENOTDIR and every file is still there, stat of Existing/ reports a directory, and mkdir/rmdir of slash-directory/ work.

  • Red: with main's General.cpp the test fails at the first new check (unlink returns 0 and deletes the file).
  • Green: guest_filesystem passes, also in 30 repeated runs; all guest_* tests except guest_video_out_* pass (310/310, two runs).

Windows: @johnmaia built and ran it on Windows 11 with WinLibs GCC 15.2.0, merged into main 3faa9cb. The full suite passes 1074 of 1074, guest_filesystem included. The new checks are skipped there, so this shows only that nothing else changes on Windows.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: none
  • AI-assisted: yes

🤖 Generated with Claude Code

@johnmaia

Copy link
Copy Markdown
Contributor

Windows build check. Setup: Windows 11 (26300), WinLibs GCC 15.2.0 r7, Release, BUILD_TESTING=ON, ctest -j4 --timeout 300, RTX 4080. Head 516e90d merged into main 3faa9cb.

It builds, and the full suite passes 1074 of 1074, guest_filesystem included. The new trailing-slash checks are #ifndef _WIN32, so this only shows that nothing else changes on Windows.

AI-assisted: yes (Claude Code)

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the build and scoped ctest on the PR merged onto main, guest_filesystem repeated 5 times, and that the new checks fail with ResolveAlias reverted to main. The Windows runs by @johnmaia and @xXPBSXx confirm nothing else changes there. Merging after the full Build is green.

Guest path resolution trimmed the trailing slash of a path under a
mount alias (/savedata0, /download0, /app0 and the others), so the host
saw "file" for "file/": unlink, remove, sceKernelUnlink and rename
removed or moved the file, open opened it, and chmod and access
succeeded on it. The same path outside an alias already failed with
ENOTDIR, because the host got the slash.

FreeBSD 11.4 rejects it in lookup() (sys/kern/vfs_lookup.c):

	/*
	 * If we're processing a path with a trailing slash,
	 * check that the end result is a directory.
	 */
	if ((cnp->cn_flags & TRAILINGSLASH) && dp->v_type != VDIR) {
		error = ENOTDIR;
		goto bad2;
	}

On Linux and macOS a path under an alias now keeps its trailing slash
on the host path, as paths outside an alias do, so the host fails with
ENOTDIR for a file and directories with a trailing slash still work.
Windows keeps the previous behaviour; TechnicalDebt records it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 14, 2026
@milescoviello
milescoviello force-pushed the fix/alias-trailing-slash branch from 516e90d to ccfadfb Compare October 14, 2026 01:33
@milescoviello

Copy link
Copy Markdown
Contributor Author

Rebased onto main 34355ee. The only conflict was docs/dev/debt/libc.md, next to the new tzname entry; both entries are kept. After the rebase, the build is clean, guest_filesystem passes 5/5 and all 354 guest_* tests pass (Ubuntu 24.04, GCC 13.3, Release).

AI-assisted: yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants