diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-18 20:55:47 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-23 22:15:10 -0800 |
| commit | 8632a28ca95a5a491bde57fdc45d9a0ce3cf597e (patch) | |
| tree | aa5d5f1db57d1037d5a18a92b69c594425bcb204 /lib/std | |
| parent | 9bbb8e0d8e8ce413ad2a9125b1eb9ddbf32b6c87 (diff) | |
| download | zig-8632a28ca95a5a491bde57fdc45d9a0ce3cf597e.tar.gz zig-8632a28ca95a5a491bde57fdc45d9a0ce3cf597e.zip | |
std: add support for realpath on file handle
and rename OperationNotSupported to OperationUnsupported
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/Io.zig | 4 | ||||
| -rw-r--r-- | lib/std/Io/Dir.zig | 115 | ||||
| -rw-r--r-- | lib/std/Io/File.zig | 49 | ||||
| -rw-r--r-- | lib/std/Io/Threaded.zig | 66 | ||||
| -rw-r--r-- | lib/std/fs/test.zig | 110 | ||||
| -rw-r--r-- | lib/std/os/windows.zig | 2 | ||||
| -rw-r--r-- | lib/std/posix.zig | 24 | ||||
| -rw-r--r-- | lib/std/posix/test.zig | 2 |
8 files changed, 220 insertions, 152 deletions
diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 78049633a0..0959ce06e7 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -670,7 +670,8 @@ pub const VTable = struct { dirOpenFile: *const fn (?*anyopaque, Dir, []const u8, File.OpenFlags) File.OpenError!File, dirClose: *const fn (?*anyopaque, []const Dir) void, dirRead: *const fn (?*anyopaque, *Dir.Reader, []Dir.Entry) Dir.Reader.Error!usize, - dirRealPath: *const fn (?*anyopaque, Dir, path_name: []const u8, out_buffer: []u8) Dir.RealPathError!usize, + dirRealPath: *const fn (?*anyopaque, Dir, out_buffer: []u8) Dir.RealPathError!usize, + dirRealPathFile: *const fn (?*anyopaque, Dir, path_name: []const u8, out_buffer: []u8) Dir.RealPathFileError!usize, dirDeleteFile: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteFileError!void, dirDeleteDir: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteDirError!void, dirRename: *const fn (?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) Dir.RenameError!void, @@ -709,6 +710,7 @@ pub const VTable = struct { fileTryLock: *const fn (?*anyopaque, File, File.Lock) File.LockError!bool, fileUnlock: *const fn (?*anyopaque, File) void, fileDowngradeLock: *const fn (?*anyopaque, File) File.DowngradeLockError!void, + fileRealPath: *const fn (?*anyopaque, File, out_buffer: []u8) File.RealPathError!usize, processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File, processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize, diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index 72b4a00cdd..ae817676a7 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -766,95 +766,80 @@ pub fn statFile(dir: Dir, io: Io, sub_path: []const u8, options: StatFileOptions return io.vtable.dirStatFile(io.userdata, dir, sub_path, options); } -pub const RealPathError = error{ - FileNotFound, - AccessDenied, - PermissionDenied, - NotSupported, - NotDir, - SymLinkLoop, - InputOutput, - FileTooBig, - IsDir, - ProcessFdQuotaExceeded, - SystemFdQuotaExceeded, - NoDevice, - SystemResources, - NoSpaceLeft, - FileSystem, - DeviceBusy, - SharingViolation, - PipeBusy, - /// On Windows, `\\server` or `\\server\share` was not found. - NetworkNotFound, - PathAlreadyExists, - /// On Windows, antivirus software is enabled by default. It can be - /// disabled, but Windows Update sometimes ignores the user's preference - /// and re-enables it. When enabled, antivirus software on Windows - /// intercepts file system operations and makes them significantly slower - /// in addition to possibly failing with this error code. - AntivirusInterference, - /// On Windows, the volume does not contain a recognized file system. File - /// system drivers might not be loaded, or the volume may be corrupt. - UnrecognizedVolume, -} || PathNameError || Io.Cancelable || Io.UnexpectedError; +pub const RealPathError = File.RealPathError; + +/// Obtains the canonicalized absolute path name of `sub_path` relative to this +/// `Dir`. If `sub_path` is absolute, ignores this `Dir` handle and obtains the +/// canonicalized absolute pathname of `sub_path` argument. +/// +/// This function has limited platform support, and using it can lead to +/// unnecessary failures and race conditions. It is generally advisable to +/// avoid this function entirely. +pub fn realPath(dir: Dir, io: Io, out_buffer: []u8) RealPathError!usize { + return io.vtable.dirRealPath(io.userdata, dir, out_buffer); +} -/// This function returns the canonicalized absolute path name of `sub_path` -/// relative to this `Dir`. If `sub_path` is absolute, ignores this `Dir` -/// handle and returns the canonicalized absolute pathname of `sub_path` -/// argument. +pub const RealPathFileError = RealPathError || PathNameError; + +/// Obtains the canonicalized absolute path name of `sub_path` relative to this +/// `Dir`. If `sub_path` is absolute, ignores this `Dir` handle and obtains the +/// canonicalized absolute pathname of `sub_path` argument. /// -/// This function is not universally supported by all platforms, and using this -/// function can lead to unnecessary failures and race conditions. +/// This function has limited platform support, and using it can lead to +/// unnecessary failures and race conditions. It is generally advisable to +/// avoid this function entirely. /// /// See also: -/// * `realPathAlloc`. -/// * `realPathAbsolute`. -pub fn realPath(dir: Dir, io: Io, sub_path: []const u8, out_buffer: []u8) RealPathError!usize { - return io.vtable.dirRealPath(io.userdata, dir, sub_path, out_buffer); +/// * `realPathFileAlloc`. +/// * `realPathFileAbsolute`. +pub fn realPathFile(dir: Dir, io: Io, sub_path: []const u8, out_buffer: []u8) RealPathFileError!usize { + return io.vtable.dirRealPathFile(io.userdata, dir, sub_path, out_buffer); } -pub const RealPathAllocError = RealPathError || Allocator.Error; +pub const RealPathFileAllocError = RealPathFileError || Allocator.Error; -/// Same as `realPath` except allocates result. +/// Same as `realPathFile` except allocates result. /// -/// This function is not universally supported by all platforms, and using this -/// function can lead to unnecessary failures and race conditions. +/// This function has limited platform support, and using it can lead to +/// unnecessary failures and race conditions. It is generally advisable to +/// avoid this function entirely. /// /// See also: -/// * `realPath`. -/// * `realPathAbsolute`. -pub fn realPathAlloc(dir: Dir, io: Io, sub_path: []const u8, allocator: Allocator) RealPathAllocError![:0]u8 { +/// * `realPathFile`. +/// * `realPathFileAbsolute`. +pub fn realPathFileAlloc(dir: Dir, io: Io, sub_path: []const u8, allocator: Allocator) RealPathFileAllocError![:0]u8 { var buffer: [max_path_bytes]u8 = undefined; - const n = try realPath(dir, io, sub_path, &buffer); + const n = try realPathFile(dir, io, sub_path, &buffer); return allocator.dupeZ(u8, buffer[0..n]); } -/// Same as `realPath` except `absolute_path` is asserted to be an absolute +/// Same as `realPathFile` except `absolute_path` is asserted to be an absolute /// path. /// -/// This function is not universally supported by all platforms, and using this -/// function can lead to unnecessary failures and race conditions. +/// This function has limited platform support, and using it can lead to +/// unnecessary failures and race conditions. It is generally advisable to +/// avoid this function entirely. /// /// See also: -/// * `realPath`. -/// * `realPathAlloc`. -pub fn realPathAbsolute(io: Io, absolute_path: []const u8, out_buffer: []u8) RealPathError!usize { +/// * `realPathFile`. +/// * `realPathFileAlloc`. +pub fn realPathFileAbsolute(io: Io, absolute_path: []const u8, out_buffer: []u8) RealPathFileError!usize { assert(path.isAbsolute(absolute_path)); - return io.vtable.dirRealPath(io.userdata, .cwd(), absolute_path, out_buffer); + return io.vtable.dirRealPathFile(io.userdata, .cwd(), absolute_path, out_buffer); } -/// Same as `realPathAbsolute` except allocates result. +/// Same as `realPathFileAbsolute` except allocates result. /// -/// This function is not universally supported by all platforms, and using this -/// function can lead to unnecessary failures and race conditions. +/// This function has limited platform support, and using it can lead to +/// unnecessary failures and race conditions. It is generally advisable to +/// avoid this function entirely. /// /// See also: -/// * `realPathAbsolute`. -/// * `realPath`. -pub fn realPathAbsoluteAlloc(io: Io, absolute_path: []const u8, allocator: Allocator) RealPathAllocError![:0]u8 { +/// * `realPathFileAbsolute`. +/// * `realPathFile`. +pub fn realPathFileAbsoluteAlloc(io: Io, absolute_path: []const u8, allocator: Allocator) RealPathFileAllocError![:0]u8 { var buffer: [max_path_bytes]u8 = undefined; - const n = try realPathAbsolute(io, absolute_path, &buffer); + const n = try realPathFileAbsolute(io, absolute_path, &buffer); return allocator.dupeZ(u8, buffer[0..n]); } @@ -1765,7 +1750,7 @@ pub const SetFilePermissionsError = PathNameError || SetPermissionsError || erro SystemFdQuotaExceeded, /// `SetFilePermissionsOptions.follow_symlinks` was set to false, which is /// not allowed by the file system or operating system. - OperationNotSupported, + OperationUnsupported, }; pub const SetFilePermissionsOptions = struct { diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index e5bf5849e9..e06f2ba40d 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -627,6 +627,55 @@ pub fn downgradeLock(file: File, io: Io) LockError!void { return io.vtable.fileDowngradeLock(io.userdata, file); } +pub const RealPathError = error{ + /// This operating system, file system, or `Io` implementation does not + /// support realpath operations. + OperationUnsupported, + /// The full file system path could not fit into the provided buffer, or + /// due to its length could not be obtained via realpath functions no + /// matter the buffer size provided. + NameTooLong, + FileNotFound, + AccessDenied, + PermissionDenied, + NotDir, + SymLinkLoop, + InputOutput, + FileTooBig, + IsDir, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NoDevice, + SystemResources, + NoSpaceLeft, + FileSystem, + DeviceBusy, + SharingViolation, + PipeBusy, + /// On Windows, `\\server` or `\\server\share` was not found. + NetworkNotFound, + PathAlreadyExists, + /// On Windows, antivirus software is enabled by default. It can be + /// disabled, but Windows Update sometimes ignores the user's preference + /// and re-enables it. When enabled, antivirus software on Windows + /// intercepts file system operations and makes them significantly slower + /// in addition to possibly failing with this error code. + AntivirusInterference, + /// On Windows, the volume does not contain a recognized file system. File + /// system drivers might not be loaded, or the volume may be corrupt. + UnrecognizedVolume, +} || Io.Cancelable || Io.UnexpectedError; + +/// Obtains the canonicalized absolute path name corresponding to an open file +/// handle. +/// +/// This function has limited platform support, and using it can lead to +/// unnecessary failures and race conditions. It is generally advisable to +/// avoid this function entirely. +pub fn realPath(file: File, io: Io, out_buffer: []u8) RealPathError!usize { + return io.vtable.fileRealPath(io.userdata, file, out_buffer); +} + test { _ = Reader; _ = Writer; diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index a6a3c01a73..a018795c21 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -716,6 +716,7 @@ pub fn io(t: *Threaded) Io { .dirClose = dirClose, .dirRead = dirRead, .dirRealPath = dirRealPath, + .dirRealPathFile = dirRealPathFile, .dirDeleteFile = dirDeleteFile, .dirDeleteDir = dirDeleteDir, .dirRename = dirRename, @@ -752,6 +753,7 @@ pub fn io(t: *Threaded) Io { .fileTryLock = fileTryLock, .fileUnlock = fileUnlock, .fileDowngradeLock = fileDowngradeLock, + .fileRealPath = fileRealPath, .processExecutableOpen = processExecutableOpen, .processExecutablePath = processExecutablePath, @@ -848,6 +850,7 @@ pub fn ioBasic(t: *Threaded) Io { .dirClose = dirClose, .dirRead = dirRead, .dirRealPath = dirRealPath, + .dirRealPathFile = dirRealPathFile, .dirDeleteFile = dirDeleteFile, .dirDeleteDir = dirDeleteDir, .dirRename = dirRename, @@ -884,6 +887,7 @@ pub fn ioBasic(t: *Threaded) Io { .fileTryLock = fileTryLock, .fileUnlock = fileUnlock, .fileDowngradeLock = fileDowngradeLock, + .fileRealPath = fileRealPath, .processExecutableOpen = processExecutableOpen, .processExecutablePath = processExecutablePath, @@ -3848,22 +3852,21 @@ fn dirReadUnimplemented(userdata: ?*anyopaque, dir_reader: *Dir.Reader, buffer: return error.Unimplemented; } -const dirRealPath = switch (native_os) { - .windows => dirRealPathWindows, - else => dirRealPathPosix, +const dirRealPathFile = switch (native_os) { + .windows => dirRealPathFileWindows, + else => dirRealPathFilePosix, }; -fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathError!usize { +fn dirRealPathFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathFileError!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); - const w = windows; const current_thread = Thread.getCurrent(t); try current_thread.checkCancel(); - var path_name_w = try w.sliceToPrefixedFileW(dir.handle, sub_path); + var path_name_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path); const h_file = blk: { - const res = w.OpenFile(path_name_w.span(), .{ + const res = windows.OpenFile(path_name_w.span(), .{ .dir = dir.handle, .access_mask = .{ .GENERIC = .{ .READ = true }, @@ -3877,9 +3880,13 @@ fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out }; break :blk res; }; - defer w.CloseHandle(h_file); + defer windows.CloseHandle(h_file); + return realPathWindows(current_thread, h_file, out_buffer); +} - const wide_slice = w.GetFinalPathNameByHandle(h_file, .{}, out_buffer); +fn realPathWindows(current_thread: *Thread, h_file: windows.HANDLE, out_buffer: []u8) Dir.RealPathFileError { + _ = current_thread; // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks + const wide_slice = windows.GetFinalPathNameByHandle(h_file, .{}, out_buffer); const len = std.unicode.calcWtf8Len(wide_slice); if (len > out_buffer.len) @@ -3888,9 +3895,8 @@ fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out return std.unicode.wtf16LeToWtf8(out_buffer, wide_slice); } -fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathError!usize { - if (native_os == .wasi) @compileError("unsupported operating system"); - const max_path_bytes = std.fs.max_path_bytes; +fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathFileError!usize { + if (native_os == .wasi) return error.OperationUnsupported; const t: *Threaded = @ptrCast(@alignCast(userdata)); const current_thread = Thread.getCurrent(t); @@ -3949,7 +3955,35 @@ fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_b } }; errdefer posix.close(fd); + return realPathPosix(current_thread, fd, out_buffer); +} + +const dirRealPath = switch (native_os) { + .windows => dirRealPathWindows, + else => dirRealPathPosix, +}; + +fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize { + if (native_os == .wasi) return error.OperationUnsupported; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + return realPathPosix(current_thread, dir.handle, out_buffer); +} + +fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + return realPathWindows(current_thread, dir.handle, out_buffer); +} + +fn fileRealPath(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize { + if (native_os == .wasi) return error.OperationUnsupported; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + return realPathPosix(current_thread, file.handle, out_buffer); +} +fn realPathPosix(current_thread: *Thread, fd: posix.fd_t, out_buffer: []u8) File.RealPathError!usize { switch (native_os) { .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => { // On macOS, we can use F.GETPATH fcntl command to query the OS for @@ -4049,7 +4083,7 @@ fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_b return len; }, .netbsd, .dragonfly => { - @memset(out_buffer[0..max_path_bytes], 0); + @memset(out_buffer[0..Dir.max_path_bytes], 0); try current_thread.beginSyscall(); while (true) { switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) { @@ -4077,7 +4111,7 @@ fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_b } return std.mem.indexOfScalar(u8, &out_buffer, 0) orelse out_buffer.len; }, - else => @compileError("unsupported OS"), + else => return error.OperationUnsupported, } comptime unreachable; } @@ -5108,7 +5142,7 @@ fn posixFchmodat( .NOENT => return error.FileNotFound, .NOTDIR => return error.FileNotFound, .NOMEM => return error.SystemResources, - .OPNOTSUPP => return error.OperationNotSupported, + .OPNOTSUPP => return error.OperationUnsupported, .PERM => return error.PermissionDenied, .ROFS => return error.ReadOnlyFileSystem, else => |err| return posix.unexpectedErrno(err), @@ -5144,7 +5178,7 @@ fn posixFchmodat( .NOENT => return error.FileNotFound, .NOMEM => return error.SystemResources, .NOTDIR => return error.FileNotFound, - .OPNOTSUPP => return error.OperationNotSupported, + .OPNOTSUPP => return error.OperationUnsupported, .PERM => return error.PermissionDenied, .ROFS => return error.ReadOnlyFileSystem, .NOSYS => { diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 6f3b48b408..92d3630ac7 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -4,6 +4,7 @@ const native_os = builtin.os.tag; const std = @import("../std.zig"); const Io = std.Io; const mem = std.mem; +const Allocator = std.mem.Allocator; const wasi = std.os.wasi; const windows = std.os.windows; const ArenaAllocator = std.heap.ArenaAllocator; @@ -27,38 +28,57 @@ const PathType = enum { fn isSupported(self: PathType, target_os: std.Target.Os) bool { return switch (self) { .relative => true, - .absolute => target_os.tag == .windows, // TODO: implement getPathForHandle for other targets + .absolute => switch (target_os.tag) { + .windows, + .driverkit, + .ios, + .maccatalyst, + .macos, + .tvos, + .visionos, + .watchos, + .linux, + .illumos, + .freebsd, + .serenity, + => true, + + .dragonfly => target_os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt, + .netbsd => target_os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt, + else => false, + }, .unc => target_os.tag == .windows, }; } const TransformError = Dir.RealPathError || error{OutOfMemory}; - const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8; + const TransformFn = fn (Allocator, Io, Dir, relative_path: [:0]const u8) TransformError![:0]const u8; fn getTransformFn(comptime path_type: PathType) TransformFn { switch (path_type) { .relative => return struct { - fn transform(allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 { + fn transform(allocator: Allocator, io: Io, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 { _ = allocator; + _ = io; _ = dir; return relative_path; } }.transform, .absolute => return struct { - fn transform(allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 { + fn transform(allocator: Allocator, io: Io, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 { // The final path may not actually exist which would cause realpath to fail. // So instead, we get the path of the dir and join it with the relative path. var fd_path_buf: [Dir.max_path_bytes]u8 = undefined; - const dir_path = try getPathForHandle(dir.handle, &fd_path_buf); + const dir_path = fd_path_buf[0..try dir.realPath(io, &fd_path_buf)]; return Dir.path.joinZ(allocator, &.{ dir_path, relative_path }); } }.transform, .unc => return struct { - fn transform(allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 { + fn transform(allocator: Allocator, io: Io, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 { // Any drive absolute path (C:\foo) can be converted into a UNC path by // using '127.0.0.1' as the server name and '<drive letter>$' as the share name. var fd_path_buf: [Dir.max_path_bytes]u8 = undefined; - const dir_path = try getPathForHandle(dir.handle, &fd_path_buf); + const dir_path = fd_path_buf[0..try dir.realPath(io, &fd_path_buf)]; const windows_path_type = windows.getWin32PathType(u8, dir_path); switch (windows_path_type) { .unc_absolute => return Dir.path.joinZ(allocator, &.{ dir_path, relative_path }), @@ -77,19 +97,6 @@ const PathType = enum { } }; -fn getPathForHandle(handle: File.Handle, out_buffer: *[Dir.max_path_bytes]u8) ![]u8 { - switch (native_os) { - .windows => { - var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined; - const wide_slice = try windows.GetFinalPathNameByHandle(handle, .{}, wide_buf[0..]); - - const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice); - return out_buffer[0..end_index]; - }, - else => @compileError("TODO or unsupported"), - } -} - const TestContext = struct { io: Io, path_type: PathType, @@ -99,7 +106,7 @@ const TestContext = struct { dir: Dir, transform_fn: *const PathType.TransformFn, - pub fn init(path_type: PathType, path_sep: u8, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext { + pub fn init(path_type: PathType, path_sep: u8, allocator: Allocator, transform_fn: *const PathType.TransformFn) TestContext { const tmp = tmpDir(.{ .iterate = true }); return .{ .io = testing.io, @@ -123,7 +130,7 @@ const TestContext = struct { /// `TestContext.deinit`. pub fn transformPath(self: *TestContext, relative_path: [:0]const u8) ![:0]const u8 { const allocator = self.arena.allocator(); - const transformed_path = try self.transform_fn(allocator, self.dir, relative_path); + const transformed_path = try self.transform_fn(allocator, self.io, self.dir, relative_path); if (native_os == .windows) { const transformed_sep_path = try allocator.dupeZ(u8, transformed_path); std.mem.replaceScalar(u8, transformed_sep_path, switch (self.path_sep) { @@ -268,7 +275,7 @@ fn testReadLink(io: Io, dir: Dir, target_path: []const u8, symlink_path: []const try expectEqualStrings(target_path, actual); } -fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void { +fn testReadLinkW(allocator: Allocator, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void { const target_path_w = try std.unicode.wtf8ToWtf16LeAlloc(allocator, target_path); defer allocator.free(target_path_w); // Calling the W functions directly requires the path to be NT-prefixed @@ -281,7 +288,7 @@ fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, sy fn testReadLinkAbsolute(io: Io, target_path: []const u8, symlink_path: []const u8) !void { var buffer: [Dir.max_path_bytes]u8 = undefined; - const given = try Dir.readLinkAbsolute(io, symlink_path, buffer[0..]); + const given = buffer[0..try Dir.readLinkAbsolute(io, symlink_path, &buffer)]; try expectEqualStrings(target_path, given); } @@ -339,7 +346,7 @@ test "accessAbsolute" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - const base_path = try tmp.dir.realPathAlloc(io, ".", gpa); + const base_path = try tmp.dir.realPathFileAlloc(io, ".", gpa); defer gpa.free(base_path); try Dir.accessAbsolute(io, base_path, .{}); @@ -358,7 +365,7 @@ test "openDirAbsolute" { const tmp_ino = (try tmp.dir.stat(io)).inode; try tmp.dir.makeDir(io, "subdir", .default_dir); - const sub_path = try tmp.dir.realPathAlloc(io, "subdir", gpa); + const sub_path = try tmp.dir.realPathFileAlloc(io, "subdir", gpa); defer gpa.free(sub_path); // Can open sub_path @@ -434,10 +441,10 @@ test "openDir non-cwd parent '..'" { var dir = try subdir.openDir(io, "..", .{}); defer dir.close(io); - const expected_path = try tmp.dir.realPathAlloc(io, ".", gpa); + const expected_path = try tmp.dir.realPathFileAlloc(io, ".", gpa); defer gpa.free(expected_path); - const actual_path = try dir.realPathAlloc(io, ".", gpa); + const actual_path = try dir.realPathFileAlloc(io, ".", gpa); defer gpa.free(actual_path); try expectEqualStrings(expected_path, actual_path); @@ -461,7 +468,7 @@ test "readLinkAbsolute" { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const base_path = try tmp.dir.realPathAlloc(io, ".", arena); + const base_path = try tmp.dir.realPathFileAlloc(io, ".", arena); { const target_path = try Dir.path.join(arena, &.{ base_path, "file.txt" }); @@ -647,11 +654,6 @@ test "Dir.Iterator but dir is deleted during iteration" { // Now, when we try to iterate, the next call should return null immediately. const entry = try iterator.next(io); try testing.expect(entry == null); - - // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux` - if (native_os == .linux) { - try expectError(error.DirNotFound, iterator.nextLinux()); - } } fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool { @@ -671,47 +673,41 @@ test "Dir.realPath smoke test" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { const io = ctx.io; - const allocator = ctx.arena.allocator(); + const arena = ctx.arena.allocator(); const test_file_path = try ctx.transformPath("test_file"); const test_dir_path = try ctx.transformPath("test_dir"); var buf: [Dir.max_path_bytes]u8 = undefined; // FileNotFound if the path doesn't exist - try expectError(error.FileNotFound, ctx.dir.realPathAlloc(io, test_file_path, allocator)); - try expectError(error.FileNotFound, ctx.dir.realPath(io, test_file_path, &buf)); - try expectError(error.FileNotFound, ctx.dir.realPathAlloc(io, test_dir_path, allocator)); - try expectError(error.FileNotFound, ctx.dir.realPath(io, test_dir_path, &buf)); + try expectError(error.FileNotFound, ctx.dir.realPathFileAlloc(io, test_file_path, arena)); + try expectError(error.FileNotFound, ctx.dir.realPathFile(io, test_file_path, &buf)); + try expectError(error.FileNotFound, ctx.dir.realPathFileAlloc(io, test_dir_path, arena)); + try expectError(error.FileNotFound, ctx.dir.realPathFile(io, test_dir_path, &buf)); // Now create the file and dir try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" }); try ctx.dir.makeDir(io, test_dir_path, .default_dir); const base_path = try ctx.transformPath("."); - const base_realpath = try ctx.dir.realPathAlloc(io, base_path, allocator); - const expected_file_path = try Dir.path.join( - allocator, - &.{ base_realpath, "test_file" }, - ); - const expected_dir_path = try Dir.path.join( - allocator, - &.{ base_realpath, "test_dir" }, - ); + const base_realpath = try ctx.dir.realPathFileAlloc(io, base_path, arena); + const expected_file_path = try Dir.path.join(arena, &.{ base_realpath, "test_file" }); + const expected_dir_path = try Dir.path.join(arena, &.{ base_realpath, "test_dir" }); // First, test non-alloc version { - const file_path = try ctx.dir.realPath(io, test_file_path, &buf); + const file_path = buf[0..try ctx.dir.realPathFile(io, test_file_path, &buf)]; try expectEqualStrings(expected_file_path, file_path); - const dir_path = try ctx.dir.realPath(io, test_dir_path, &buf); + const dir_path = buf[0..try ctx.dir.realPathFile(io, test_dir_path, &buf)]; try expectEqualStrings(expected_dir_path, dir_path); } // Next, test alloc version { - const file_path = try ctx.dir.realPathAlloc(io, test_file_path, allocator); + const file_path = try ctx.dir.realPathFileAlloc(io, test_file_path, arena); try expectEqualStrings(expected_file_path, file_path); - const dir_path = try ctx.dir.realPathAlloc(io, test_dir_path, allocator); + const dir_path = try ctx.dir.realPathFileAlloc(io, test_dir_path, arena); try expectEqualStrings(expected_dir_path, dir_path); } } @@ -1114,7 +1110,7 @@ test "renameAbsolute" { defer arena.deinit(); const allocator = arena.allocator(); - const base_path = try tmp_dir.dir.realPathAlloc(io, ".", allocator); + const base_path = try tmp_dir.dir.realPathFileAlloc(io, ".", allocator); try expectError(error.FileNotFound, Dir.renameAbsolute( try Dir.path.join(allocator, &.{ base_path, "missing_file_name" }), @@ -2022,7 +2018,7 @@ test "'.' and '..' in absolute functions" { defer arena.deinit(); const allocator = arena.allocator(); - const base_path = try tmp.dir.realPathAlloc(io, ".", allocator); + const base_path = try tmp.dir.realPathFileAlloc(io, ".", allocator); const subdir_path = try Dir.path.join(allocator, &.{ base_path, "./subdir" }); try Dir.makeDirAbsolute(io, subdir_path, .default_dir); @@ -2140,8 +2136,8 @@ test "invalid UTF-8/WTF-8 paths" { try expectError(expected_err, ctx.dir.statFile(io, invalid_path, .{})); if (native_os != .wasi) { - try expectError(expected_err, ctx.dir.realPath(io, invalid_path, &[_]u8{})); - try expectError(expected_err, ctx.dir.realPathAlloc(io, invalid_path, testing.allocator)); + try expectError(expected_err, ctx.dir.realPathFile(io, invalid_path, &[_]u8{})); + try expectError(expected_err, ctx.dir.realPathFileAlloc(io, invalid_path, testing.allocator)); } try expectError(expected_err, Dir.rename(ctx.dir, invalid_path, ctx.dir, invalid_path, io)); @@ -2402,7 +2398,7 @@ test "fchmodat smoke test" { var test_link = true; tmp.dir.setFilePermissions(io, "symlink", .fromMode(0o600), .{ .follow_symlinks = false }) catch |err| switch (err) { - error.OperationNotSupported => test_link = false, + error.OperationUnsupported => test_link = false, else => |e| return e, }; if (test_link) try expectMode(io, tmp.dir, "symlink", .fromMode(0o600)); diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 02357d6d2a..f2ff2a1c1b 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -3620,6 +3620,8 @@ pub const GetFinalPathNameByHandleFormat = struct { /// NT or DOS volume name (e.g., `\Device\HarddiskVolume0\foo.txt` versus `C:\foo.txt`). /// If DOS volume name format is selected, note that this function does *not* prepend /// `\\?\` prefix to the resultant path. +/// +/// TODO move this function into std.Io.Threaded and add cancelation checks pub fn GetFinalPathNameByHandle( hFile: HANDLE, fmt: GetFinalPathNameByHandleFormat, diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 370e6bde33..61ef8bb3af 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -1527,7 +1527,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi pub const ListenError = error{ FileDescriptorNotASocket, - OperationNotSupported, + OperationUnsupported, } || std.Io.net.IpAddress.ListenError || std.Io.net.UnixAddress.ListenError; pub fn listen(sock: socket_t, backlog: u31) ListenError!void { @@ -1540,7 +1540,7 @@ pub fn listen(sock: socket_t, backlog: u31) ListenError!void { .ADDRINUSE => return error.AddressInUse, .BADF => unreachable, .NOTSOCK => return error.FileDescriptorNotASocket, - .OPNOTSUPP => return error.OperationNotSupported, + .OPNOTSUPP => return error.OperationUnsupported, else => |err| return unexpectedErrno(err), } } @@ -2202,7 +2202,7 @@ pub const FanotifyMarkError = error{ SystemResources, UserMarkQuotaExceeded, NotDir, - OperationNotSupported, + OperationUnsupported, PermissionDenied, NotSameFileSystem, NameTooLong, @@ -2242,7 +2242,7 @@ pub fn fanotify_markZ( .NOMEM => return error.SystemResources, .NOSPC => return error.UserMarkQuotaExceeded, .NOTDIR => return error.NotDir, - .OPNOTSUPP => return error.OperationNotSupported, + .OPNOTSUPP => return error.OperationUnsupported, .PERM => return error.PermissionDenied, .XDEV => return error.NotSameFileSystem, else => |err| return unexpectedErrno(err), @@ -3466,7 +3466,7 @@ pub const SetSockOptError = error{ /// Setting the socket option requires more elevated permissions. PermissionDenied, - OperationNotSupported, + OperationUnsupported, NetworkDown, FileDescriptorNotASocket, SocketNotBound, @@ -3502,7 +3502,7 @@ pub fn setsockopt(fd: socket_t, level: i32, optname: u32, opt: []const u8) SetSo .NOBUFS => return error.SystemResources, .PERM => return error.PermissionDenied, .NODEV => return error.NoDevice, - .OPNOTSUPP => return error.OperationNotSupported, + .OPNOTSUPP => return error.OperationUnsupported, else => |err| return unexpectedErrno(err), } } @@ -3712,7 +3712,7 @@ pub const PrctlError = error{ /// or PR_MPX_DISABLE_MANAGEMENT UnsupportedFeature, /// Can only occur with PR_SET_FP_MODE - OperationNotSupported, + OperationUnsupported, PermissionDenied, } || UnexpectedError; @@ -3736,7 +3736,7 @@ pub fn prctl(option: PR, args: anytype) PrctlError!u31 { .FAULT => return error.InvalidAddress, .INVAL => unreachable, .NODEV, .NXIO => return error.UnsupportedFeature, - .OPNOTSUPP => return error.OperationNotSupported, + .OPNOTSUPP => return error.OperationUnsupported, .PERM, .BUSY => return error.PermissionDenied, .RANGE => unreachable, else => |err| return unexpectedErrno(err), @@ -3995,7 +3995,7 @@ pub const PtraceError = error{ DeviceBusy, InputOutput, NameTooLong, - OperationNotSupported, + OperationUnsupported, OutOfMemory, ProcessNotFound, PermissionDenied, @@ -4097,7 +4097,7 @@ pub fn ptrace(request: u32, pid: pid_t, addr: usize, data: usize) PtraceError!vo .INVAL => unreachable, .PERM => error.PermissionDenied, .BUSY => error.DeviceBusy, - .NOTSUP => error.OperationNotSupported, + .NOTSUP => error.OperationUnsupported, else => |err| return unexpectedErrno(err), }, @@ -4108,7 +4108,7 @@ pub fn ptrace(request: u32, pid: pid_t, addr: usize, data: usize) PtraceError!vo pub const NameToFileHandleAtError = error{ FileNotFound, NotDir, - OperationNotSupported, + OperationUnsupported, NameTooLong, Unexpected, }; @@ -4137,7 +4137,7 @@ pub fn name_to_handle_atZ( .INVAL => unreachable, // bad flags, or handle_bytes too big .NOENT => return error.FileNotFound, .NOTDIR => return error.NotDir, - .OPNOTSUPP => return error.OperationNotSupported, + .OPNOTSUPP => return error.OperationUnsupported, .OVERFLOW => return error.NameTooLong, else => |err| return unexpectedErrno(err), } diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 0048431bec..648f284131 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -503,7 +503,7 @@ test "rename smoke test" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - const base_path = try tmp.dir.realPathAlloc(io, ".", gpa); + const base_path = try tmp.dir.realPathFileAlloc(io, ".", gpa); defer gpa.free(base_path); const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666; |
