Commit a89d5cfc3e
Changed files (5)
lib
lib/std/fs/watch.zig
@@ -374,15 +374,13 @@ pub fn Watch(comptime V: type) type {
defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null);
const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
- const dir_handle = try windows.CreateFileW(
- dirname_utf16le.ptr,
- windows.FILE_LIST_DIRECTORY,
- windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,
- null,
- windows.OPEN_EXISTING,
- windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,
- null,
- );
+ const dir_handle = try windows.OpenFile(dirname_utf16le, .{
+ .dir = std.fs.cwd().fd,
+ .access_mask = windows.FILE_LIST_DIRECTORY,
+ .creation = windows.FILE_OPEN,
+ .io_mode = .blocking,
+ .expect_dir = true,
+ });
var dir_handle_consumed = false;
defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);
lib/std/os/windows.zig
@@ -49,52 +49,10 @@ pub const CreateFileError = error{
Unexpected,
};
-pub fn CreateFile(
- file_path: []const u8,
- desired_access: DWORD,
- share_mode: DWORD,
- lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
- creation_disposition: DWORD,
- flags_and_attrs: DWORD,
- hTemplateFile: ?HANDLE,
-) CreateFileError!HANDLE {
- const file_path_w = try sliceToPrefixedFileW(file_path);
- return CreateFileW(file_path_w.span().ptr, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
-}
-
-pub fn CreateFileW(
- file_path_w: [*:0]const u16,
- desired_access: DWORD,
- share_mode: DWORD,
- lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
- creation_disposition: DWORD,
- flags_and_attrs: DWORD,
- hTemplateFile: ?HANDLE,
-) CreateFileError!HANDLE {
- const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
-
- if (result == INVALID_HANDLE_VALUE) {
- switch (kernel32.GetLastError()) {
- .SHARING_VIOLATION => return error.SharingViolation,
- .ALREADY_EXISTS => return error.PathAlreadyExists,
- .FILE_EXISTS => return error.PathAlreadyExists,
- .FILE_NOT_FOUND => return error.FileNotFound,
- .PATH_NOT_FOUND => return error.FileNotFound,
- .ACCESS_DENIED => return error.AccessDenied,
- .PIPE_BUSY => return error.PipeBusy,
- .FILENAME_EXCED_RANGE => return error.NameTooLong,
- else => |err| return unexpectedError(err),
- }
- }
-
- return result;
-}
-
pub const OpenError = error{
IsDir,
FileNotFound,
NoDevice,
- SharingViolation,
AccessDenied,
PipeBusy,
PathAlreadyExists,
@@ -111,15 +69,16 @@ pub const OpenFileOptions = struct {
share_access_nonblocking: bool = false,
creation: ULONG,
io_mode: std.io.ModeOverride,
+ expect_dir: bool = false,
};
/// TODO when share_access_nonblocking is false, this implementation uses
/// untinterruptible sleep() to block. This is not the final iteration of the API.
pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
- if (mem.eql(u16, sub_path_w, &[_]u16{'.'})) {
+ if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.expect_dir) {
return error.IsDir;
}
- if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' })) {
+ if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.expect_dir) {
return error.IsDir;
}
@@ -145,8 +104,9 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
var delay: usize = 1;
while (true) {
- var flags: ULONG = undefined;
const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
+ const file_or_dir_flag: ULONG = if (options.expect_dir) FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT else FILE_NON_DIRECTORY_FILE;
+ const flags: ULONG = file_or_dir_flag | blocking_flag;
const rc = ntdll.NtCreateFile(
&result,
options.access_mask,
@@ -156,7 +116,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
FILE_ATTRIBUTE_NORMAL,
options.share_access,
options.creation,
- FILE_NON_DIRECTORY_FILE | blocking_flag,
+ flags,
null,
0,
);
@@ -183,7 +143,7 @@ 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 => return error.IsDir,
+ .FILE_IS_A_DIRECTORY => if (options.expect_dir) unreachable else return error.IsDir,
else => return unexpectedStatus(rc),
}
}
@@ -733,7 +693,6 @@ pub fn CreateSymbolicLinkW(
error.WouldBlock => unreachable,
error.IsDir => return error.PathAlreadyExists,
error.PipeBusy => unreachable,
- error.SharingViolation => return error.AccessDenied,
else => |e| return e,
};
}
@@ -915,80 +874,6 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW
}
}
-pub const CreateDirectoryError = error{
- NameTooLong,
- PathAlreadyExists,
- FileNotFound,
- NoDevice,
- AccessDenied,
- InvalidUtf8,
- BadPathName,
- Unexpected,
-};
-
-/// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`.
-pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE {
- const pathname_w = try sliceToPrefixedFileW(pathname);
- return CreateDirectoryW(dir, pathname_w.span().ptr, sa);
-}
-
-/// Same as `CreateDirectory` except takes a WTF-16 encoded path.
-pub fn CreateDirectoryW(
- dir: ?HANDLE,
- sub_path_w: [*:0]const u16,
- sa: ?*SECURITY_ATTRIBUTES,
-) CreateDirectoryError!HANDLE {
- const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
- error.Overflow => return error.NameTooLong,
- };
- var nt_name = UNICODE_STRING{
- .Length = path_len_bytes,
- .MaximumLength = path_len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
- };
-
- if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
- // Windows does not recognize this, but it does work with empty string.
- nt_name.Length = 0;
- }
-
- var attr = OBJECT_ATTRIBUTES{
- .Length = @sizeOf(OBJECT_ATTRIBUTES),
- .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
- .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
- .ObjectName = &nt_name,
- .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,
- .SecurityQualityOfService = null,
- };
- var io: IO_STATUS_BLOCK = undefined;
- var result_handle: HANDLE = undefined;
- const rc = ntdll.NtCreateFile(
- &result_handle,
- GENERIC_READ | SYNCHRONIZE,
- &attr,
- &io,
- null,
- FILE_ATTRIBUTE_NORMAL,
- FILE_SHARE_READ,
- FILE_CREATE,
- FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
- null,
- 0,
- );
- switch (rc) {
- .SUCCESS => return result_handle,
- .OBJECT_NAME_INVALID => unreachable,
- .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
- .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
- .NO_MEDIA_IN_DEVICE => return error.NoDevice,
- .INVALID_PARAMETER => unreachable,
- .ACCESS_DENIED => return error.AccessDenied,
- .OBJECT_PATH_SYNTAX_BAD => unreachable,
- .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
- else => return unexpectedStatus(rc),
- }
-}
-
pub const RemoveDirectoryError = error{
FileNotFound,
DirNotEmpty,
@@ -1493,8 +1378,7 @@ pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {
}
/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
-/// it will get NT-style prefix `\??\` prepended automatically. For prepending
-/// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead.
+/// it will get NT-style prefix `\??\` prepended automatically.
pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {
// TODO https://github.com/ziglang/zig/issues/2765
var path_space: PathSpace = undefined;
lib/std/child_process.zig
@@ -480,25 +480,20 @@ pub const ChildProcess = struct {
const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
- // TODO use CreateFileW here since we are using a string literal for the path
const nul_handle = if (any_ignore)
- windows.CreateFile(
- "NUL",
- windows.GENERIC_READ,
- windows.FILE_SHARE_READ,
- null,
- windows.OPEN_EXISTING,
- windows.FILE_ATTRIBUTE_NORMAL,
- null,
- ) catch |err| switch (err) {
- error.SharingViolation => unreachable, // not possible for "NUL"
+ windows.OpenFile(&[_]u16{ 'N', 'U', 'L' }, .{
+ .dir = std.fs.cwd().fd,
+ .access_mask = windows.GENERIC_READ,
+ .share_access = windows.FILE_SHARE_READ,
+ .creation = windows.OPEN_EXISTING,
+ .io_mode = .blocking,
+ }) catch |err| switch (err) {
error.PathAlreadyExists => unreachable, // not possible for "NUL"
error.PipeBusy => unreachable, // not possible for "NUL"
- error.InvalidUtf8 => unreachable, // not possible for "NUL"
- error.BadPathName => unreachable, // not possible for "NUL"
error.FileNotFound => unreachable, // not possible for "NUL"
error.AccessDenied => unreachable, // not possible for "NUL"
error.NameTooLong => unreachable, // not possible for "NUL"
+ error.WouldBlock => unreachable, // not possible for "NUL"
else => |e| return e,
}
else
lib/std/fs.zig
@@ -225,8 +225,7 @@ pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 encoded string.
pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
assert(path.isAbsoluteWindowsW(absolute_path_w));
- const handle = try os.windows.CreateDirectoryW(null, absolute_path_w, null);
- os.windows.CloseHandle(handle);
+ return os.mkdirW(absolute_path_w, default_new_dir_mode);
}
pub const deleteDir = @compileError("deprecated; use dir.deleteDir or deleteDirAbsolute");
@@ -881,8 +880,7 @@ pub const Dir = struct {
}
pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) !void {
- const handle = try os.windows.CreateDirectoryW(self.fd, sub_path, null);
- os.windows.CloseHandle(handle);
+ try os.mkdiratW(self.fd, sub_path, default_new_dir_mode);
}
/// Calls makeDir recursively to make an entire path. Returns success if the path
lib/std/os.zig
@@ -2146,7 +2146,18 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
}
pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
- const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null);
+ const sub_dir_handle = windows.OpenFile(std.mem.spanZ(sub_path_w), .{
+ .dir = dir_fd,
+ .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
+ .creation = windows.FILE_CREATE,
+ .io_mode = .blocking,
+ .expect_dir = true,
+ }) catch |err| switch (err) {
+ error.IsDir => unreachable,
+ error.PipeBusy => unreachable,
+ error.WouldBlock => unreachable,
+ else => |e| return e,
+ };
windows.CloseHandle(sub_dir_handle);
}
@@ -2175,9 +2186,8 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
if (builtin.os.tag == .wasi) {
@compileError("mkdir is not supported in WASI; use mkdirat instead");
} else if (builtin.os.tag == .windows) {
- const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);
- windows.CloseHandle(sub_dir_handle);
- return;
+ const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
+ return mkdirW(dir_path_w.span().ptr, mode);
} else {
const dir_path_c = try toPosixPath(dir_path);
return mkdirZ(&dir_path_c, mode);
@@ -2188,9 +2198,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
if (builtin.os.tag == .windows) {
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
- const sub_dir_handle = try windows.CreateDirectoryW(null, dir_path_w.span().ptr, null);
- windows.CloseHandle(sub_dir_handle);
- return;
+ return mkdirW(dir_path_w.span().ptr, mode);
}
switch (errno(system.mkdir(dir_path, mode))) {
0 => return,
@@ -2211,6 +2219,23 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
}
}
+/// Windows-only. Same as `mkdir` but the parameters is null-terminated, WTF16 encoded.
+pub fn mkdirW(dir_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
+ const sub_dir_handle = windows.OpenFile(std.mem.spanZ(dir_path_w), .{
+ .dir = std.fs.cwd().fd,
+ .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
+ .creation = windows.FILE_CREATE,
+ .io_mode = .blocking,
+ .expect_dir = true,
+ }) catch |err| switch (err) {
+ error.IsDir => unreachable,
+ error.PipeBusy => unreachable,
+ error.WouldBlock => unreachable,
+ else => |e| return e,
+ };
+ windows.CloseHandle(sub_dir_handle);
+}
+
pub const DeleteDirError = error{
AccessDenied,
FileBusy,
@@ -4013,19 +4038,40 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
/// TODO use ntdll for better semantics
pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
- const h_file = try windows.CreateFileW(
- pathname,
- windows.GENERIC_READ,
- windows.FILE_SHARE_READ,
- null,
- windows.OPEN_EXISTING,
- windows.FILE_FLAG_BACKUP_SEMANTICS,
- null,
- );
- defer windows.CloseHandle(h_file);
+ const w = windows;
+
+ const dir = std.fs.cwd().fd;
+ const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
+ const share_access = w.FILE_SHARE_READ;
+ const creation = w.FILE_OPEN;
+ const h_file = blk: {
+ const res = w.OpenFile(std.mem.spanZ(pathname), .{
+ .dir = dir,
+ .access_mask = access_mask,
+ .share_access = share_access,
+ .creation = creation,
+ .io_mode = .blocking,
+ }) catch |err| switch (err) {
+ error.IsDir => break :blk w.OpenFile(std.mem.spanZ(pathname), .{
+ .dir = dir,
+ .access_mask = access_mask,
+ .share_access = share_access,
+ .creation = creation,
+ .io_mode = .blocking,
+ .expect_dir = true,
+ }) catch |er| switch (er) {
+ error.WouldBlock => unreachable,
+ else => |e2| return e2,
+ },
+ error.WouldBlock => unreachable,
+ else => |e| return e,
+ };
+ break :blk res;
+ };
+ defer w.CloseHandle(h_file);
- var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
- const wide_slice = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS);
+ var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
+ const wide_slice = try w.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, w.VOLUME_NAME_DOS);
// Windows returns \\?\ prepended to the path.
// We strip it to make this function consistent across platforms.