Commit 4d9eff4bdb
Changed files (5)
lib
lib/std/os/bits/windows.zig
@@ -237,3 +237,28 @@ pub const IPPROTO_TCP = ws2_32.IPPROTO_TCP;
pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;
pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;
pub const IPPROTO_RM = ws2_32.IPPROTO_RM;
+
+pub const O_RDONLY = 0o0;
+pub const O_WRONLY = 0o1;
+pub const O_RDWR = 0o2;
+
+pub const O_CREAT = 0o100;
+pub const O_EXCL = 0o200;
+pub const O_NOCTTY = 0o400;
+pub const O_TRUNC = 0o1000;
+pub const O_APPEND = 0o2000;
+pub const O_NONBLOCK = 0o4000;
+pub const O_DSYNC = 0o10000;
+pub const O_SYNC = 0o4010000;
+pub const O_RSYNC = 0o4010000;
+pub const O_DIRECTORY = 0o200000;
+pub const O_NOFOLLOW = 0o400000;
+pub const O_CLOEXEC = 0o2000000;
+
+pub const O_ASYNC = 0o20000;
+pub const O_DIRECT = 0o40000;
+pub const O_LARGEFILE = 0;
+pub const O_NOATIME = 0o1000000;
+pub const O_PATH = 0o10000000;
+pub const O_TMPFILE = 0o20200000;
+pub const O_NDELAY = O_NONBLOCK;
\ No newline at end of file
lib/std/os/test.zig
@@ -21,7 +21,6 @@ const Dir = std.fs.Dir;
const ArenaAllocator = std.heap.ArenaAllocator;
test "open smoke test" {
- if (builtin.os.tag == .windows) return error.SkipZigTest;
if (builtin.os.tag == .wasi) return error.SkipZigTest;
// TODO verify file attributes using `fstat`
@@ -40,41 +39,41 @@ test "open smoke test" {
var file_path: []u8 = undefined;
var fd: os.fd_t = undefined;
+ const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
// Create some file using `open`.
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
- fd = try os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666);
+ fd = try os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
os.close(fd);
// Try this again with the same flags. This op should fail with error.PathAlreadyExists.
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
- expectError(error.PathAlreadyExists, os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666));
+ expectError(error.PathAlreadyExists, os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
// Try opening without `O_EXCL` flag.
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
- fd = try os.open(file_path, os.O_RDWR | os.O_CREAT, 0o666);
+ fd = try os.open(file_path, os.O_RDWR | os.O_CREAT, mode);
os.close(fd);
// Try opening as a directory which should fail.
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
- expectError(error.NotDir, os.open(file_path, os.O_RDWR | os.O_DIRECTORY, 0o666));
+ expectError(error.NotDir, os.open(file_path, os.O_RDWR | os.O_DIRECTORY, mode));
// Create some directory
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
- try os.mkdir(file_path, 0o666);
+ try os.mkdir(file_path, mode);
// Open dir using `open`
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
- fd = try os.open(file_path, os.O_RDONLY | os.O_DIRECTORY, 0o666);
+ fd = try os.open(file_path, os.O_RDONLY | os.O_DIRECTORY, mode);
os.close(fd);
// Try opening as file which should fail.
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
- expectError(error.IsDir, os.open(file_path, os.O_RDWR, 0o666));
+ expectError(error.IsDir, os.open(file_path, os.O_RDWR, mode));
}
test "openat smoke test" {
- if (builtin.os.tag == .windows) return error.SkipZigTest;
if (builtin.os.tag == .wasi) return error.SkipZigTest;
// TODO verify file attributes using `fstatat`
@@ -83,30 +82,31 @@ test "openat smoke test" {
defer tmp.cleanup();
var fd: os.fd_t = undefined;
+ const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
// Create some file using `openat`.
- fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666);
+ fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
os.close(fd);
// Try this again with the same flags. This op should fail with error.PathAlreadyExists.
- expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666));
+ expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
// Try opening without `O_EXCL` flag.
- fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT, 0o666);
+ fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT, mode);
os.close(fd);
// Try opening as a directory which should fail.
- expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_DIRECTORY, 0o666));
+ expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_DIRECTORY, mode));
// Create some directory
- try os.mkdirat(tmp.dir.fd, "some_dir", 0o666);
+ try os.mkdirat(tmp.dir.fd, "some_dir", mode);
// Open dir using `open`
- fd = try os.openat(tmp.dir.fd, "some_dir", os.O_RDONLY | os.O_DIRECTORY, 0o666);
+ fd = try os.openat(tmp.dir.fd, "some_dir", os.O_RDONLY | os.O_DIRECTORY, mode);
os.close(fd);
// Try opening as file which should fail.
- expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.O_RDWR, 0o666));
+ expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.O_RDWR, mode));
}
test "symlink with relative paths" {
lib/std/os/windows.zig
@@ -27,6 +27,7 @@ pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
pub const OpenError = error{
IsDir,
+ NotDir,
FileNotFound,
NoDevice,
AccessDenied,
@@ -125,7 +126,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
.PIPE_BUSY => return error.PipeBusy,
.OBJECT_PATH_SYNTAX_BAD => unreachable,
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
- .FILE_IS_A_DIRECTORY => if (options.open_dir) unreachable else return error.IsDir,
+ .FILE_IS_A_DIRECTORY => return error.IsDir,
+ .NOT_A_DIRECTORY => return error.NotDir,
else => return unexpectedStatus(rc),
}
}
@@ -609,6 +611,7 @@ pub fn CreateSymbolicLink(
.open_dir = is_directory,
}) catch |err| switch (err) {
error.IsDir => return error.PathAlreadyExists,
+ error.NotDir => unreachable,
error.WouldBlock => unreachable,
error.PipeBusy => unreachable,
else => |e| return e,
lib/std/child_process.zig
@@ -364,6 +364,7 @@ pub const ChildProcess = struct {
error.FileTooBig => unreachable,
error.DeviceBusy => unreachable,
error.FileLocksNotSupported => unreachable,
+ error.BadPathName => unreachable, // Windows-only
else => |e| return e,
}
else
lib/std/os.zig
@@ -1041,6 +1041,9 @@ pub const OpenError = error{
/// The underlying filesystem does not support file locks
FileLocksNotSupported,
+
+ BadPathName,
+ InvalidUtf8,
} || UnexpectedError;
/// Open and possibly create a file. Keeps trying if it gets interrupted.
@@ -1092,18 +1095,65 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t
}
}
+fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
+ const w = windows;
+
+ var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE;
+ if (flags & O_RDWR != 0) {
+ access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
+ } else if (flags & O_WRONLY != 0) {
+ access_mask |= w.GENERIC_WRITE;
+ } else {
+ access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
+ }
+
+ const open_dir: bool = flags & O_DIRECTORY != 0;
+ const follow_symlinks: bool = flags & O_NOFOLLOW == 0;
+
+ const creation: w.ULONG = blk: {
+ if (flags & O_CREAT != 0) {
+ if (flags & O_EXCL != 0) {
+ break :blk w.FILE_CREATE;
+ }
+ }
+ break :blk w.FILE_OPEN;
+ };
+
+ return .{
+ .access_mask = access_mask,
+ .io_mode = .blocking,
+ .creation = creation,
+ .open_dir = open_dir,
+ .follow_symlinks = follow_symlinks,
+ };
+}
+
/// Windows-only. The path parameter is
/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
/// Translates the POSIX open API call to a Windows API call.
-pub fn openW(file_path_w: []const u16, flags: u32, perm: usize) OpenError!fd_t {
- @compileError("TODO implement openW for windows");
+/// TODO currently, this function does not handle all flag combinations
+/// or makes use of perm argument.
+pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t {
+ var options = openOptionsFromFlags(flags);
+ options.dir = std.fs.cwd().fd;
+ return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
+ error.WouldBlock => unreachable,
+ error.PipeBusy => unreachable,
+ else => |e| return e,
+ };
}
/// Open and possibly create a file. Keeps trying if it gets interrupted.
/// `file_path` is relative to the open directory handle `dir_fd`.
/// See also `openatC`.
-/// TODO support windows
pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
+ if (builtin.os.tag == .wasi) {
+ @compileError("use openatWasi instead");
+ }
+ if (builtin.os.tag == .windows) {
+ const file_path_w = try windows.sliceToPrefixedFileW(file_path);
+ return openatW(dir_fd, file_path_w.span(), flags, mode);
+ }
const file_path_c = try toPosixPath(file_path);
return openatZ(dir_fd, &file_path_c, flags, mode);
}
@@ -1145,8 +1195,11 @@ pub const openatC = @compileError("deprecated: renamed to openatZ");
/// Open and possibly create a file. Keeps trying if it gets interrupted.
/// `file_path` is relative to the open directory handle `dir_fd`.
/// See also `openat`.
-/// TODO support windows
pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
+ if (builtin.os.tag == .windows) {
+ const file_path_w = try windows.cStrToPrefixedFileW(file_path);
+ return openatW(dir_fd, file_path_w.span(), flags, mode);
+ }
while (true) {
const rc = system.openat(dir_fd, file_path, flags, mode);
switch (errno(rc)) {
@@ -1177,6 +1230,20 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
}
}
+/// Windows-only. Similar to `openat` but with pathname argument null-terminated
+/// WTF16 encoded.
+/// TODO currently, this function does not handle all flag combinations
+/// or makes use of perm argument.
+pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t {
+ var options = openOptionsFromFlags(flags);
+ options.dir = dir_fd;
+ return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
+ error.WouldBlock => unreachable,
+ error.PipeBusy => unreachable,
+ else => |e| return e,
+ };
+}
+
pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
while (true) {
switch (errno(system.dup2(old_fd, new_fd))) {