From 2a7db08c20811139c9410b65499d27eca47e793c Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 4 Aug 2024 00:59:40 -0400 Subject: [PATCH] Remove returned bool from access and faccessat in sys/linux. Switch to using AT_EMPTY_PATH to execve with file descriptors. --- core/os/os2/file_linux.odin | 3 +- core/os/os2/process_linux.odin | 52 ++++++++++++++++++++++++---------- core/sys/linux/sys.odin | 14 ++++----- 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 9f1559fdd..8887c8274 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -386,8 +386,7 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { _exists :: proc(name: string) -> bool { TEMP_ALLOCATOR_GUARD() name_cstr, _ := temp_cstring(name) - res, errno := linux.access(name_cstr, linux.F_OK) - return !res && errno == .NONE + return linux.access(name_cstr, linux.F_OK) == .NONE } /* For reading Linux system files that stat to size 0 */ diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin index 553be5fda..f645131b2 100644 --- a/core/os/os2/process_linux.odin +++ b/core/os/os2/process_linux.odin @@ -300,6 +300,12 @@ _Sys_Process_Attributes :: struct {} @(private="package") _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { + _has_executable_permissions :: proc(fd: linux.Fd) -> bool { + backing: [48]u8 + _ = fmt.bprintf(backing[:], "/proc/self/fd/%d", fd) + return linux.access(cstring(&backing[0]), linux.X_OK) == .NONE + } + TEMP_ALLOCATOR_GUARD() if len(desc.command) == 0 { @@ -314,38 +320,54 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { return process, _get_platform_error(errno) } } + defer if desc.working_dir != "" { + linux.close(dir_fd) + } // search PATH if just a plain name is provided + exe_fd: linux.Fd executable_name := desc.command[0] - executable_path: cstring if strings.index_byte(executable_name, '/') == -1 { path_env := get_env("PATH", temp_allocator()) path_dirs := filepath.split_list(path_env, temp_allocator()) + found: bool for dir in path_dirs { - executable_path = fmt.caprintf("%s/%s", dir, executable_name, temp_allocator()) - fail: bool - if fail, errno = linux.faccessat(dir_fd, executable_path, linux.F_OK); errno == .NONE && !fail { - found = true - break + exe_path := fmt.caprintf("%s/%s", dir, executable_name, allocator=temp_allocator()) + if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE { + continue } + if !_has_executable_permissions(exe_fd) { + linux.close(exe_fd) + continue + } + found = true + break } if !found { // check in cwd to match windows behavior - executable_path = fmt.caprintf("./%s", name, temp_allocator()) - fail: bool - if fail, errno = linux.faccessat(dir_fd, executable_path, linux.F_OK); errno != .NONE || fail { + exe_path := fmt.caprintf("./%s", executable_name, allocator=temp_allocator()) + if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE { return process, .Not_Exist } + if !_has_executable_permissions(exe_fd) { + linux.close(exe_fd) + return process, .Permission_Denied + } } } else { - executable_path = temp_cstring(executable_name) or_return + exe_path := temp_cstring(executable_name) or_return + if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE { + return process, _get_platform_error(errno) + } + if !_has_executable_permissions(exe_fd) { + linux.close(exe_fd) + return process, .Permission_Denied + } } - not_exec: bool - if not_exec, errno = linux.faccessat(dir_fd, executable_path, linux.F_OK | linux.X_OK); errno != .NONE || not_exec { - return process, errno == .NONE ? .Permission_Denied : _get_platform_error(errno) - } + // At this point, we have an executable. + defer linux.close(exe_fd) // args and environment need to be a list of cstrings // that are terminated by a nil pointer. @@ -409,7 +431,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { } } - if errno = linux.execveat(dir_fd, executable_path, &cargs[0], env); errno != .NONE { + if errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH}); errno != .NONE { intrinsics.trap() } unreachable() diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index ec7357c48..d756f0ece 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -279,13 +279,13 @@ writev :: proc "contextless" (fd: Fd, iov: []IO_Vec) -> (int, Errno) { Available since Linux 1.0. For ARM64 available since Linux 2.6.16. */ -access :: proc "contextless" (name: cstring, mode: Mode = F_OK) -> (bool, Errno) { +access :: proc "contextless" (name: cstring, mode: Mode = F_OK) -> (Errno) { when ODIN_ARCH == .arm64 { ret := syscall(SYS_faccessat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode) - return errno_unwrap(ret, bool) + return Errno(-ret) } else { ret := syscall(SYS_access, cast(rawptr) name, transmute(u32) mode) - return errno_unwrap(ret, bool) + return Errno(-ret) } } @@ -2616,9 +2616,9 @@ fchmodat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode, flags: FD_ Checks the user permissions for a file at specified dirfd. Available since Linux 2.6.16. */ -faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) -> (bool, Errno) { +faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) -> (Errno) { ret := syscall(SYS_faccessat, dirfd, cast(rawptr) name, transmute(u32) mode) - return errno_unwrap(ret, bool) + return Errno(-ret) } /* @@ -2916,9 +2916,9 @@ pidfd_getfd :: proc "contextless" (pidfd: Pid_FD, fd: Fd, flags: i32 = 0) -> (Fd Checks the user permissions for a file at specified dirfd (with flags). Available since Linux 5.8. */ -faccessat2 :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK, flags: FD_Flags = FD_Flags{}) -> (bool, Errno) { +faccessat2 :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK, flags: FD_Flags = FD_Flags{}) -> (Errno) { ret := syscall(SYS_faccessat2, dirfd, cast(rawptr) name, transmute(u32) mode, transmute(i32) flags) - return errno_unwrap(ret, bool) + return Errno(-ret) } // TODO(flysand): process_madvise