Browse Source

Remove returned bool from access and faccessat in sys/linux.
Switch to using AT_EMPTY_PATH to execve with file descriptors.

jason 1 year ago
parent
commit
2a7db08c20
3 changed files with 45 additions and 24 deletions
  1. 1 2
      core/os/os2/file_linux.odin
  2. 37 15
      core/os/os2/process_linux.odin
  3. 7 7
      core/sys/linux/sys.odin

+ 1 - 2
core/os/os2/file_linux.odin

@@ -386,8 +386,7 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
 _exists :: proc(name: string) -> bool {
 _exists :: proc(name: string) -> bool {
 	TEMP_ALLOCATOR_GUARD()
 	TEMP_ALLOCATOR_GUARD()
 	name_cstr, _ := temp_cstring(name)
 	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 */
 /* For reading Linux system files that stat to size 0 */

+ 37 - 15
core/os/os2/process_linux.odin

@@ -300,6 +300,12 @@ _Sys_Process_Attributes :: struct {}
 
 
 @(private="package")
 @(private="package")
 _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 _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()
 	TEMP_ALLOCATOR_GUARD()
 
 
 	if len(desc.command) == 0 {
 	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)
 			return process, _get_platform_error(errno)
 		}
 		}
 	}
 	}
+	defer if desc.working_dir != "" {
+		linux.close(dir_fd)
+	}
 
 
 	// search PATH if just a plain name is provided
 	// search PATH if just a plain name is provided
+	exe_fd: linux.Fd
 	executable_name := desc.command[0]
 	executable_name := desc.command[0]
-	executable_path: cstring
 	if strings.index_byte(executable_name, '/') == -1 {
 	if strings.index_byte(executable_name, '/') == -1 {
 		path_env := get_env("PATH", temp_allocator())
 		path_env := get_env("PATH", temp_allocator())
 		path_dirs := filepath.split_list(path_env, temp_allocator())
 		path_dirs := filepath.split_list(path_env, temp_allocator())
+
 		found: bool
 		found: bool
 		for dir in path_dirs {
 		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 {
 		if !found {
 			// check in cwd to match windows behavior
 			// 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
 				return process, .Not_Exist
 			}
 			}
+			if !_has_executable_permissions(exe_fd) {
+				linux.close(exe_fd)
+				return process, .Permission_Denied
+			}
 		}
 		}
 	} else {
 	} 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
 	// args and environment need to be a list of cstrings
 	// that are terminated by a nil pointer.
 	// 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()
 			intrinsics.trap()
 		}
 		}
 		unreachable()
 		unreachable()

+ 7 - 7
core/sys/linux/sys.odin

@@ -279,13 +279,13 @@ writev :: proc "contextless" (fd: Fd, iov: []IO_Vec) -> (int, Errno) {
 	Available since Linux 1.0.
 	Available since Linux 1.0.
 	For ARM64 available since Linux 2.6.16.
 	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 {
 	when ODIN_ARCH == .arm64 {
 		ret := syscall(SYS_faccessat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
 		ret := syscall(SYS_faccessat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
-		return errno_unwrap(ret, bool)
+		return Errno(-ret)
 	} else {
 	} else {
 		ret := syscall(SYS_access, cast(rawptr) name, transmute(u32) mode)
 		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.
 	Checks the user permissions for a file at specified dirfd.
 	Available since Linux 2.6.16.
 	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)
 	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).
 	Checks the user permissions for a file at specified dirfd (with flags).
 	Available since Linux 5.8.
 	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)
 	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
 // TODO(flysand): process_madvise