zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
Mlib/std/dynamic_library.zig | 38+++++++++++++++-----------------------
Mlib/std/os/linux/IoUring/test.zig | 42++++++++++++++++++++++++------------------
Mlib/std/posix.zig | 60------------------------------------------------------------
Mlib/std/posix/test.zig | 20++++++++++++--------
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, .{})); } }