commit 213ef953462341b44c52adc0696ab3fbc60e82b4 (tree)
parent 2f372b3dc00c60a625e1cc3518fa1bda3429de59
Author: Andrew Kelley <andrew@ziglang.org>
Date: Wed, 7 Jan 2026 14:12:34 -0800
goodbye posix.open
see #6600
Diffstat:
4 files changed, 51 insertions(+), 109 deletions(-)
diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig
@@ -148,7 +148,7 @@ const ElfDynLibError = error{
ElfHashTableNotFound,
Canceled,
Streaming,
-} || posix.OpenError || posix.MMapError;
+} || Io.File.OpenError || posix.MMapError;
pub const ElfDynLib = struct {
strings: [*:0]u8,
@@ -177,27 +177,20 @@ pub const ElfDynLib = struct {
return parent;
}
- fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t {
+ fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?Io.File {
var paths = std.mem.tokenizeScalar(u8, search_path, delim);
while (paths.next()) |p| {
var dir = openPath(io, p) catch continue;
defer dir.close(io);
- const fd = posix.openat(dir.handle, file_name, .{
- .ACCMODE = .RDONLY,
- .CLOEXEC = true,
- }, 0) catch continue;
- return fd;
+ return dir.openFile(io, file_name, .{}) catch continue;
}
return null;
}
- fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?posix.fd_t {
+ fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?Io.File {
var dir = Io.Dir.cwd().openDir(io, dir_path, .{}) catch return null;
defer dir.close(io);
- return posix.openat(dir.handle, file_name, .{
- .ACCMODE = .RDONLY,
- .CLOEXEC = true,
- }, 0) catch null;
+ return dir.openFile(io, file_name, .{}) catch null;
}
// This implements enough to be able to load system libraries in general
@@ -205,10 +198,10 @@ pub const ElfDynLib = struct {
// - DT_RPATH of the calling binary is not used as a search path
// - DT_RUNPATH of the calling binary is not used as a search path
// - /etc/ld.so.cache is not read
- fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !posix.fd_t {
+ fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !Io.File {
// If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname
if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| {
- return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
+ return Io.Dir.cwd().openFile(io, path_or_name, .{});
}
// Only read LD_LIBRARY_PATH if the binary is not setuid/setgid
@@ -216,15 +209,15 @@ pub const ElfDynLib = struct {
std.os.linux.getegid() == std.os.linux.getgid())
{
if (LD_LIBRARY_PATH) |ld_library_path| {
- if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |fd| {
- return fd;
+ if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |file| {
+ return file;
}
}
}
// Lastly the directories /lib and /usr/lib are searched (in this exact order)
- if (resolveFromParent(io, "/lib", path_or_name)) |fd| return fd;
- if (resolveFromParent(io, "/usr/lib", path_or_name)) |fd| return fd;
+ if (resolveFromParent(io, "/lib", path_or_name)) |file| return file;
+ if (resolveFromParent(io, "/usr/lib", path_or_name)) |file| return file;
return error.FileNotFound;
}
@@ -232,10 +225,9 @@ pub const ElfDynLib = struct {
pub fn open(path: []const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib {
const io = std.Options.debug_io;
- const fd = try resolveFromName(io, path, LD_LIBRARY_PATH);
- defer posix.close(fd);
+ const file = try resolveFromName(io, path, LD_LIBRARY_PATH);
+ defer file.close(io);
- const file: Io.File = .{ .handle = fd };
const stat = try file.stat(io);
const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
@@ -248,7 +240,7 @@ pub const ElfDynLib = struct {
mem.alignForward(usize, size, page_size),
posix.PROT.READ,
.{ .TYPE = .PRIVATE },
- fd,
+ file.handle,
0,
);
defer posix.munmap(file_bytes);
@@ -318,7 +310,7 @@ pub const ElfDynLib = struct {
extended_memsz,
prot,
.{ .TYPE = .PRIVATE, .FIXED = true },
- fd,
+ file.handle,
ph.p_offset - extra_bytes,
);
} else {
diff --git a/lib/std/os/linux/IoUring/test.zig b/lib/std/os/linux/IoUring/test.zig
@@ -932,6 +932,8 @@ test "accept/connect/recv/cancel" {
}
test "register_files_update" {
+ const io = testing.io;
+
var ring = IoUring.init(1, 0) catch |err| switch (err) {
error.SystemOutdated => return error.SkipZigTest,
error.PermissionDenied => return error.SkipZigTest,
@@ -939,13 +941,13 @@ test "register_files_update" {
};
defer ring.deinit();
- const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
- defer posix.close(fd);
+ const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
+ defer file.close(io);
var registered_fds = [_]linux.fd_t{0} ** 2;
const fd_index = 0;
const fd_index2 = 1;
- registered_fds[fd_index] = fd;
+ registered_fds[fd_index] = file.handle;
registered_fds[fd_index2] = -1;
ring.register_files(registered_fds[0..]) catch |err| switch (err) {
@@ -957,10 +959,10 @@ test "register_files_update" {
// Test IORING_REGISTER_FILES_UPDATE
// Only available since Linux 5.5
- const fd2 = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
- defer posix.close(fd2);
+ const file2 = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
+ defer file2.close(io);
- registered_fds[fd_index] = fd2;
+ registered_fds[fd_index] = file2.handle;
registered_fds[fd_index2] = -1;
try ring.register_files_update(0, registered_fds[0..]);
@@ -1339,6 +1341,8 @@ test "linkat" {
}
test "provide_buffers: read" {
+ const io = testing.io;
+
var ring = IoUring.init(1, 0) catch |err| switch (err) {
error.SystemOutdated => return error.SkipZigTest,
error.PermissionDenied => return error.SkipZigTest,
@@ -1346,8 +1350,8 @@ test "provide_buffers: read" {
};
defer ring.deinit();
- const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
- defer posix.close(fd);
+ const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
+ defer file.close(io);
const group_id = 1337;
const buffer_id = 0;
@@ -1380,9 +1384,9 @@ test "provide_buffers: read" {
var i: usize = 0;
while (i < buffers.len) : (i += 1) {
- const sqe = try ring.read(0xdededede, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
+ const sqe = try ring.read(0xdededede, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
- try testing.expectEqual(@as(i32, fd), sqe.fd);
+ try testing.expectEqual(@as(i32, file.handle), sqe.fd);
try testing.expectEqual(@as(u64, 0), sqe.addr);
try testing.expectEqual(@as(u32, buffer_len), sqe.len);
try testing.expectEqual(@as(u16, group_id), sqe.buf_index);
@@ -1406,9 +1410,9 @@ test "provide_buffers: read" {
// This read should fail
{
- const sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
+ const sqe = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
- try testing.expectEqual(@as(i32, fd), sqe.fd);
+ try testing.expectEqual(@as(i32, file.handle), sqe.fd);
try testing.expectEqual(@as(u64, 0), sqe.addr);
try testing.expectEqual(@as(u32, buffer_len), sqe.len);
try testing.expectEqual(@as(u16, group_id), sqe.buf_index);
@@ -1445,9 +1449,9 @@ test "provide_buffers: read" {
// Final read which should work
{
- const sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
+ const sqe = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
- try testing.expectEqual(@as(i32, fd), sqe.fd);
+ try testing.expectEqual(@as(i32, file.handle), sqe.fd);
try testing.expectEqual(@as(u64, 0), sqe.addr);
try testing.expectEqual(@as(u32, buffer_len), sqe.len);
try testing.expectEqual(@as(u16, group_id), sqe.buf_index);
@@ -1469,6 +1473,8 @@ test "provide_buffers: read" {
}
test "remove_buffers" {
+ const io = testing.io;
+
var ring = IoUring.init(1, 0) catch |err| switch (err) {
error.SystemOutdated => return error.SkipZigTest,
error.PermissionDenied => return error.SkipZigTest,
@@ -1476,8 +1482,8 @@ test "remove_buffers" {
};
defer ring.deinit();
- const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
- defer posix.close(fd);
+ const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
+ defer file.close(io);
const group_id = 1337;
const buffer_id = 0;
@@ -1522,7 +1528,7 @@ test "remove_buffers" {
// This read should work
{
- _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
+ _ = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(@as(u32, 1), try ring.submit());
const cqe = try ring.copy_cqe();
@@ -1542,7 +1548,7 @@ test "remove_buffers" {
// Final read should _not_ work
{
- _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
+ _ = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(@as(u32, 1), try ring.submit());
const cqe = try ring.copy_cqe();
diff --git a/lib/std/posix.zig b/lib/std/posix.zig
@@ -449,66 +449,6 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
pub const OpenError = std.Io.File.OpenError || error{WouldBlock};
/// Open and possibly create a file. Keeps trying if it gets interrupted.
-/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `file_path` should be encoded as valid UTF-8.
-/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
-/// See also `openZ`.
-pub fn open(file_path: []const u8, flags: O, perm: mode_t) OpenError!fd_t {
- if (native_os == .windows) {
- @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
- } else if (native_os == .wasi and !builtin.link_libc) {
- return openat(AT.FDCWD, file_path, flags, perm);
- }
- const file_path_c = try toPosixPath(file_path);
- return openZ(&file_path_c, flags, perm);
-}
-
-/// Open and possibly create a file. Keeps trying if it gets interrupted.
-/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `file_path` should be encoded as valid UTF-8.
-/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
-/// See also `open`.
-pub fn openZ(file_path: [*:0]const u8, flags: O, perm: mode_t) OpenError!fd_t {
- if (native_os == .windows) {
- @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
- } else if (native_os == .wasi and !builtin.link_libc) {
- return open(mem.sliceTo(file_path, 0), flags, perm);
- }
-
- const open_sym = if (lfs64_abi) system.open64 else system.open;
- while (true) {
- const rc = open_sym(file_path, flags, perm);
- switch (errno(rc)) {
- .SUCCESS => return @intCast(rc),
- .INTR => continue,
-
- .FAULT => unreachable,
- .INVAL => return error.BadPathName,
- .ACCES => return error.AccessDenied,
- .FBIG => return error.FileTooBig,
- .OVERFLOW => return error.FileTooBig,
- .ISDIR => return error.IsDir,
- .LOOP => return error.SymLinkLoop,
- .MFILE => return error.ProcessFdQuotaExceeded,
- .NAMETOOLONG => return error.NameTooLong,
- .NFILE => return error.SystemFdQuotaExceeded,
- .NODEV => return error.NoDevice,
- .NOENT => return error.FileNotFound,
- // Can happen on Linux when opening procfs files.
- .SRCH => return error.FileNotFound,
- .NOMEM => return error.SystemResources,
- .NOSPC => return error.NoSpaceLeft,
- .NOTDIR => return error.NotDir,
- .PERM => return error.PermissionDenied,
- .EXIST => return error.PathAlreadyExists,
- .BUSY => return error.DeviceBusy,
- .ILSEQ => return error.BadPathName,
- else => |err| return unexpectedErrno(err),
- }
- }
-}
-
-/// Open and possibly create a file. Keeps trying if it gets interrupted.
/// `file_path` is relative to the open directory handle `dir_fd`.
/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
/// On WASI, `file_path` should be encoded as valid UTF-8.
diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig
@@ -463,8 +463,12 @@ test "rename smoke test" {
// Create some file using `open`.
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
defer gpa.free(file_path);
- const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
- posix.close(fd);
+ const file = try Io.Dir.cwd().createFile(io, file_path, .{
+ .read = true,
+ .exclusive = true,
+ .permissions = .fromMode(mode),
+ });
+ file.close(io);
// Rename the file
const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
@@ -476,15 +480,15 @@ test "rename smoke test" {
// Try opening renamed file
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
defer gpa.free(file_path);
- const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode);
- posix.close(fd);
+ const file = try Io.Dir.cwd().openFile(io, file_path, .{ .mode = .read_write });
+ file.close(io);
}
{
// Try opening original file - should fail with error.FileNotFound
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
defer gpa.free(file_path);
- try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode));
+ try expectError(error.FileNotFound, Io.Dir.cwd().openFile(io, file_path, .{ .mode = .read_write }));
}
{
@@ -503,15 +507,15 @@ test "rename smoke test" {
// Try opening renamed directory
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" });
defer gpa.free(file_path);
- const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);
- posix.close(fd);
+ const dir = try Io.Dir.cwd().openDir(io, file_path, .{});
+ dir.close(io);
}
{
// Try opening original directory - should fail with error.FileNotFound
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
defer gpa.free(file_path);
- try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode));
+ try expectError(error.FileNotFound, Io.Dir.cwd().openDir(io, file_path, .{}));
}
}