aboutsummaryrefslogtreecommitdiff
path: root/lib/std/os.zig
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2020-08-12 23:50:00 +0200
committerJakub Konka <kubkon@jakubkonka.com>2020-08-13 07:08:39 +0200
commit3e2e6baee568b0fb9d019fc53974aad30386b824 (patch)
tree432202df308ca46ee7f8d26c5021eecdea07cca5 /lib/std/os.zig
parente043396b242d727cd72fecda265bd4b78a86958a (diff)
downloadzig-3e2e6baee568b0fb9d019fc53974aad30386b824.tar.gz
zig-3e2e6baee568b0fb9d019fc53974aad30386b824.zip
Add std.os.getFdPath and std.fs.Dir.realpath
`std.os.getFdPath` is very platform-specific and can be used to query the OS for a canonical path to a file handle. Currently supported hosts are Linux, macOS and Windows. `std.fs.Dir.realpath` (and null-terminated, plus WTF16 versions) are similar to `std.os.realpath`, however, they resolve a path wrt to this `Dir` instance. If the input pathname argument turns out to be an absolute path, this function reverts to calling `realpath` on that pathname completely ignoring this `Dir`.
Diffstat (limited to 'lib/std/os.zig')
-rw-r--r--lib/std/os.zig65
1 files changed, 48 insertions, 17 deletions
diff --git a/lib/std/os.zig b/lib/std/os.zig
index ae2b232ef7..123dfc9747 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -4025,23 +4025,15 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
const pathname_w = try windows.cStrToPrefixedFileW(pathname);
return realpathW(pathname_w.span(), out_buffer);
}
- if (builtin.os.tag == .linux and !builtin.link_libc) {
- const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {
+ if (!builtin.link_libc) {
+ const flags = if (builtin.os.tag == .linux) O_PATH | O_NONBLOCK | O_CLOEXEC else O_NONBLOCK | O_CLOEXEC;
+ const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
error.FileLocksNotSupported => unreachable,
else => |e| return e,
};
defer close(fd);
- var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;
- const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable;
-
- const target = readlinkZ(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer) catch |err| {
- switch (err) {
- error.UnsupportedReparsePointType => unreachable, // Windows only,
- else => |e| return e,
- }
- };
- return target;
+ return getFdPath(fd, out_buffer);
}
const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) {
EINVAL => unreachable,
@@ -4093,12 +4085,51 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat
};
defer w.CloseHandle(h_file);
- var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
- const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, wide_buf[0..]);
+ return getFdPath(h_file, out_buffer);
+}
+
+/// Return canonical path of handle `fd`.
+/// This function is very host-specific and is not universally supported by all hosts.
+/// For example, while it generally works on Linux, macOS or Windows, it is unsupported
+/// on FreeBSD, or WASI.
+pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
+ switch (builtin.os.tag) {
+ .windows => {
+ var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
+ const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
- // Trust that Windows gives us valid UTF-16LE.
- const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
- return out_buffer[0..end_index];
+ // Trust that Windows gives us valid UTF-16LE.
+ const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
+ return out_buffer[0..end_index];
+ },
+ .macosx, .ios, .watchos, .tvos => {
+ // On macOS, we can use F_GETPATH fcntl command to query the OS for
+ // the path to the file descriptor.
+ @memset(out_buffer, 0, MAX_PATH_BYTES);
+ switch (errno(system.fcntl(fd, F_GETPATH, out_buffer))) {
+ 0 => {},
+ EBADF => return error.FileNotFound,
+ // TODO man pages for fcntl on macOS don't really tell you what
+ // errno values to expect when command is F_GETPATH...
+ else => |err| return unexpectedErrno(err),
+ }
+ const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES;
+ return out_buffer[0..len];
+ },
+ .linux => {
+ var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;
+ const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable;
+
+ const target = readlinkZ(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer) catch |err| {
+ switch (err) {
+ error.UnsupportedReparsePointType => unreachable, // Windows only,
+ else => |e| return e,
+ }
+ };
+ return target;
+ },
+ else => @compileError("querying for canonical path of a handle is unsupported on this host"),
+ }
}
/// Spurious wakeups are possible and no precision of timing is guaranteed.