Browse Source

os2: improve absolute/full path handling for posix

Laytan Laats 1 year ago
parent
commit
b07d0b38b1

+ 5 - 1
core/os/os2/file.odin

@@ -115,7 +115,11 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
 
 @(require_results)
 new_file :: proc(handle: uintptr, name: string) -> ^File {
-	return _new_file(handle, name) or_else panic("Out of memory")
+	file, err := _new_file(handle, name)
+	if err != nil {
+		panic(error_string(err))
+	}
+	return file
 }
 
 @(require_results)

+ 16 - 14
core/os/os2/file_posix.odin

@@ -26,9 +26,18 @@ File_Impl :: struct {
 @(init)
 init_std_files :: proc() {
 	// NOTE: is this (paths) also the case on non darwin?
-	stdin  = new_file(posix.STDIN_FILENO,  "/dev/stdin") 
-	stdout = new_file(posix.STDOUT_FILENO, "/dev/stdout")
-	stderr = new_file(posix.STDERR_FILENO, "/dev/stdout")
+
+	stdin = __new_file(posix.STDIN_FILENO)
+	(^File_Impl)(stdin.impl).name  = "/dev/stdin"
+	(^File_Impl)(stdin.impl).cname = "/dev/stdin"
+
+	stdout = __new_file(posix.STDIN_FILENO)
+	(^File_Impl)(stdout.impl).name  = "/dev/stdout"
+	(^File_Impl)(stdout.impl).cname = "/dev/stdout"
+
+	stderr = __new_file(posix.STDIN_FILENO)
+	(^File_Impl)(stderr.impl).name  = "/dev/stderr"
+	(^File_Impl)(stderr.impl).cname = "/dev/stderr"
 }
 
 _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -75,19 +84,12 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
 		return
 	}
 
-	TEMP_ALLOCATOR_GUARD()
-	cname := temp_cstring(name)
-
-	crname := posix.realpath(cname, nil)
-	if crname == nil {
-		err = _get_platform_error()
-		return
-	}
-	rname := string(crname)
+	crname := _posix_absolute_path(posix.FD(handle), name, file_allocator()) or_return
+	rname  := string(crname)
 
-	f     = __new_file(posix.FD(handle))
+	f = __new_file(posix.FD(handle))
 	impl := (^File_Impl)(f.impl)
-	impl.name = rname
+	impl.name  = rname
 	impl.cname = crname
 
 	return f, nil

+ 18 - 0
core/os/os2/file_posix_darwin.odin

@@ -0,0 +1,18 @@
+//+private
+package os2
+
+import "base:runtime"
+
+import "core:sys/posix"
+
+_posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
+	F_GETPATH :: 50
+
+	buf: [posix.PATH_MAX]byte
+	if posix.fcntl(fd, posix.FCNTL_Cmd(F_GETPATH), &buf) != 0 {
+		err = _get_platform_error()
+		return
+	}
+
+	return clone_to_cstring(string(cstring(&buf[0])), allocator)
+}

+ 47 - 0
core/os/os2/file_posix_freebsd.odin

@@ -0,0 +1,47 @@
+//+private
+package os2
+
+import "base:runtime"
+
+import "core:c"
+import "core:sys/posix"
+
+_posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
+	// NOTE(Feoramund): The situation isn't ideal, but this was the best way I
+	// could find to implement this. There are a couple outstanding bug reports
+	// regarding the desire to retrieve an absolute path from a handle, but to
+	// my knowledge, there hasn't been any work done on it.
+	//
+	// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198570
+	//
+	// This may be unreliable, according to a comment from 2023.
+
+	KInfo_File :: struct {
+		structsize: c.int,
+		type:       c.int,
+		fd:         c.int,
+		ref_count:  c.int,
+		flags:      c.int,
+		pad0:       c.int,
+		offset:     i64,
+
+		// NOTE(Feoramund): This field represents a complicated union that I am
+		// avoiding implementing for now. I only need the path data below.
+		_union: [336]byte,
+
+		path: [posix.PATH_MAX]c.char,
+	}
+
+	F_KINFO :: 22
+
+	kinfo: KInfo_File
+	kinfo.structsize = size_of(KInfo_File)
+
+	res := posix.fcntl(fd, posix.FCNTL_Cmd(F_KINFO), &kinfo)
+	if res == -1 {
+		err = _get_platform_error()
+		return
+	}
+
+	return clone_to_cstring(string(cstring(&kinfo.path[0])), allocator)
+}

+ 18 - 0
core/os/os2/file_posix_netbsd.odin

