diff --git a/src/lib/libsyscall.js b/src/lib/libsyscall.js index 1e1976cf41875..4853b70861623 100644 --- a/src/lib/libsyscall.js +++ b/src/lib/libsyscall.js @@ -930,14 +930,15 @@ var SyscallsLibrary = { __syscall_faccessat: (dirfd, path, amode, flags) => { path = SYSCALLS.getStr(path); #if ASSERTIONS - assert(!flags || flags == {{{ cDefs.AT_EACCESS }}}); + assert(!(flags & ~({{{ cDefs.AT_EACCESS }}} | {{{ cDefs.AT_SYMLINK_NOFOLLOW }}}))); #endif path = SYSCALLS.calculateAt(dirfd, path); if (amode & ~{{{ cDefs.S_IRWXO }}}) { // need a valid mode return -{{{ cDefs.EINVAL }}}; } - var lookup = FS.lookupPath(path, { follow: true }); + var nofollow = !!(flags & {{{ cDefs.AT_SYMLINK_NOFOLLOW }}}); + var lookup = FS.lookupPath(path, { follow: !nofollow }); var node = lookup.node; if (!node) { return -{{{ cDefs.ENOENT }}}; diff --git a/test/unistd/access.c b/test/unistd/access.c index 491a106dea83d..3b0d9c6219ed5 100644 --- a/test/unistd/access.c +++ b/test/unistd/access.c @@ -76,6 +76,27 @@ void test_lchmod() { #endif } +void test_symlink_nofollow() { +#if defined(MEMFS) + FILE* f = fopen("target_file", "w"); + fclose(f); + symlink("target_file", "test_symlink"); + symlink("dangling_target", "dangling_symlink"); + + // With AT_SYMLINK_NOFOLLOW, faccessat checks the symlink itself, not the target + assert(faccessat(AT_FDCWD, "test_symlink", F_OK, AT_SYMLINK_NOFOLLOW) == 0); + + // Dangling symlink should still be found with AT_SYMLINK_NOFOLLOW (symlink exists) + assert(faccessat(AT_FDCWD, "dangling_symlink", F_OK, AT_SYMLINK_NOFOLLOW) == 0); + + // Without AT_SYMLINK_NOFOLLOW, dangling symlink should fail + assert(faccessat(AT_FDCWD, "dangling_symlink", F_OK, 0) != 0); + + // Combined flags should work + assert(faccessat(AT_FDCWD, "test_symlink", F_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW) == 0); +#endif +} + void test_chmod_errors() { EM_ASM( var ex; @@ -119,6 +140,7 @@ int main() { test_rename(); test_fchmod(); test_lchmod(); + test_symlink_nofollow(); test_chmod_errors(); return 0;