aboutsummaryrefslogtreecommitdiff
path: root/std/os
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2018-08-10 15:51:17 -0400
committerGitHub <noreply@github.com>2018-08-10 15:51:17 -0400
commitc4b9466da7592b95246909908619b68db5389ceb (patch)
tree6c55cc3ecb3e289fe2c13e346b70560fceaafbef /std/os
parentd927f347de1f5a19545fc235f8779c2326409543 (diff)
parent598e80957e6eccc13ade72ce2693dcd60934763d (diff)
downloadzig-c4b9466da7592b95246909908619b68db5389ceb.tar.gz
zig-c4b9466da7592b95246909908619b68db5389ceb.zip
Merge pull request #1294 from ziglang/async-fs
introduce std.event.fs for async file system functions
Diffstat (limited to 'std/os')
-rw-r--r--std/os/darwin.zig209
-rw-r--r--std/os/file.zig37
-rw-r--r--std/os/index.zig181
-rw-r--r--std/os/linux/index.zig68
-rw-r--r--std/os/path.zig2
-rw-r--r--std/os/windows/index.zig17
-rw-r--r--std/os/windows/kernel32.zig67
-rw-r--r--std/os/windows/util.zig23
8 files changed, 478 insertions, 126 deletions
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index cf67b01d5a..935d28d6f1 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -482,91 +482,98 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
/// data is mach absolute time units
pub const NOTE_MACHTIME = 0x00000100;
-pub const AF_UNSPEC: c_int = 0;
-pub const AF_LOCAL: c_int = 1;
-pub const AF_UNIX: c_int = AF_LOCAL;
-pub const AF_INET: c_int = 2;
-pub const AF_SYS_CONTROL: c_int = 2;
-pub const AF_IMPLINK: c_int = 3;
-pub const AF_PUP: c_int = 4;
-pub const AF_CHAOS: c_int = 5;
-pub const AF_NS: c_int = 6;
-pub const AF_ISO: c_int = 7;
-pub const AF_OSI: c_int = AF_ISO;
-pub const AF_ECMA: c_int = 8;
-pub const AF_DATAKIT: c_int = 9;
-pub const AF_CCITT: c_int = 10;
-pub const AF_SNA: c_int = 11;
-pub const AF_DECnet: c_int = 12;
-pub const AF_DLI: c_int = 13;
-pub const AF_LAT: c_int = 14;
-pub const AF_HYLINK: c_int = 15;
-pub const AF_APPLETALK: c_int = 16;
-pub const AF_ROUTE: c_int = 17;
-pub const AF_LINK: c_int = 18;
-pub const AF_XTP: c_int = 19;
-pub const AF_COIP: c_int = 20;
-pub const AF_CNT: c_int = 21;
-pub const AF_RTIP: c_int = 22;
-pub const AF_IPX: c_int = 23;
-pub const AF_SIP: c_int = 24;
-pub const AF_PIP: c_int = 25;
-pub const AF_ISDN: c_int = 28;
-pub const AF_E164: c_int = AF_ISDN;
-pub const AF_KEY: c_int = 29;
-pub const AF_INET6: c_int = 30;
-pub const AF_NATM: c_int = 31;
-pub const AF_SYSTEM: c_int = 32;
-pub const AF_NETBIOS: c_int = 33;
-pub const AF_PPP: c_int = 34;
-pub const AF_MAX: c_int = 40;
-
-pub const PF_UNSPEC: c_int = AF_UNSPEC;
-pub const PF_LOCAL: c_int = AF_LOCAL;
-pub const PF_UNIX: c_int = PF_LOCAL;
-pub const PF_INET: c_int = AF_INET;
-pub const PF_IMPLINK: c_int = AF_IMPLINK;
-pub const PF_PUP: c_int = AF_PUP;
-pub const PF_CHAOS: c_int = AF_CHAOS;
-pub const PF_NS: c_int = AF_NS;
-pub const PF_ISO: c_int = AF_ISO;
-pub const PF_OSI: c_int = AF_ISO;
-pub const PF_ECMA: c_int = AF_ECMA;
-pub const PF_DATAKIT: c_int = AF_DATAKIT;
-pub const PF_CCITT: c_int = AF_CCITT;
-pub const PF_SNA: c_int = AF_SNA;
-pub const PF_DECnet: c_int = AF_DECnet;
-pub const PF_DLI: c_int = AF_DLI;
-pub const PF_LAT: c_int = AF_LAT;
-pub const PF_HYLINK: c_int = AF_HYLINK;
-pub const PF_APPLETALK: c_int = AF_APPLETALK;
-pub const PF_ROUTE: c_int = AF_ROUTE;
-pub const PF_LINK: c_int = AF_LINK;
-pub const PF_XTP: c_int = AF_XTP;
-pub const PF_COIP: c_int = AF_COIP;
-pub const PF_CNT: c_int = AF_CNT;
-pub const PF_SIP: c_int = AF_SIP;
-pub const PF_IPX: c_int = AF_IPX;
-pub const PF_RTIP: c_int = AF_RTIP;
-pub const PF_PIP: c_int = AF_PIP;
-pub const PF_ISDN: c_int = AF_ISDN;
-pub const PF_KEY: c_int = AF_KEY;
-pub const PF_INET6: c_int = AF_INET6;
-pub const PF_NATM: c_int = AF_NATM;
-pub const PF_SYSTEM: c_int = AF_SYSTEM;
-pub const PF_NETBIOS: c_int = AF_NETBIOS;
-pub const PF_PPP: c_int = AF_PPP;
-pub const PF_MAX: c_int = AF_MAX;
-
-pub const SYSPROTO_EVENT: c_int = 1;
-pub const SYSPROTO_CONTROL: c_int = 2;
-
-pub const SOCK_STREAM: c_int = 1;
-pub const SOCK_DGRAM: c_int = 2;
-pub const SOCK_RAW: c_int = 3;
-pub const SOCK_RDM: c_int = 4;
-pub const SOCK_SEQPACKET: c_int = 5;
-pub const SOCK_MAXADDRLEN: c_int = 255;
+pub const AF_UNSPEC = 0;
+pub const AF_LOCAL = 1;
+pub const AF_UNIX = AF_LOCAL;
+pub const AF_INET = 2;
+pub const AF_SYS_CONTROL = 2;
+pub const AF_IMPLINK = 3;
+pub const AF_PUP = 4;
+pub const AF_CHAOS = 5;
+pub const AF_NS = 6;
+pub const AF_ISO = 7;
+pub const AF_OSI = AF_ISO;
+pub const AF_ECMA = 8;
+pub const AF_DATAKIT = 9;
+pub const AF_CCITT = 10;
+pub const AF_SNA = 11;
+pub const AF_DECnet = 12;
+pub const AF_DLI = 13;
+pub const AF_LAT = 14;
+pub const AF_HYLINK = 15;
+pub const AF_APPLETALK = 16;
+pub const AF_ROUTE = 17;
+pub const AF_LINK = 18;
+pub const AF_XTP = 19;
+pub const AF_COIP = 20;
+pub const AF_CNT = 21;
+pub const AF_RTIP = 22;
+pub const AF_IPX = 23;
+pub const AF_SIP = 24;
+pub const AF_PIP = 25;
+pub const AF_ISDN = 28;
+pub const AF_E164 = AF_ISDN;
+pub const AF_KEY = 29;
+pub const AF_INET6 = 30;
+pub const AF_NATM = 31;
+pub const AF_SYSTEM = 32;
+pub const AF_NETBIOS = 33;
+pub const AF_PPP = 34;
+pub const AF_MAX = 40;
+
+pub const PF_UNSPEC = AF_UNSPEC;
+pub const PF_LOCAL = AF_LOCAL;
+pub const PF_UNIX = PF_LOCAL;
+pub const PF_INET = AF_INET;
+pub const PF_IMPLINK = AF_IMPLINK;
+pub const PF_PUP = AF_PUP;
+pub const PF_CHAOS = AF_CHAOS;
+pub const PF_NS = AF_NS;
+pub const PF_ISO = AF_ISO;
+pub const PF_OSI = AF_ISO;
+pub const PF_ECMA = AF_ECMA;
+pub const PF_DATAKIT = AF_DATAKIT;
+pub const PF_CCITT = AF_CCITT;
+pub const PF_SNA = AF_SNA;
+pub const PF_DECnet = AF_DECnet;
+pub const PF_DLI = AF_DLI;
+pub const PF_LAT = AF_LAT;
+pub const PF_HYLINK = AF_HYLINK;
+pub const PF_APPLETALK = AF_APPLETALK;
+pub const PF_ROUTE = AF_ROUTE;
+pub const PF_LINK = AF_LINK;
+pub const PF_XTP = AF_XTP;
+pub const PF_COIP = AF_COIP;
+pub const PF_CNT = AF_CNT;
+pub const PF_SIP = AF_SIP;
+pub const PF_IPX = AF_IPX;
+pub const PF_RTIP = AF_RTIP;
+pub const PF_PIP = AF_PIP;
+pub const PF_ISDN = AF_ISDN;
+pub const PF_KEY = AF_KEY;
+pub const PF_INET6 = AF_INET6;
+pub const PF_NATM = AF_NATM;
+pub const PF_SYSTEM = AF_SYSTEM;
+pub const PF_NETBIOS = AF_NETBIOS;
+pub const PF_PPP = AF_PPP;
+pub const PF_MAX = AF_MAX;
+
+pub const SYSPROTO_EVENT = 1;
+pub const SYSPROTO_CONTROL = 2;
+
+pub const SOCK_STREAM = 1;
+pub const SOCK_DGRAM = 2;
+pub const SOCK_RAW = 3;
+pub const SOCK_RDM = 4;
+pub const SOCK_SEQPACKET = 5;
+pub const SOCK_MAXADDRLEN = 255;
+
+pub const IPPROTO_ICMP = 1;
+pub const IPPROTO_ICMPV6 = 58;
+pub const IPPROTO_TCP = 6;
+pub const IPPROTO_UDP = 17;
+pub const IPPROTO_IP = 0;
+pub const IPPROTO_IPV6 = 41;
fn wstatus(x: i32) i32 {
return x & 0o177;
@@ -605,6 +612,11 @@ pub fn abort() noreturn {
c.abort();
}
+// bind(int socket, const struct sockaddr *address, socklen_t address_len)
+pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize {
+ return errnoWrap(c.bind(@bitCast(c_int, fd), addr, len));
+}
+
pub fn exit(code: i32) noreturn {
c.exit(code);
}
@@ -634,6 +646,10 @@ pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {
return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte));
}
+pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize {
+ return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset));
+}
+
pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {
return errnoWrap(c.stat(path, buf));
}
@@ -642,6 +658,10 @@ pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {
return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte));
}
+pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize {
+ return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset));
+}
+
pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
const ptr_result = c.mmap(
@ptrCast(*c_void, address),
@@ -805,6 +825,20 @@ pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigacti
return result;
}
+pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
+ return errnoWrap(c.socket(@bitCast(c_int, domain), @bitCast(c_int, socket_type), @bitCast(c_int, protocol)));
+}
+
+pub const iovec = extern struct {
+ iov_base: [*]u8,
+ iov_len: usize,
+};
+
+pub const iovec_const = extern struct {
+ iov_base: [*]const u8,
+ iov_len: usize,
+};
+
pub const sigset_t = c.sigset_t;
pub const empty_sigset = sigset_t(0);
@@ -812,8 +846,13 @@ pub const timespec = c.timespec;
pub const Stat = c.Stat;
pub const dirent = c.dirent;
+pub const in_port_t = c.in_port_t;
pub const sa_family_t = c.sa_family_t;
+pub const socklen_t = c.socklen_t;
+
pub const sockaddr = c.sockaddr;
+pub const sockaddr_in = c.sockaddr_in;
+pub const sockaddr_in6 = c.sockaddr_in6;
/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.
pub const Kevent = c.Kevent;
diff --git a/std/os/file.zig b/std/os/file.zig
index 6998ba00d1..074547193c 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -15,6 +15,16 @@ pub const File = struct {
/// The OS-specific file descriptor or file handle.
handle: os.FileHandle,
+ pub const Mode = switch (builtin.os) {
+ Os.windows => void,
+ else => u32,
+ };
+
+ pub const default_mode = switch (builtin.os) {
+ Os.windows => {},
+ else => 0o666,
+ };
+
pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
@@ -39,16 +49,16 @@ pub const File = struct {
}
}
- /// Calls `openWriteMode` with os.default_file_mode for the mode.
+ /// Calls `openWriteMode` with os.File.default_mode for the mode.
pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
- return openWriteMode(allocator, path, os.default_file_mode);
+ return openWriteMode(allocator, path, os.File.default_mode);
}
/// If the path does not exist it will be created.
/// If a file already exists in the destination it will be truncated.
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
/// Call close to clean up.
- pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
+ pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
if (is_posix) {
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
const fd = try os.posixOpen(allocator, path, flags, file_mode);
@@ -72,7 +82,7 @@ pub const File = struct {
/// If a file already exists in the destination this returns OpenError.PathAlreadyExists
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
/// Call close to clean up.
- pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
+ pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
if (is_posix) {
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
const fd = try os.posixOpen(allocator, path, flags, file_mode);
@@ -282,7 +292,7 @@ pub const File = struct {
Unexpected,
};
- pub fn mode(self: *File) ModeError!os.FileMode {
+ pub fn mode(self: *File) ModeError!Mode {
if (is_posix) {
var stat: posix.Stat = undefined;
const err = posix.getErrno(posix.fstat(self.handle, &stat));
@@ -296,7 +306,7 @@ pub const File = struct {
// TODO: we should be able to cast u16 to ModeError!u32, making this
// explicit cast not necessary
- return os.FileMode(stat.mode);
+ return Mode(stat.mode);
} else if (is_windows) {
return {};
} else {
@@ -305,9 +315,11 @@ pub const File = struct {
}
pub const ReadError = error{
- BadFd,
- Io,
+ FileClosed,
+ InputOutput,
IsDir,
+ WouldBlock,
+ SystemResources,
Unexpected,
};
@@ -323,9 +335,12 @@ pub const File = struct {
posix.EINTR => continue,
posix.EINVAL => unreachable,
posix.EFAULT => unreachable,
- posix.EBADF => return error.BadFd,
- posix.EIO => return error.Io,
+ posix.EAGAIN => return error.WouldBlock,
+ posix.EBADF => return error.FileClosed,
+ posix.EIO => return error.InputOutput,
posix.EISDIR => return error.IsDir,
+ posix.ENOBUFS => return error.SystemResources,
+ posix.ENOMEM => return error.SystemResources,
else => return os.unexpectedErrorPosix(read_err),
}
}
@@ -338,7 +353,7 @@ pub const File = struct {
while (index < buffer.len) {
const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
var amt_read: windows.DWORD = undefined;
- if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) {
+ if (windows.ReadFile(self.handle, buffer.ptr + index, want_read_count, &amt_read, null) == 0) {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.OPERATION_ABORTED => continue,
diff --git a/std/os/index.zig b/std/os/index.zig
index d47444d67d..34be1acabe 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -38,16 +38,6 @@ pub const path = @import("path.zig");
pub const File = @import("file.zig").File;
pub const time = @import("time.zig");
-pub const FileMode = switch (builtin.os) {
- Os.windows => void,
- else => u32,
-};
-
-pub const default_file_mode = switch (builtin.os) {
- Os.windows => {},
- else => 0o666,
-};
-
pub const page_size = 4 * 1024;
pub const UserInfo = @import("get_user_id.zig").UserInfo;
@@ -256,6 +246,67 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
}
}
+/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
+pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) !usize {
+ switch (builtin.os) {
+ builtin.Os.macosx => {
+ // Darwin does not have preadv but it does have pread.
+ var off: usize = 0;
+ var iov_i: usize = 0;
+ var inner_off: usize = 0;
+ while (true) {
+ const v = iov[iov_i];
+ const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
+ const err = darwin.getErrno(rc);
+ switch (err) {
+ 0 => {
+ off += rc;
+ inner_off += rc;
+ if (inner_off == v.iov_len) {
+ iov_i += 1;
+ inner_off = 0;
+ if (iov_i == count) {
+ return off;
+ }
+ }
+ if (rc == 0) return off; // EOF
+ continue;
+ },
+ posix.EINTR => continue,
+ posix.EINVAL => unreachable,
+ posix.EFAULT => unreachable,
+ posix.ESPIPE => unreachable, // fd is not seekable
+ posix.EAGAIN => return error.WouldBlock,
+ posix.EBADF => return error.FileClosed,
+ posix.EIO => return error.InputOutput,
+ posix.EISDIR => return error.IsDir,
+ posix.ENOBUFS => return error.SystemResources,
+ posix.ENOMEM => return error.SystemResources,
+ else => return unexpectedErrorPosix(err),
+ }
+ }
+ },
+ builtin.Os.linux, builtin.Os.freebsd => while (true) {
+ const rc = posix.preadv(fd, iov, count, offset);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return rc,
+ posix.EINTR => continue,
+ posix.EINVAL => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EAGAIN => return error.WouldBlock,
+ posix.EBADF => return error.FileClosed,
+ posix.EIO => return error.InputOutput,
+ posix.EISDIR => return error.IsDir,
+ posix.ENOBUFS => return error.SystemResources,
+ posix.ENOMEM => return error.SystemResources,
+ else => return unexpectedErrorPosix(err),
+ }
+ },
+ else => @compileError("Unsupported OS"),
+ }
+}
+
pub const PosixWriteError = error{
WouldBlock,
FileClosed,
@@ -300,6 +351,71 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
}
}
+pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void {
+ switch (builtin.os) {
+ builtin.Os.macosx => {
+ // Darwin does not have pwritev but it does have pwrite.
+ var off: usize = 0;
+ var iov_i: usize = 0;
+ var inner_off: usize = 0;
+ while (true) {
+ const v = iov[iov_i];
+ const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
+ const err = darwin.getErrno(rc);
+ switch (err) {
+ 0 => {
+ off += rc;
+ inner_off += rc;
+ if (inner_off == v.iov_len) {
+ iov_i += 1;
+ inner_off = 0;
+ if (iov_i == count) {
+ return;
+ }
+ }
+ continue;
+ },
+ posix.EINTR => continue,
+ posix.ESPIPE => unreachable, // fd is not seekable
+ posix.EINVAL => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EAGAIN => return PosixWriteError.WouldBlock,
+ posix.EBADF => return PosixWriteError.FileClosed,
+ posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired,
+ posix.EDQUOT => return PosixWriteError.DiskQuota,
+ posix.EFBIG => return PosixWriteError.FileTooBig,
+ posix.EIO => return PosixWriteError.InputOutput,
+ posix.ENOSPC => return PosixWriteError.NoSpaceLeft,
+ posix.EPERM => return PosixWriteError.AccessDenied,
+ posix.EPIPE => return PosixWriteError.BrokenPipe,
+ else => return unexpectedErrorPosix(err),
+ }
+ }
+ },
+ builtin.Os.linux => while (true) {
+ const rc = posix.pwritev(fd, iov, count, offset);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ posix.EINTR => continue,
+ posix.EINVAL => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EAGAIN => return PosixWriteError.WouldBlock,
+ posix.EBADF => return PosixWriteError.FileClosed,
+ posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired,
+ posix.EDQUOT => return PosixWriteError.DiskQuota,
+ posix.EFBIG => return PosixWriteError.FileTooBig,
+ posix.EIO => return PosixWriteError.InputOutput,
+ posix.ENOSPC => return PosixWriteError.NoSpaceLeft,
+ posix.EPERM => return PosixWriteError.AccessDenied,
+ posix.EPIPE => return PosixWriteError.BrokenPipe,
+ else => return unexpectedErrorPosix(err),
+ }
+ },
+ else => @compileError("Unsupported OS"),
+ }
+}
+
pub const PosixOpenError = error{
OutOfMemory,
AccessDenied,
@@ -853,7 +969,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con
/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
/// merged and readily available,
/// there is a possibility of power loss or application termination leaving temporary files present
-pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void {
+pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
var in_file = try os.File.openRead(allocator, source_path);
defer in_file.close();
@@ -879,7 +995,7 @@ pub const AtomicFile = struct {
/// dest_path must remain valid for the lifetime of AtomicFile
/// call finish to atomically replace dest_path with contents
- pub fn init(allocator: *Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile {
+ pub fn init(allocator: *Allocator, dest_path: []const u8, mode: File.Mode) !AtomicFile {
const dirname = os.path.dirname(dest_path);
var rand_buf: [12]u8 = undefined;
@@ -2943,3 +3059,44 @@ pub fn bsdKEvent(
}
}
}
+
+pub fn linuxINotifyInit1(flags: u32) !i32 {
+ const rc = linux.inotify_init1(flags);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return @intCast(i32, rc),
+ posix.EINVAL => unreachable,
+ posix.EMFILE => return error.ProcessFdQuotaExceeded,
+ posix.ENFILE => return error.SystemFdQuotaExceeded,
+ posix.ENOMEM => return error.SystemResources,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 {
+ const rc = linux.inotify_add_watch(inotify_fd, pathname, mask);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return @intCast(i32, rc),
+ posix.EACCES => return error.AccessDenied,
+ posix.EBADF => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ posix.ENAMETOOLONG => return error.NameTooLong,
+ posix.ENOENT => return error.FileNotFound,
+ posix.ENOMEM => return error.SystemResources,
+ posix.ENOSPC => return error.UserResourceLimitReached,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void {
+ const rc = linux.inotify_rm_watch(inotify_fd, wd);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return rc,
+ posix.EBADF => unreachable,
+ posix.EINVAL => unreachable,
+ else => unreachable,
+ }
+}
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 15ca649f06..c369921e14 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -567,6 +567,37 @@ pub const MNT_DETACH = 2;
pub const MNT_EXPIRE = 4;
pub const UMOUNT_NOFOLLOW = 8;
+pub const IN_CLOEXEC = O_CLOEXEC;
+pub const IN_NONBLOCK = O_NONBLOCK;
+
+pub const IN_ACCESS = 0x00000001;
+pub const IN_MODIFY = 0x00000002;
+pub const IN_ATTRIB = 0x00000004;
+pub const IN_CLOSE_WRITE = 0x00000008;
+pub const IN_CLOSE_NOWRITE = 0x00000010;
+pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE;
+pub const IN_OPEN = 0x00000020;
+pub const IN_MOVED_FROM = 0x00000040;
+pub const IN_MOVED_TO = 0x00000080;
+pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO;
+pub const IN_CREATE = 0x00000100;
+pub const IN_DELETE = 0x00000200;
+pub const IN_DELETE_SELF = 0x00000400;
+pub const IN_MOVE_SELF = 0x00000800;
+pub const IN_ALL_EVENTS = 0x00000fff;
+
+pub const IN_UNMOUNT = 0x00002000;
+pub const IN_Q_OVERFLOW = 0x00004000;
+pub const IN_IGNORED = 0x00008000;
+
+pub const IN_ONLYDIR = 0x01000000;
+pub const IN_DONT_FOLLOW = 0x02000000;
+pub const IN_EXCL_UNLINK = 0x04000000;
+pub const IN_MASK_ADD = 0x20000000;
+
+pub const IN_ISDIR = 0x40000000;
+pub const IN_ONESHOT = 0x80000000;
+
pub const S_IFMT = 0o170000;
pub const S_IFDIR = 0o040000;
@@ -692,6 +723,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us
return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
}
+pub fn futex_wake(uaddr: usize, futex_op: u32, val: i32) usize {
+ return syscall3(SYS_futex, uaddr, futex_op, @bitCast(u32, val));
+}
+
pub fn getcwd(buf: [*]u8, size: usize) usize {
return syscall2(SYS_getcwd, @ptrToInt(buf), size);
}
@@ -700,6 +735,18 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {
return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);
}
+pub fn inotify_init1(flags: u32) usize {
+ return syscall1(SYS_inotify_init1, flags);
+}
+
+pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize {
+ return syscall3(SYS_inotify_add_watch, @intCast(usize, fd), @ptrToInt(pathname), mask);
+}
+
+pub fn inotify_rm_watch(fd: i32, wd: i32) usize {
+ return syscall2(SYS_inotify_rm_watch, @intCast(usize, fd), @intCast(usize, wd));
+}
+
pub fn isatty(fd: i32) bool {
var wsz: winsize = undefined;
return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
@@ -742,6 +789,14 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count);
}
+pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize {
+ return syscall4(SYS_preadv, @intCast(usize, fd), @ptrToInt(iov), count, offset);
+}
+
+pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize {
+ return syscall4(SYS_pwritev, @intCast(usize, fd), @ptrToInt(iov), count, offset);
+}
+
// TODO https://github.com/ziglang/zig/issues/265
pub fn rmdir(path: [*]const u8) usize {
return syscall1(SYS_rmdir, @ptrToInt(path));
@@ -1064,6 +1119,11 @@ pub const iovec = extern struct {
iov_len: usize,
};
+pub const iovec_const = extern struct {
+ iov_base: [*]const u8,
+ iov_len: usize,
+};
+
pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));
}
@@ -1372,6 +1432,14 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {
return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
}
+pub const inotify_event = extern struct {
+ wd: i32,
+ mask: u32,
+ cookie: u32,
+ len: u32,
+ //name: [?]u8,
+};
+
test "import" {
if (builtin.os == builtin.Os.linux) {
_ = @import("test.zig");
diff --git a/std/os/path.zig b/std/os/path.zig
index d3ab0c519f..23c217b295 100644
--- a/std/os/path.zig
+++ b/std/os/path.zig
@@ -506,7 +506,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
result_index += 1;
}
- return result[0..result_index];
+ return allocator.shrink(u8, result, result_index);
}
/// This function is like a series of `cd` statements executed one after another.
diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
index 90ccfaf6c5..bb055468a5 100644
--- a/std/os/windows/index.zig
+++ b/std/os/windows/index.zig
@@ -67,8 +67,9 @@ pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
pub const OVERLAPPED = extern struct {
Internal: ULONG_PTR,
InternalHigh: ULONG_PTR,
- Pointer: PVOID,
- hEvent: HANDLE,
+ Offset: DWORD,
+ OffsetHigh: DWORD,
+ hEvent: ?HANDLE,
};
pub const LPOVERLAPPED = *OVERLAPPED;
@@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));
+
+pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
+pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
+pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
+pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
+pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
+pub const FILE_FLAG_OVERLAPPED = 0x40000000;
+pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
+pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
+pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
+pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
+pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig
index daeebf1021..0cdf27754a 100644
--- a/std/os/windows/kernel32.zig
+++ b/std/os/windows/kernel32.zig
@@ -1,5 +1,8 @@
use @import("index.zig");
+
+pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
+
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn CreateDirectoryA(
@@ -8,7 +11,17 @@ pub extern "kernel32" stdcallcc fn CreateDirectoryA(
) BOOL;
pub extern "kernel32" stdcallcc fn CreateFileA(
- lpFileName: LPCSTR,
+ lpFileName: [*]const u8, // TODO null terminated pointer type
+ dwDesiredAccess: DWORD,
+ dwShareMode: DWORD,
+ lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
+ dwCreationDisposition: DWORD,
+ dwFlagsAndAttributes: DWORD,
+ hTemplateFile: ?HANDLE,
+) HANDLE;
+
+pub extern "kernel32" stdcallcc fn CreateFileW(
+ lpFileName: [*]const u16, // TODO null terminated pointer type
dwDesiredAccess: DWORD,
dwShareMode: DWORD,
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
@@ -94,6 +107,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
dwFlags: DWORD,
) DWORD;
+
+pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
+
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
@@ -104,7 +120,6 @@ pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: S
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
-pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
@@ -114,6 +129,8 @@ pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBy
pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
+pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
+
pub extern "kernel32" stdcallcc fn MoveFileExA(
lpExistingFileName: LPCSTR,
lpNewFileName: LPCSTR,
@@ -126,11 +143,22 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
+pub extern "kernel32" stdcallcc fn ReadDirectoryChangesW(
+ hDirectory: HANDLE,
+ lpBuffer: [*]align(@alignOf(FILE_NOTIFY_INFORMATION)) u8,
+ nBufferLength: DWORD,
+ bWatchSubtree: BOOL,
+ dwNotifyFilter: DWORD,
+ lpBytesReturned: ?*DWORD,
+ lpOverlapped: ?*OVERLAPPED,
+ lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
+) BOOL;
+
pub extern "kernel32" stdcallcc fn ReadFile(
in_hFile: HANDLE,
- out_lpBuffer: *c_void,
+ out_lpBuffer: [*]u8,
in_nNumberOfBytesToRead: DWORD,
- out_lpNumberOfBytesRead: *DWORD,
+ out_lpNumberOfBytesRead: ?*DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
@@ -153,13 +181,42 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis
pub extern "kernel32" stdcallcc fn WriteFile(
in_hFile: HANDLE,
- in_lpBuffer: *const c_void,
+ in_lpBuffer: [*]const u8,
in_nNumberOfBytesToWrite: DWORD,
out_lpNumberOfBytesWritten: ?*DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
+pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
+
//TODO: call unicode versions instead of relying on ANSI code page
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
+
+
+pub const FILE_NOTIFY_INFORMATION = extern struct {
+ NextEntryOffset: DWORD,
+ Action: DWORD,
+ FileNameLength: DWORD,
+ FileName: [1]WCHAR,
+};
+
+pub const FILE_ACTION_ADDED = 0x00000001;
+pub const FILE_ACTION_REMOVED = 0x00000002;
+pub const FILE_ACTION_MODIFIED = 0x00000003;
+pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
+pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
+
+pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn(DWORD, DWORD, *OVERLAPPED) void;
+
+pub const FILE_LIST_DIRECTORY = 1;
+
+pub const FILE_NOTIFY_CHANGE_CREATION = 64;
+pub const FILE_NOTIFY_CHANGE_SIZE = 8;
+pub const FILE_NOTIFY_CHANGE_SECURITY = 256;
+pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32;
+pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
+pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
+pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
+pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig
index c9d2c3c3e6..2f9f4f2c72 100644
--- a/std/os/windows/util.zig
+++ b/std/os/windows/util.zig
@@ -36,20 +36,19 @@ pub fn windowsClose(handle: windows.HANDLE) void {
pub const WriteError = error{
SystemResources,
OperationAborted,
- IoPending,
BrokenPipe,
Unexpected,
};
pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {
- if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), @intCast(u32, bytes.len), null, null) == 0) {
+ if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), null, null) == 0) {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
- windows.ERROR.IO_PENDING => WriteError.IoPending,
+ windows.ERROR.IO_PENDING => unreachable,
windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
else => os.unexpectedErrorWindows(err),
};
@@ -221,6 +220,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl
const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
const err = windows.GetLastError();
switch (err) {
+ windows.ERROR.INVALID_PARAMETER => unreachable,
else => return os.unexpectedErrorWindows(err),
}
};
@@ -238,21 +238,24 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_
}
}
-pub const WindowsWaitResult = error{
+pub const WindowsWaitResult = enum{
Normal,
Aborted,
+ Cancelled,
};
pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {
if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
- if (std.debug.runtime_safety) {
- const err = windows.GetLastError();
- if (err != windows.ERROR.ABANDONED_WAIT_0) {
- std.debug.warn("err: {}\n", err);
+ const err = windows.GetLastError();
+ switch (err) {
+ windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted,
+ windows.ERROR.OPERATION_ABORTED => return WindowsWaitResult.Cancelled,
+ else => {
+ if (std.debug.runtime_safety) {
+ std.debug.panic("unexpected error: {}\n", err);
+ }
}
- assert(err == windows.ERROR.ABANDONED_WAIT_0);
}
- return WindowsWaitResult.Aborted;
}
return WindowsWaitResult.Normal;
}