zig/lib/std/os.zig
Alex Rønne Petersen dba1bf9353 remove all Oracle Solaris support
There is no straightforward way for the Zig team to access the Solaris system
headers; to do this, one has to create an Oracle account, accept their EULA to
download the installer ISO, and finally install it on a machine or VM. We do not
have to jump through hoops like this for any other OS that we support, and no
one on the team has expressed willingness to do it.

As a result, we cannot audit any Solaris contributions to std.c or other
similarly sensitive parts of the standard library. The best we would be able to
do is assume that Solaris and illumos are 100% compatible with no way to verify
that assumption. But at that point, the solaris and illumos OS tags would be
functionally identical anyway.

For Solaris especially, any contributions that involve APIs introduced after the
OS was made closed-source would also be inherently more risky than equivalent
contributions for other proprietary OSs due to the case of Google LLC v. Oracle
America, Inc., wherein Oracle clearly demonstrated its willingness to pursue
legal action against entities that merely copy API declarations.

Finally, Oracle laid off most of the Solaris team in 2017; the OS has been in
maintenance mode since, presumably to be retired completely sometime in the 2030s.

For these reasons, this commit removes all Oracle Solaris support.

Anyone who still wishes to use Zig on Solaris can try their luck by simply using
illumos instead of solaris in target triples - chances are it'll work. But there
will be no effort from the Zig team to support this use case; we recommend that
people move to illumos instead.
2025-10-27 07:35:38 -07:00

236 lines
10 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` or `accessZ`.
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,
.macos,
.ios,
.watchos,
.tvos,
.visionos,
.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];
},
.macos, .ios, .watchos, .tvos, .visionos => {
// 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.InvalidUtf8 => unreachable, // WASI-only
error.InvalidWtf8 => unreachable, // Windows-only
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,
error.InvalidUtf8 => unreachable, // WASI-only
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
}
}
/// WASI-only. Same as `fstatat` but targeting WASI.
/// `pathname` should be encoded as valid UTF-8.
/// See also `fstatat`.
pub fn fstatat_wasi(dirfd: posix.fd_t, pathname: []const u8, flags: wasi.lookupflags_t) posix.FStatAtError!wasi.filestat_t {
var stat: wasi.filestat_t = undefined;
switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
.SUCCESS => return stat,
.INVAL => unreachable,
.BADF => unreachable, // Always a race condition.
.NOMEM => return error.SystemResources,
.ACCES => return error.AccessDenied,
.FAULT => unreachable,
.NAMETOOLONG => return error.NameTooLong,
.NOENT => return error.FileNotFound,
.NOTDIR => return error.FileNotFound,
.NOTCAPABLE => return error.AccessDenied,
.ILSEQ => return error.InvalidUtf8,
else => |err| return posix.unexpectedErrno(err),
}
}
pub fn fstat_wasi(fd: posix.fd_t) posix.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),
}
}