@@ -0,0 +1,18 @@
+//+private
+package os2
+
+import "base:runtime"
+
+import "core:sys/posix"
+
+_posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
+	F_GETPATH :: 15
+
+	buf: [posix.PATH_MAX]byte
+	if posix.fcntl(fd, posix.FCNTL_Cmd(F_GETPATH), &buf) != 0 {
+		err = _get_platform_error()
+		return
+	}
+
+	return clone_to_cstring(string(cstring(&buf[0])), allocator)
+}

+ 21 - 0
core/os/os2/file_posix_other.odin

@@ -0,0 +1,21 @@
+//+private
+//+build openbsd
+package os2
+
+import "base:runtime"
+
+import "core:sys/posix"
+
+_posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
+	TEMP_ALLOCATOR_GUARD(ignore=is_temp(allocator))
+	cname := temp_cstring(name)
+
+	buf: [posix.PATH_MAX]byte
+	path = posix.realpath(cname, raw_data(buf[:]))
+	if path == nil {
+		err = _get_platform_error()
+		return
+	}
+
+	return clone_to_cstring(string(path), allocator)
+}

+ 9 - 8
core/os/os2/stat_posix.odin

@@ -73,21 +73,22 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err
 	TEMP_ALLOCATOR_GUARD(ignore=is_temp(allocator))
 	cname := temp_cstring(name) or_return
 
-	rcname := posix.realpath(cname)
-	if rcname == nil {
-		err = .Invalid_Path
+	fd := posix.open(cname, {})
+	if fd == -1 {
+		err = _get_platform_error()
 		return
 	}
-	defer posix.free(rcname)
+	defer posix.close(fd)
+
+	fullpath := _posix_absolute_path(fd, name, allocator) or_return
 
 	stat: posix.stat_t
-	if posix.stat(rcname, &stat) != .OK {
+	if posix.stat(fullpath, &stat) != .OK {
 		err = _get_platform_error()
 		return
 	}
 
-	fullpath := clone_string(string(rcname), allocator) or_return
-	return internal_stat(stat, fullpath), nil
+	return internal_stat(stat, string(fullpath)), nil
 }
 
 _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
@@ -98,7 +99,7 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er
 
 	TEMP_ALLOCATOR_GUARD(ignore=is_temp(allocator))
 
-	// NOTE: can't use realpath here because it tries to resolve symlinks.
+	// NOTE: can't use realpath or open (+ fcntl F_GETPATH) here because it tries to resolve symlinks.
 
 	// NOTE: This might not be correct when given "/symlink/foo.txt",
 	// you would want that to resolve "/symlink", but not resolve "foo.txt".

+ 1 - 1
core/os/os_freebsd.odin

@@ -388,7 +388,7 @@ foreign libc {
 	@(link_name="unlink")           _unix_unlink        :: proc(path: cstring) -> c.int ---
 	@(link_name="rmdir")            _unix_rmdir         :: proc(path: cstring) -> c.int ---
 	@(link_name="mkdir")            _unix_mkdir         :: proc(path: cstring, mode: mode_t) -> c.int ---
-	@(link_name="fcntl")            _unix_fcntl         :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int ---
+	@(link_name="fcntl")            _unix_fcntl         :: proc(fd: Handle, cmd: c.int, #c_vararg args: ..any) -> c.int ---
 	@(link_name="dup")              _unix_dup           :: proc(fd: Handle) -> Handle ---
 	
 	@(link_name="fdopendir")        _unix_fdopendir     :: proc(fd: Handle) -> Dir ---

+ 1 - 1
core/os/os_netbsd.odin

@@ -440,7 +440,7 @@ foreign libc {
 	@(link_name="unlink")           _unix_unlink        :: proc(path: cstring) -> c.int ---
 	@(link_name="rmdir")            _unix_rmdir         :: proc(path: cstring) -> c.int ---
 	@(link_name="mkdir")            _unix_mkdir         :: proc(path: cstring, mode: mode_t) -> c.int ---
-	@(link_name="fcntl")            _unix_fcntl         :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int ---
+	@(link_name="fcntl")            _unix_fcntl         :: proc(fd: Handle, cmd: c.int, #c_vararg args: ..any) -> c.int ---
 	@(link_name="dup")              _unix_dup           :: proc(fd: Handle) -> Handle ---
 	
 	@(link_name="fdopendir")        _unix_fdopendir     :: proc(fd: Handle) -> Dir ---

+ 1 - 1
core/sys/posix/fcntl.odin

@@ -23,7 +23,7 @@ foreign lib {
 
 	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html ]]
 	*/
-	fcntl :: proc(fd: FD, cmd: FCNTL_Cmd, arg: rawptr = nil) -> c.int ---
+	fcntl :: proc(fd: FD, cmd: FCNTL_Cmd, #c_vararg args: ..any) -> c.int ---
 
 	/*
 	Establish the connection between a file and a file descriptor.