Browse Source

fix `os.read_dir` closing the given file descriptor

Laytan Laats 1 năm trước cách đây
mục cha
commit
a4ac3cc6e8

+ 4 - 2
core/os/dir_unix.odin

@@ -5,10 +5,12 @@ import "core:strings"
 
 @(require_results)
 read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
-	dirp := _fdopendir(fd) or_return
+	dupfd := _dup(fd) or_return
+
+	dirp := _fdopendir(dupfd) or_return
 	defer _closedir(dirp)
 
-	dirpath := absolute_path_from_handle(fd) or_return
+	dirpath := absolute_path_from_handle(dupfd) or_return
 	defer delete(dirpath)
 
 	n := n

+ 11 - 1
core/os/os_darwin.odin

@@ -598,7 +598,8 @@ foreign libc {
 	@(link_name="fstat64")          _unix_fstat         :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
 	@(link_name="readlink")         _unix_readlink      :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
 	@(link_name="access")           _unix_access        :: proc(path: cstring, mask: c.int) -> c.int ---
-        @(link_name="fsync")            _unix_fsync         :: proc(handle: Handle) -> c.int ---
+    @(link_name="fsync")            _unix_fsync         :: proc(handle: Handle) -> c.int ---
+	@(link_name="dup")              _unix_dup           :: proc(handle: Handle) -> Handle ---
 
 	@(link_name="fdopendir$INODE64") _unix_fdopendir_amd64 :: proc(fd: Handle) -> Dir ---
 	@(link_name="readdir_r$INODE64") _unix_readdir_r_amd64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
@@ -1009,6 +1010,15 @@ _readlink :: proc(path: string) -> (string, Error) {
 	}
 }
 
+@(private, require_results)
+_dup :: proc(fd: Handle) -> (Handle, Error) {
+	dup := _unix_dup(fd)
+	if dup == -1 {
+		return INVALID_HANDLE, get_last_error()
+	}
+	return dup, nil
+}
+
 @(require_results)
 absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
 	buf: [DARWIN_MAXPATHLEN]byte

+ 10 - 0
core/os/os_freebsd.odin

@@ -389,6 +389,7 @@ foreign libc {
 	@(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="dup")              _unix_dup           :: proc(fd: Handle) -> Handle ---
 	
 	@(link_name="fdopendir")        _unix_fdopendir     :: proc(fd: Handle) -> Dir ---
 	@(link_name="closedir")         _unix_closedir      :: proc(dirp: Dir) -> c.int ---
@@ -739,6 +740,15 @@ _readlink :: proc(path: string) -> (string, Error) {
 	return "", Error{}
 }
 
+@(private, require_results)
+_dup :: proc(fd: Handle) -> (Handle, Error) {
+	dup := _unix_dup(fd)
+	if dup == -1 {
+		return INVALID_HANDLE, get_last_error()
+	}
+	return dup, nil
+}
+
 @(require_results)
 absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
 	// NOTE(Feoramund): The situation isn't ideal, but this was the best way I

+ 6 - 0
core/os/os_linux.odin

@@ -886,6 +886,12 @@ _readlink :: proc(path: string) -> (string, Error) {
 	}
 }
 
+@(private, require_results)
+_dup :: proc(fd: Handle) -> (Handle, Error) {
+	dup, err := linux.dup(linux.Fd(fd))
+	return Handle(dup), err
+}
+
 @(require_results)
 absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
 	buf : [256]byte

+ 10 - 0
core/os/os_netbsd.odin

@@ -441,6 +441,7 @@ foreign libc {
 	@(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="dup")              _unix_dup           :: proc(fd: Handle) -> Handle ---
 	
 	@(link_name="fdopendir")        _unix_fdopendir     :: proc(fd: Handle) -> Dir ---
 	@(link_name="closedir")         _unix_closedir      :: proc(dirp: Dir) -> c.int ---
@@ -801,6 +802,15 @@ _readlink :: proc(path: string) -> (string, Error) {
 	return "", Error{}
 }
 
+@(private, require_results)
+_dup :: proc(fd: Handle) -> (Handle, Error) {
+	dup := _unix_dup(fd)
+	if dup == -1 {
+		return INVALID_HANDLE, get_last_error()
+	}
+	return dup, nil
+}
+
 @(require_results)
 absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
 	buf: [MAX_PATH]byte

+ 10 - 0
core/os/os_openbsd.odin

@@ -364,6 +364,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="dup")      _unix_dup   :: proc(fd: Handle) -> Handle ---
 
 	@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
 	@(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
@@ -716,6 +717,15 @@ _readlink :: proc(path: string) -> (string, Error) {
 	}
 }
 
+@(private, require_results)
+_dup :: proc(fd: Handle) -> (Handle, Error) {
+	dup := _unix_dup(fd)
+	if dup == -1 {
+		return INVALID_HANDLE, get_last_error()
+	}
+	return dup, nil
+}
+
 // XXX OpenBSD
 @(require_results)
 absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {

+ 4 - 2
tests/core/os/os.odin

@@ -32,7 +32,9 @@ read_dir :: proc(t: ^testing.T) {
 
 	fd, err := os.open(#directory + "/dir")
 	testing.expect_value(t, err, nil)
-	defer os.close(fd)
+	defer {
+		testing.expect_value(t, os.close(fd), nil)
+	}
 
 	dir, err2 := os.read_dir(fd, -1)
 	testing.expect_value(t, err2, nil)
@@ -58,4 +60,4 @@ read_dir :: proc(t: ^testing.T) {
 		testing.expect_value(t, dir[2].name, "sub")
 		testing.expect(t, dir[2].is_dir, "is not a directory")
 	}
-}
+}