authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-31 00:54:33+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-31 16:41:25+02:00
log4d9eff4bdb368dd4dad99b6b19c83468607cd1d2
tree49b95454ae4485624d4dc145255f7a6f586c6d3a
parenta694f575adbe4ca6852cc7c9022101ebcfc3a8d7

Add prelim `openW` and `openatW`

Added POSIX functions targeting Windows pass `open` and `openat` smoke tests.

5 files changed, 117 insertions(+), 21 deletions(-)

lib/std/child_process.zig+1
...@@ -364,6 +364,7 @@ pub const ChildProcess = struct {...@@ -364,6 +364,7 @@ pub const ChildProcess = struct {
364 error.FileTooBig => unreachable,364 error.FileTooBig => unreachable,
365 error.DeviceBusy => unreachable,365 error.DeviceBusy => unreachable,
366 error.FileLocksNotSupported => unreachable,366 error.FileLocksNotSupported => unreachable,
367 error.BadPathName => unreachable, // Windows-only
367 else => |e| return e,368 else => |e| return e,
368 }369 }
369 else370 else
lib/std/os.zig+71-4
...@@ -1041,6 +1041,9 @@ pub const OpenError = error{...@@ -1041,6 +1041,9 @@ pub const OpenError = error{
10411041
1042 /// The underlying filesystem does not support file locks1042 /// The underlying filesystem does not support file locks
1043 FileLocksNotSupported,1043 FileLocksNotSupported,
1044
1045 BadPathName,
1046 InvalidUtf8,
1044} || UnexpectedError;1047} || UnexpectedError;
10451048
1046/// Open and possibly create a file. Keeps trying if it gets interrupted.1049/// 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...@@ -1092,18 +1095,65 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t
1092 }1095 }
1093}1096}
10941097
1098fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
1099 const w = windows;
1100
1101 var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE;
1102 if (flags & O_RDWR != 0) {
1103 access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
1104 } else if (flags & O_WRONLY != 0) {
1105 access_mask |= w.GENERIC_WRITE;
1106 } else {
1107 access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
1108 }
1109
1110 const open_dir: bool = flags & O_DIRECTORY != 0;
1111 const follow_symlinks: bool = flags & O_NOFOLLOW == 0;
1112
1113 const creation: w.ULONG = blk: {
1114 if (flags & O_CREAT != 0) {
1115 if (flags & O_EXCL != 0) {
1116 break :blk w.FILE_CREATE;
1117 }
1118 }
1119 break :blk w.FILE_OPEN;
1120 };
1121
1122 return .{
1123 .access_mask = access_mask,
1124 .io_mode = .blocking,
1125 .creation = creation,
1126 .open_dir = open_dir,
1127 .follow_symlinks = follow_symlinks,
1128 };
1129}
1130
1095/// Windows-only. The path parameter is1131/// Windows-only. The path parameter is
1096/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.1132/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
1097/// Translates the POSIX open API call to a Windows API call.1133/// Translates the POSIX open API call to a Windows API call.
1098pub fn openW(file_path_w: []const u16, flags: u32, perm: usize) OpenError!fd_t {1134/// TODO currently, this function does not handle all flag combinations
1099 @compileError("TODO implement openW for windows");1135/// or makes use of perm argument.
1136pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t {
1137 var options = openOptionsFromFlags(flags);
1138 options.dir = std.fs.cwd().fd;
1139 return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
1140 error.WouldBlock => unreachable,
1141 error.PipeBusy => unreachable,
1142 else => |e| return e,
1143 };
1100}1144}
11011145
1102/// Open and possibly create a file. Keeps trying if it gets interrupted.1146/// Open and possibly create a file. Keeps trying if it gets interrupted.
1103/// `file_path` is relative to the open directory handle `dir_fd`.1147/// `file_path` is relative to the open directory handle `dir_fd`.
1104/// See also `openatC`.1148/// See also `openatC`.
1105/// TODO support windows
1106pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {1149pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
1150 if (builtin.os.tag == .wasi) {
1151 @compileError("use openatWasi instead");
1152 }
1153 if (builtin.os.tag == .windows) {
1154 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1155 return openatW(dir_fd, file_path_w.span(), flags, mode);
1156 }
1107 const file_path_c = try toPosixPath(file_path);1157 const file_path_c = try toPosixPath(file_path);
1108 return openatZ(dir_fd, &file_path_c, flags, mode);1158 return openatZ(dir_fd, &file_path_c, flags, mode);
1109}1159}
...@@ -1145,8 +1195,11 @@ pub const openatC = @compileError("deprecated: renamed to openatZ");...@@ -1145,8 +1195,11 @@ pub const openatC = @compileError("deprecated: renamed to openatZ");
1145/// Open and possibly create a file. Keeps trying if it gets interrupted.1195/// Open and possibly create a file. Keeps trying if it gets interrupted.
1146/// `file_path` is relative to the open directory handle `dir_fd`.1196/// `file_path` is relative to the open directory handle `dir_fd`.
1147/// See also `openat`.1197/// See also `openat`.
1148/// TODO support windows
1149pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {1198pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
1199 if (builtin.os.tag == .windows) {
1200 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
1201 return openatW(dir_fd, file_path_w.span(), flags, mode);
1202 }
1150 while (true) {1203 while (true) {
1151 const rc = system.openat(dir_fd, file_path, flags, mode);1204 const rc = system.openat(dir_fd, file_path, flags, mode);
1152 switch (errno(rc)) {1205 switch (errno(rc)) {
...@@ -1177,6 +1230,20 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)...@@ -1177,6 +1230,20 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
1177 }1230 }
1178}1231}
11791232
1233/// Windows-only. Similar to `openat` but with pathname argument null-terminated
1234/// WTF16 encoded.
1235/// TODO currently, this function does not handle all flag combinations
1236/// or makes use of perm argument.
1237pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t {
1238 var options = openOptionsFromFlags(flags);
1239 options.dir = dir_fd;
1240 return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
1241 error.WouldBlock => unreachable,
1242 error.PipeBusy => unreachable,
1243 else => |e| return e,
1244 };
1245}
1246
1180pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {1247pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
1181 while (true) {1248 while (true) {
1182 switch (errno(system.dup2(old_fd, new_fd))) {1249 switch (errno(system.dup2(old_fd, new_fd))) {
lib/std/os/bits/windows.zig+25
...@@ -237,3 +237,28 @@ pub const IPPROTO_TCP = ws2_32.IPPROTO_TCP;...@@ -237,3 +237,28 @@ pub const IPPROTO_TCP = ws2_32.IPPROTO_TCP;
237pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;237pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;
238pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;238pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;
239pub const IPPROTO_RM = ws2_32.IPPROTO_RM;239pub const IPPROTO_RM = ws2_32.IPPROTO_RM;
240
241pub const O_RDONLY = 0o0;
242pub const O_WRONLY = 0o1;
243pub const O_RDWR = 0o2;
244
245pub const O_CREAT = 0o100;
246pub const O_EXCL = 0o200;
247pub const O_NOCTTY = 0o400;
248pub const O_TRUNC = 0o1000;
249pub const O_APPEND = 0o2000;
250pub const O_NONBLOCK = 0o4000;
251pub const O_DSYNC = 0o10000;
252pub const O_SYNC = 0o4010000;
253pub const O_RSYNC = 0o4010000;
254pub const O_DIRECTORY = 0o200000;
255pub const O_NOFOLLOW = 0o400000;
256pub const O_CLOEXEC = 0o2000000;
257
258pub const O_ASYNC = 0o20000;
259pub const O_DIRECT = 0o40000;
260pub const O_LARGEFILE = 0;
261pub const O_NOATIME = 0o1000000;
262pub const O_PATH = 0o10000000;
263pub const O_TMPFILE = 0o20200000;
264pub const O_NDELAY = O_NONBLOCK;
\ No newline at end of file
lib/std/os/test.zig+16-16
...@@ -21,7 +21,6 @@ const Dir = std.fs.Dir;...@@ -21,7 +21,6 @@ const Dir = std.fs.Dir;
21const ArenaAllocator = std.heap.ArenaAllocator;21const ArenaAllocator = std.heap.ArenaAllocator;
2222
23test "open smoke test" {23test "open smoke test" {
24 if (builtin.os.tag == .windows) return error.SkipZigTest;
25 if (builtin.os.tag == .wasi) return error.SkipZigTest;24 if (builtin.os.tag == .wasi) return error.SkipZigTest;
2625
27 // TODO verify file attributes using `fstat`26 // TODO verify file attributes using `fstat`
...@@ -40,41 +39,41 @@ test "open smoke test" {...@@ -40,41 +39,41 @@ test "open smoke test" {
4039
41 var file_path: []u8 = undefined;40 var file_path: []u8 = undefined;
42 var fd: os.fd_t = undefined;41 var fd: os.fd_t = undefined;
42 const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
4343
44 // Create some file using `open`.44 // Create some file using `open`.
45 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });45 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
46 fd = try os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666);46 fd = try os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
47 os.close(fd);47 os.close(fd);
4848
49 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.49 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
50 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });50 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
51 expectError(error.PathAlreadyExists, os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666));51 expectError(error.PathAlreadyExists, os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
5252
53 // Try opening without `O_EXCL` flag.53 // Try opening without `O_EXCL` flag.
54 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });54 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
55 fd = try os.open(file_path, os.O_RDWR | os.O_CREAT, 0o666);55 fd = try os.open(file_path, os.O_RDWR | os.O_CREAT, mode);
56 os.close(fd);56 os.close(fd);
5757
58 // Try opening as a directory which should fail.58 // Try opening as a directory which should fail.
59 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });59 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
60 expectError(error.NotDir, os.open(file_path, os.O_RDWR | os.O_DIRECTORY, 0o666));60 expectError(error.NotDir, os.open(file_path, os.O_RDWR | os.O_DIRECTORY, mode));
6161
62 // Create some directory62 // Create some directory
63 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });63 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
64 try os.mkdir(file_path, 0o666);64 try os.mkdir(file_path, mode);
6565
66 // Open dir using `open`66 // Open dir using `open`
67 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });67 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
68 fd = try os.open(file_path, os.O_RDONLY | os.O_DIRECTORY, 0o666);68 fd = try os.open(file_path, os.O_RDONLY | os.O_DIRECTORY, mode);
69 os.close(fd);69 os.close(fd);
7070
71 // Try opening as file which should fail.71 // Try opening as file which should fail.
72 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });72 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
73 expectError(error.IsDir, os.open(file_path, os.O_RDWR, 0o666));73 expectError(error.IsDir, os.open(file_path, os.O_RDWR, mode));
74}74}
7575
76test "openat smoke test" {76test "openat smoke test" {
77 if (builtin.os.tag == .windows) return error.SkipZigTest;
78 if (builtin.os.tag == .wasi) return error.SkipZigTest;77 if (builtin.os.tag == .wasi) return error.SkipZigTest;
7978
80 // TODO verify file attributes using `fstatat`79 // TODO verify file attributes using `fstatat`
...@@ -83,30 +82,31 @@ test "openat smoke test" {...@@ -83,30 +82,31 @@ test "openat smoke test" {
83 defer tmp.cleanup();82 defer tmp.cleanup();
8483
85 var fd: os.fd_t = undefined;84 var fd: os.fd_t = undefined;
85 const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
8686
87 // Create some file using `openat`.87 // Create some file using `openat`.
88 fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666);88 fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
89 os.close(fd);89 os.close(fd);
9090
91 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.91 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
92 expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666));92 expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
9393
94 // Try opening without `O_EXCL` flag.94 // Try opening without `O_EXCL` flag.
95 fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT, 0o666);95 fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT, mode);
96 os.close(fd);96 os.close(fd);
9797
98 // Try opening as a directory which should fail.98 // Try opening as a directory which should fail.
99 expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_DIRECTORY, 0o666));99 expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_DIRECTORY, mode));
100100
101 // Create some directory101 // Create some directory
102 try os.mkdirat(tmp.dir.fd, "some_dir", 0o666);102 try os.mkdirat(tmp.dir.fd, "some_dir", mode);
103103
104 // Open dir using `open`104 // Open dir using `open`
105 fd = try os.openat(tmp.dir.fd, "some_dir", os.O_RDONLY | os.O_DIRECTORY, 0o666);105 fd = try os.openat(tmp.dir.fd, "some_dir", os.O_RDONLY | os.O_DIRECTORY, mode);
106 os.close(fd);106 os.close(fd);
107107
108 // Try opening as file which should fail.108 // Try opening as file which should fail.
109 expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.O_RDWR, 0o666));109 expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.O_RDWR, mode));
110}110}
111111
112test "symlink with relative paths" {112test "symlink with relative paths" {
lib/std/os/windows.zig+4-1
...@@ -27,6 +27,7 @@ pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));...@@ -27,6 +27,7 @@ pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
2727
28pub const OpenError = error{28pub const OpenError = error{
29 IsDir,29 IsDir,
30 NotDir,
30 FileNotFound,31 FileNotFound,
31 NoDevice,32 NoDevice,
32 AccessDenied,33 AccessDenied,
...@@ -125,7 +126,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -125,7 +126,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
125 .PIPE_BUSY => return error.PipeBusy,126 .PIPE_BUSY => return error.PipeBusy,
126 .OBJECT_PATH_SYNTAX_BAD => unreachable,127 .OBJECT_PATH_SYNTAX_BAD => unreachable,
127 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,128 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
128 .FILE_IS_A_DIRECTORY => if (options.open_dir) unreachable else return error.IsDir,129 .FILE_IS_A_DIRECTORY => return error.IsDir,
130 .NOT_A_DIRECTORY => return error.NotDir,
129 else => return unexpectedStatus(rc),131 else => return unexpectedStatus(rc),
130 }132 }
131 }133 }
...@@ -609,6 +611,7 @@ pub fn CreateSymbolicLink(...@@ -609,6 +611,7 @@ pub fn CreateSymbolicLink(
609 .open_dir = is_directory,611 .open_dir = is_directory,
610 }) catch |err| switch (err) {612 }) catch |err| switch (err) {
611 error.IsDir => return error.PathAlreadyExists,613 error.IsDir => return error.PathAlreadyExists,
614 error.NotDir => unreachable,
612 error.WouldBlock => unreachable,615 error.WouldBlock => unreachable,
613 error.PipeBusy => unreachable,616 error.PipeBusy => unreachable,
614 else => |e| return e,617 else => |e| return e,