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..88996f4998e12 100644 --- a/test/unistd/access.c +++ b/test/unistd/access.c @@ -76,6 +76,29 @@ void test_lchmod() { #endif } +void test_symlink_nofollow() { + 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 + printf("F_OK symlink nofollow: %s\n", + faccessat(AT_FDCWD, "test_symlink", F_OK, AT_SYMLINK_NOFOLLOW) == 0 ? "OK" : strerror(errno)); + + // Dangling symlink should still be found with AT_SYMLINK_NOFOLLOW (symlink exists) + printf("F_OK dangling nofollow: %s\n", + faccessat(AT_FDCWD, "dangling_symlink", F_OK, AT_SYMLINK_NOFOLLOW) == 0 ? "OK" : strerror(errno)); + + // Without AT_SYMLINK_NOFOLLOW, dangling symlink should fail + printf("F_OK dangling follow: %s\n", + faccessat(AT_FDCWD, "dangling_symlink", F_OK, 0) == 0 ? "OK" : strerror(errno)); + + // Combined flags should work + printf("F_OK symlink eaccess|nofollow: %s\n", + faccessat(AT_FDCWD, "test_symlink", F_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW) == 0 ? "OK" : strerror(errno)); +} + void test_chmod_errors() { EM_ASM( var ex; @@ -119,6 +142,7 @@ int main() { test_rename(); test_fchmod(); test_lchmod(); + test_symlink_nofollow(); test_chmod_errors(); return 0; diff --git a/test/unistd/access.out b/test/unistd/access.out index 43454609d4f2c..803923c9923ae 100644 --- a/test/unistd/access.out +++ b/test/unistd/access.out @@ -32,3 +32,7 @@ F_OK('filetorename'): -1 F_OK('renamedfile'): 0 F_OK('filetorename'): -1 F_OK('renamedfile'): 0 +F_OK symlink nofollow: OK +F_OK dangling nofollow: OK +F_OK dangling follow: No such file or directory +F_OK symlink eaccess|nofollow: OK