mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 22:04:21 +00:00
Apple's own headers and tbd files prefer to think of Mac Catalyst as a distinct OS target. Earlier, when DriverKit support was added to LLVM, it was represented a distinct OS. So why Apple decided to only represent Mac Catalyst as an ABI in the target triple is beyond me. But this isn't the first time they've ignored established target triple norms (see: armv7k and aarch64_32) and it probably won't be the last. While doing this, I also audited all Darwin OS prongs throughout the codebase and made sure they cover all the tags.
220 lines
9.1 KiB
Zig
220 lines
9.1 KiB
Zig
//! This file contains thin wrappers around OS-specific APIs, with these
|
|
//! specific goals in mind:
|
|
//! * Convert "errno"-style error codes into Zig errors.
|
|
//! * When null-terminated byte buffers are required, provide APIs which accept
|
|
//! slices as well as APIs which accept null-terminated byte buffers. Same goes
|
|
//! for WTF-16LE encoding.
|
|
//! * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide
|
|
//! cross platform abstracting.
|
|
//! * When there exists a corresponding libc function and linking libc, the libc
|
|
//! implementation is used. Exceptions are made for known buggy areas of libc.
|
|
//! On Linux libc can be side-stepped by using `std.os.linux` directly.
|
|
//! * For Windows, this file represents the API that libc would provide for
|
|
//! Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.
|
|
|
|
const root = @import("root");
|
|
const std = @import("std.zig");
|
|
const builtin = @import("builtin");
|
|
const assert = std.debug.assert;
|
|
const math = std.math;
|
|
const mem = std.mem;
|
|
const elf = std.elf;
|
|
const fs = std.fs;
|
|
const dl = @import("dynamic_library.zig");
|
|
const max_path_bytes = std.fs.max_path_bytes;
|
|
const posix = std.posix;
|
|
const native_os = builtin.os.tag;
|
|
|
|
pub const linux = @import("os/linux.zig");
|
|
pub const plan9 = @import("os/plan9.zig");
|
|
pub const uefi = @import("os/uefi.zig");
|
|
pub const wasi = @import("os/wasi.zig");
|
|
pub const emscripten = @import("os/emscripten.zig");
|
|
pub const windows = @import("os/windows.zig");
|
|
pub const freebsd = @import("os/freebsd.zig");
|
|
|
|
test {
|
|
_ = linux;
|
|
if (native_os == .uefi) {
|
|
_ = uefi;
|
|
}
|
|
_ = wasi;
|
|
_ = windows;
|
|
}
|
|
|
|
/// See also `getenv`. Populated by startup code before main().
|
|
/// TODO this is a footgun because the value will be undefined when using `zig build-lib`.
|
|
/// https://github.com/ziglang/zig/issues/4524
|
|
pub var environ: [][*:0]u8 = undefined;
|
|
|
|
/// Populated by startup code before main().
|
|
/// Not available on WASI or Windows without libc. See `std.process.argsAlloc`
|
|
/// or `std.process.argsWithAllocator` for a cross-platform alternative.
|
|
pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (native_os) {
|
|
.windows => @compileError("argv isn't supported on Windows: use std.process.argsAlloc instead"),
|
|
.wasi => @compileError("argv isn't supported on WASI: use std.process.argsAlloc instead"),
|
|
else => undefined,
|
|
};
|
|
|
|
/// Call from Windows-specific code if you already have a WTF-16LE encoded, null terminated string.
|
|
/// Otherwise use `access`.
|
|
pub fn accessW(path: [*:0]const u16) windows.GetFileAttributesError!void {
|
|
const ret = try windows.GetFileAttributesW(path);
|
|
if (ret != windows.INVALID_FILE_ATTRIBUTES) {
|
|
return;
|
|
}
|
|
switch (windows.GetLastError()) {
|
|
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
.ACCESS_DENIED => return error.AccessDenied,
|
|
else => |err| return windows.unexpectedError(err),
|
|
}
|
|
}
|
|
|
|
pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
|
|
return switch (os.tag) {
|
|
.windows,
|
|
.driverkit,
|
|
.ios,
|
|
.maccatalyst,
|
|
.macos,
|
|
.tvos,
|
|
.visionos,
|
|
.watchos,
|
|
.linux,
|
|
.illumos,
|
|
.freebsd,
|
|
.serenity,
|
|
=> true,
|
|
|
|
.dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,
|
|
.netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// 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, FreeBSD or Windows, it is
|
|
/// unsupported on WASI.
|
|
///
|
|
/// * On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
|
/// * On other platforms, the result is an opaque sequence of bytes with no particular encoding.
|
|
///
|
|
/// Calling this function is usually a bug.
|
|
pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix.RealPathError![]u8 {
|
|
if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {
|
|
@compileError("querying for canonical path of a handle is unsupported on this host");
|
|
}
|
|
switch (native_os) {
|
|
.windows => {
|
|
var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
|
|
const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
|
|
|
|
const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
|
|
return out_buffer[0..end_index];
|
|
},
|
|
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
|
|
// 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], 0);
|
|
switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, out_buffer))) {
|
|
.SUCCESS => {},
|
|
.BADF => return error.FileNotFound,
|
|
.NOSPC => return error.NameTooLong,
|
|
.NOENT => 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 posix.unexpectedErrno(err),
|
|
}
|
|
const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes;
|
|
return out_buffer[0..len];
|
|
},
|
|
.linux, .serenity => {
|
|
var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
|
|
const proc_path = std.fmt.bufPrintSentinel(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}, 0) catch unreachable;
|
|
|
|
const target = posix.readlinkZ(proc_path, out_buffer) catch |err| {
|
|
switch (err) {
|
|
error.NotLink => unreachable,
|
|
error.BadPathName => unreachable,
|
|
error.UnsupportedReparsePointType => unreachable, // Windows-only
|
|
error.NetworkNotFound => unreachable, // Windows-only
|
|
else => |e| return e,
|
|
}
|
|
};
|
|
return target;
|
|
},
|
|
.illumos => {
|
|
var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
|
|
const proc_path = std.fmt.bufPrintSentinel(procfs_buf[0..], "/proc/self/path/{d}", .{fd}, 0) catch unreachable;
|
|
|
|
const target = posix.readlinkZ(proc_path, out_buffer) catch |err| switch (err) {
|
|
error.UnsupportedReparsePointType => unreachable,
|
|
error.NotLink => unreachable,
|
|
else => |e| return e,
|
|
};
|
|
return target;
|
|
},
|
|
.freebsd => {
|
|
var kfile: std.c.kinfo_file = undefined;
|
|
kfile.structsize = std.c.KINFO_FILE_SIZE;
|
|
switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&kfile)))) {
|
|
.SUCCESS => {},
|
|
.BADF => return error.FileNotFound,
|
|
else => |err| return posix.unexpectedErrno(err),
|
|
}
|
|
const len = mem.indexOfScalar(u8, &kfile.path, 0) orelse max_path_bytes;
|
|
if (len == 0) return error.NameTooLong;
|
|
const result = out_buffer[0..len];
|
|
@memcpy(result, kfile.path[0..len]);
|
|
return result;
|
|
},
|
|
.dragonfly => {
|
|
@memset(out_buffer[0..max_path_bytes], 0);
|
|
switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) {
|
|
.SUCCESS => {},
|
|
.BADF => return error.FileNotFound,
|
|
.RANGE => return error.NameTooLong,
|
|
else => |err| return posix.unexpectedErrno(err),
|
|
}
|
|
const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes;
|
|
return out_buffer[0..len];
|
|
},
|
|
.netbsd => {
|
|
@memset(out_buffer[0..max_path_bytes], 0);
|
|
switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) {
|
|
.SUCCESS => {},
|
|
.ACCES => return error.AccessDenied,
|
|
.BADF => return error.FileNotFound,
|
|
.NOENT => return error.FileNotFound,
|
|
.NOMEM => return error.SystemResources,
|
|
.RANGE => return error.NameTooLong,
|
|
else => |err| return posix.unexpectedErrno(err),
|
|
}
|
|
const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes;
|
|
return out_buffer[0..len];
|
|
},
|
|
else => unreachable, // made unreachable by isGetFdPathSupportedOnTarget above
|
|
}
|
|
}
|
|
|
|
pub const FstatError = error{
|
|
SystemResources,
|
|
AccessDenied,
|
|
Unexpected,
|
|
};
|
|
|
|
pub fn fstat_wasi(fd: posix.fd_t) FstatError!wasi.filestat_t {
|
|
var stat: wasi.filestat_t = undefined;
|
|
switch (wasi.fd_filestat_get(fd, &stat)) {
|
|
.SUCCESS => return stat,
|
|
.INVAL => unreachable,
|
|
.BADF => unreachable, // Always a race condition.
|
|
.NOMEM => return error.SystemResources,
|
|
.ACCES => return error.AccessDenied,
|
|
.NOTCAPABLE => return error.AccessDenied,
|
|
else => |err| return posix.unexpectedErrno(err),
|
|
}
|
|
}
|