aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-12-18 20:55:47 -0800
committerAndrew Kelley <andrew@ziglang.org>2025-12-23 22:15:10 -0800
commit8632a28ca95a5a491bde57fdc45d9a0ce3cf597e (patch)
treeaa5d5f1db57d1037d5a18a92b69c594425bcb204 /lib/std
parent9bbb8e0d8e8ce413ad2a9125b1eb9ddbf32b6c87 (diff)
downloadzig-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.zig4
-rw-r--r--lib/std/Io/Dir.zig115
-rw-r--r--lib/std/Io/File.zig49
-rw-r--r--lib/std/Io/Threaded.zig66
-rw-r--r--lib/std/fs/test.zig110
-rw-r--r--lib/std/os/windows.zig2
-rw-r--r--lib/std/posix.zig24
-rw-r--r--lib/std/posix/test.zig2
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;