authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-30 17:00:50+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-31 16:31:23+02:00
loga89d5cfc3eaaf97b914abc5d355099ec8357925d
tree7c4ff9d033abd54aec89601906f8c6b0358fc0f9
parent0d31877444bf6c21307dfd3c412f9673dfd8acab

Remove CreateDirectoryW and CreateFileW calls

Replace them with `std.os.windows.OpenFile` instead. To allow creation/opening of directories, `std.os.windows.OpenFileOptions` now features a `.expect_dir: bool` member which is meant to emualate POSIX's `O_DIRECTORY` flag.

5 files changed, 90 insertions(+), 169 deletions(-)

lib/std/child_process.zig+8-13
......@@ -480,25 +480,20 @@ pub const ChildProcess = struct {
480480
481481 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
482482
483 // TODO use CreateFileW here since we are using a string literal for the path
484483 const nul_handle = if (any_ignore)
485 windows.CreateFile(
486 "NUL",
487 windows.GENERIC_READ,
488 windows.FILE_SHARE_READ,
489 null,
490 windows.OPEN_EXISTING,
491 windows.FILE_ATTRIBUTE_NORMAL,
492 null,
493 ) catch |err| switch (err) {
494 error.SharingViolation => unreachable, // not possible for "NUL"
484 windows.OpenFile(&[_]u16{ 'N', 'U', 'L' }, .{
485 .dir = std.fs.cwd().fd,
486 .access_mask = windows.GENERIC_READ,
487 .share_access = windows.FILE_SHARE_READ,
488 .creation = windows.OPEN_EXISTING,
489 .io_mode = .blocking,
490 }) catch |err| switch (err) {
495491 error.PathAlreadyExists => unreachable, // not possible for "NUL"
496492 error.PipeBusy => unreachable, // not possible for "NUL"
497 error.InvalidUtf8 => unreachable, // not possible for "NUL"
498 error.BadPathName => unreachable, // not possible for "NUL"
499493 error.FileNotFound => unreachable, // not possible for "NUL"
500494 error.AccessDenied => unreachable, // not possible for "NUL"
501495 error.NameTooLong => unreachable, // not possible for "NUL"
496 error.WouldBlock => unreachable, // not possible for "NUL"
502497 else => |e| return e,
503498 }
504499 else
lib/std/fs.zig+2-4
......@@ -225,8 +225,7 @@ pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
225225/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 encoded string.
226226pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
227227 assert(path.isAbsoluteWindowsW(absolute_path_w));
228 const handle = try os.windows.CreateDirectoryW(null, absolute_path_w, null);
229 os.windows.CloseHandle(handle);
228 return os.mkdirW(absolute_path_w, default_new_dir_mode);
230229}
231230
232231pub const deleteDir = @compileError("deprecated; use dir.deleteDir or deleteDirAbsolute");
......@@ -881,8 +880,7 @@ pub const Dir = struct {
881880 }
882881
883882 pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) !void {
884 const handle = try os.windows.CreateDirectoryW(self.fd, sub_path, null);
885 os.windows.CloseHandle(handle);
883 try os.mkdiratW(self.fd, sub_path, default_new_dir_mode);
886884 }
887885
888886 /// Calls makeDir recursively to make an entire path. Returns success if the path
lib/std/fs/watch.zig+7-9
......@@ -374,15 +374,13 @@ pub fn Watch(comptime V: type) type {
374374 defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null);
375375 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
376376
377 const dir_handle = try windows.CreateFileW(
378 dirname_utf16le.ptr,
379 windows.FILE_LIST_DIRECTORY,
380 windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,
381 null,
382 windows.OPEN_EXISTING,
383 windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,
384 null,
385 );
377 const dir_handle = try windows.OpenFile(dirname_utf16le, .{
378 .dir = std.fs.cwd().fd,
379 .access_mask = windows.FILE_LIST_DIRECTORY,
380 .creation = windows.FILE_OPEN,
381 .io_mode = .blocking,
382 .expect_dir = true,
383 });
386384 var dir_handle_consumed = false;
387385 defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);
388386
lib/std/os.zig+65-19
......@@ -2146,7 +2146,18 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
21462146}
21472147
21482148pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
2149 const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null);
2149 const sub_dir_handle = windows.OpenFile(std.mem.spanZ(sub_path_w), .{
2150 .dir = dir_fd,
2151 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
2152 .creation = windows.FILE_CREATE,
2153 .io_mode = .blocking,
2154 .expect_dir = true,
2155 }) catch |err| switch (err) {
2156 error.IsDir => unreachable,
2157 error.PipeBusy => unreachable,
2158 error.WouldBlock => unreachable,
2159 else => |e| return e,
2160 };
21502161 windows.CloseHandle(sub_dir_handle);
21512162}
21522163
......@@ -2175,9 +2186,8 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
21752186 if (builtin.os.tag == .wasi) {
21762187 @compileError("mkdir is not supported in WASI; use mkdirat instead");
21772188 } else if (builtin.os.tag == .windows) {
2178 const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);
2179 windows.CloseHandle(sub_dir_handle);
2180 return;
2189 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
2190 return mkdirW(dir_path_w.span().ptr, mode);
21812191 } else {
21822192 const dir_path_c = try toPosixPath(dir_path);
21832193 return mkdirZ(&dir_path_c, mode);
......@@ -2188,9 +2198,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
21882198pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
21892199 if (builtin.os.tag == .windows) {
21902200 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
2191 const sub_dir_handle = try windows.CreateDirectoryW(null, dir_path_w.span().ptr, null);
2192 windows.CloseHandle(sub_dir_handle);
2193 return;
2201 return mkdirW(dir_path_w.span().ptr, mode);
21942202 }
21952203 switch (errno(system.mkdir(dir_path, mode))) {
21962204 0 => return,
......@@ -2211,6 +2219,23 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
22112219 }
22122220}
22132221
2222/// Windows-only. Same as `mkdir` but the parameters is null-terminated, WTF16 encoded.
2223pub fn mkdirW(dir_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
2224 const sub_dir_handle = windows.OpenFile(std.mem.spanZ(dir_path_w), .{
2225 .dir = std.fs.cwd().fd,
2226 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
2227 .creation = windows.FILE_CREATE,
2228 .io_mode = .blocking,
2229 .expect_dir = true,
2230 }) catch |err| switch (err) {
2231 error.IsDir => unreachable,
2232 error.PipeBusy => unreachable,
2233 error.WouldBlock => unreachable,
2234 else => |e| return e,
2235 };
2236 windows.CloseHandle(sub_dir_handle);
2237}
2238
22142239pub const DeleteDirError = error{
22152240 AccessDenied,
22162241 FileBusy,
......@@ -4013,19 +4038,40 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
40134038/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
40144039/// TODO use ntdll for better semantics
40154040pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4016 const h_file = try windows.CreateFileW(
4017 pathname,
4018 windows.GENERIC_READ,
4019 windows.FILE_SHARE_READ,
4020 null,
4021 windows.OPEN_EXISTING,
4022 windows.FILE_FLAG_BACKUP_SEMANTICS,
4023 null,
4024 );
4025 defer windows.CloseHandle(h_file);
4041 const w = windows;
4042
4043 const dir = std.fs.cwd().fd;
4044 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
4045 const share_access = w.FILE_SHARE_READ;
4046 const creation = w.FILE_OPEN;
4047 const h_file = blk: {
4048 const res = w.OpenFile(std.mem.spanZ(pathname), .{
4049 .dir = dir,
4050 .access_mask = access_mask,
4051 .share_access = share_access,
4052 .creation = creation,
4053 .io_mode = .blocking,
4054 }) catch |err| switch (err) {
4055 error.IsDir => break :blk w.OpenFile(std.mem.spanZ(pathname), .{
4056 .dir = dir,
4057 .access_mask = access_mask,
4058 .share_access = share_access,
4059 .creation = creation,
4060 .io_mode = .blocking,
4061 .expect_dir = true,
4062 }) catch |er| switch (er) {
4063 error.WouldBlock => unreachable,
4064 else => |e2| return e2,
4065 },
4066 error.WouldBlock => unreachable,
4067 else => |e| return e,
4068 };
4069 break :blk res;
4070 };
4071 defer w.CloseHandle(h_file);
40264072
4027 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
4028 const wide_slice = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS);
4073 var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
4074 const wide_slice = try w.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, w.VOLUME_NAME_DOS);
40294075
40304076 // Windows returns \\?\ prepended to the path.
40314077 // We strip it to make this function consistent across platforms.
lib/std/os/windows.zig+8-124
......@@ -49,52 +49,10 @@ pub const CreateFileError = error{
4949 Unexpected,
5050};
5151
52pub fn CreateFile(
53 file_path: []const u8,
54 desired_access: DWORD,
55 share_mode: DWORD,
56 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
57 creation_disposition: DWORD,
58 flags_and_attrs: DWORD,
59 hTemplateFile: ?HANDLE,
60) CreateFileError!HANDLE {
61 const file_path_w = try sliceToPrefixedFileW(file_path);
62 return CreateFileW(file_path_w.span().ptr, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
63}
64
65pub fn CreateFileW(
66 file_path_w: [*:0]const u16,
67 desired_access: DWORD,
68 share_mode: DWORD,
69 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
70 creation_disposition: DWORD,
71 flags_and_attrs: DWORD,
72 hTemplateFile: ?HANDLE,
73) CreateFileError!HANDLE {
74 const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
75
76 if (result == INVALID_HANDLE_VALUE) {
77 switch (kernel32.GetLastError()) {
78 .SHARING_VIOLATION => return error.SharingViolation,
79 .ALREADY_EXISTS => return error.PathAlreadyExists,
80 .FILE_EXISTS => return error.PathAlreadyExists,
81 .FILE_NOT_FOUND => return error.FileNotFound,
82 .PATH_NOT_FOUND => return error.FileNotFound,
83 .ACCESS_DENIED => return error.AccessDenied,
84 .PIPE_BUSY => return error.PipeBusy,
85 .FILENAME_EXCED_RANGE => return error.NameTooLong,
86 else => |err| return unexpectedError(err),
87 }
88 }
89
90 return result;
91}
92
9352pub const OpenError = error{
9453 IsDir,
9554 FileNotFound,
9655 NoDevice,
97 SharingViolation,
9856 AccessDenied,
9957 PipeBusy,
10058 PathAlreadyExists,
......@@ -111,15 +69,16 @@ pub const OpenFileOptions = struct {
11169 share_access_nonblocking: bool = false,
11270 creation: ULONG,
11371 io_mode: std.io.ModeOverride,
72 expect_dir: bool = false,
11473};
11574
11675/// TODO when share_access_nonblocking is false, this implementation uses
11776/// untinterruptible sleep() to block. This is not the final iteration of the API.
11877pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
119 if (mem.eql(u16, sub_path_w, &[_]u16{'.'})) {
78 if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.expect_dir) {
12079 return error.IsDir;
12180 }
122 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' })) {
81 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.expect_dir) {
12382 return error.IsDir;
12483 }
12584
......@@ -145,8 +104,9 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
145104
146105 var delay: usize = 1;
147106 while (true) {
148 var flags: ULONG = undefined;
149107 const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
108 const file_or_dir_flag: ULONG = if (options.expect_dir) FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT else FILE_NON_DIRECTORY_FILE;
109 const flags: ULONG = file_or_dir_flag | blocking_flag;
150110 const rc = ntdll.NtCreateFile(
151111 &result,
152112 options.access_mask,
......@@ -156,7 +116,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
156116 FILE_ATTRIBUTE_NORMAL,
157117 options.share_access,
158118 options.creation,
159 FILE_NON_DIRECTORY_FILE | blocking_flag,
119 flags,
160120 null,
161121 0,
162122 );
......@@ -183,7 +143,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
183143 .PIPE_BUSY => return error.PipeBusy,
184144 .OBJECT_PATH_SYNTAX_BAD => unreachable,
185145 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
186 .FILE_IS_A_DIRECTORY => return error.IsDir,
146 .FILE_IS_A_DIRECTORY => if (options.expect_dir) unreachable else return error.IsDir,
187147 else => return unexpectedStatus(rc),
188148 }
189149 }
......@@ -733,7 +693,6 @@ pub fn CreateSymbolicLinkW(
733693 error.WouldBlock => unreachable,
734694 error.IsDir => return error.PathAlreadyExists,
735695 error.PipeBusy => unreachable,
736 error.SharingViolation => return error.AccessDenied,
737696 else => |e| return e,
738697 };
739698 }
......@@ -915,80 +874,6 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW
915874 }
916875}
917876
918pub const CreateDirectoryError = error{
919 NameTooLong,
920 PathAlreadyExists,
921 FileNotFound,
922 NoDevice,
923 AccessDenied,
924 InvalidUtf8,
925 BadPathName,
926 Unexpected,
927};
928
929/// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`.
930pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE {
931 const pathname_w = try sliceToPrefixedFileW(pathname);
932 return CreateDirectoryW(dir, pathname_w.span().ptr, sa);
933}
934
935/// Same as `CreateDirectory` except takes a WTF-16 encoded path.
936pub fn CreateDirectoryW(
937 dir: ?HANDLE,
938 sub_path_w: [*:0]const u16,
939 sa: ?*SECURITY_ATTRIBUTES,
940) CreateDirectoryError!HANDLE {
941 const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
942 error.Overflow => return error.NameTooLong,
943 };
944 var nt_name = UNICODE_STRING{
945 .Length = path_len_bytes,
946 .MaximumLength = path_len_bytes,
947 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
948 };
949
950 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
951 // Windows does not recognize this, but it does work with empty string.
952 nt_name.Length = 0;
953 }
954
955 var attr = OBJECT_ATTRIBUTES{
956 .Length = @sizeOf(OBJECT_ATTRIBUTES),
957 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
958 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
959 .ObjectName = &nt_name,
960 .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,
961 .SecurityQualityOfService = null,
962 };
963 var io: IO_STATUS_BLOCK = undefined;
964 var result_handle: HANDLE = undefined;
965 const rc = ntdll.NtCreateFile(
966 &result_handle,
967 GENERIC_READ | SYNCHRONIZE,
968 &attr,
969 &io,
970 null,
971 FILE_ATTRIBUTE_NORMAL,
972 FILE_SHARE_READ,
973 FILE_CREATE,
974 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
975 null,
976 0,
977 );
978 switch (rc) {
979 .SUCCESS => return result_handle,
980 .OBJECT_NAME_INVALID => unreachable,
981 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
982 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
983 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
984 .INVALID_PARAMETER => unreachable,
985 .ACCESS_DENIED => return error.AccessDenied,
986 .OBJECT_PATH_SYNTAX_BAD => unreachable,
987 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
988 else => return unexpectedStatus(rc),
989 }
990}
991
992877pub const RemoveDirectoryError = error{
993878 FileNotFound,
994879 DirNotEmpty,
......@@ -1493,8 +1378,7 @@ pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {
14931378}
14941379
14951380/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
1496/// it will get NT-style prefix `\??\` prepended automatically. For prepending
1497/// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead.
1381/// it will get NT-style prefix `\??\` prepended automatically.
14981382pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {
14991383 // TODO https://github.com/ziglang/zig/issues/2765
15001384 var path_space: PathSpace = undefined;