Commit 8173fbfb66

Andrew Kelley <andrew@ziglang.org>
2020-02-16 23:10:43
implement os.faccessat for Windows
1 parent 4b02a39
Changed files (4)
lib
std
lib/std/event/batch.zig
@@ -109,13 +109,13 @@ pub fn Batch(
 
 test "std.event.Batch" {
     var count: usize = 0;
-    var batch = Batch(void, 2).init();
+    var batch = Batch(void, 2, .auto_async).init();
     batch.add(&async sleepALittle(&count));
     batch.add(&async increaseByTen(&count));
     batch.wait();
     testing.expect(count == 11);
 
-    var another = Batch(anyerror!void, 2).init();
+    var another = Batch(anyerror!void, 2, .auto_async).init();
     another.add(&async somethingElse());
     another.add(&async doSomethingThatFails());
     testing.expectError(error.ItBroke, another.wait());
lib/std/os/windows/ntdll.zig
@@ -8,6 +8,12 @@ pub extern "NtDll" fn NtQueryInformationFile(
     Length: ULONG,
     FileInformationClass: FILE_INFORMATION_CLASS,
 ) callconv(.Stdcall) NTSTATUS;
+
+pub extern "NtDll" fn NtQueryAttributesFile(
+    ObjectAttributes: *OBJECT_ATTRIBUTES,
+    FileAttributes: *FILE_BASIC_INFORMATION,
+) callconv(.Stdcall) NTSTATUS;
+
 pub extern "NtDll" fn NtCreateFile(
     FileHandle: *HANDLE,
     DesiredAccess: ACCESS_MASK,
lib/std/fs.zig
@@ -818,6 +818,13 @@ pub const Dir = struct {
     ) File.OpenError!File {
         const w = os.windows;
 
+        if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
+            return error.IsDir;
+        }
+        if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
+            return error.IsDir;
+        }
+
         var result = File{
             .handle = undefined,
             .io_mode = .blocking,
@@ -839,12 +846,6 @@ pub const Dir = struct {
             .SecurityDescriptor = null,
             .SecurityQualityOfService = null,
         };
-        if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
-            return error.IsDir;
-        }
-        if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
-            return error.IsDir;
-        }
         var io: w.IO_STATUS_BLOCK = undefined;
         const rc = w.ntdll.NtCreateFile(
             &result.handle,
@@ -1332,12 +1333,20 @@ pub const Dir = struct {
     /// For example, instead of testing if a file exists and then opening it, just
     /// open it and handle the error for file not found.
     pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
+        if (builtin.os == .windows) {
+            const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
+            return self.accessW(&sub_path_w, flags);
+        }
         const path_c = try os.toPosixPath(sub_path);
         return self.accessZ(&path_c, flags);
     }
 
     /// Same as `access` except the path parameter is null-terminated.
     pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
+        if (builtin.os == .windows) {
+            const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path);
+            return self.accessW(&sub_path_w, flags);
+        }
         const os_mode = if (flags.write and flags.read)
             @as(u32, os.R_OK | os.W_OK)
         else if (flags.write)
@@ -1351,9 +1360,13 @@ pub const Dir = struct {
         return result;
     }
 
-    /// Same as `access` except the parameter is null-terminated UTF16LE-encoded.
-    pub fn accessW(self: Dir, sub_path: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
-        return os.faccessatW(self.fd, sub_path, 0, 0);
+    /// Same as `access` except asserts the target OS is Windows and the path parameter is
+    /// * WTF-16 encoded
+    /// * null-terminated
+    /// * NtDll prefixed
+    /// TODO currently this ignores `flags`.
+    pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
+        return os.faccessatW(self.fd, sub_path_w, 0, 0);
     }
 };
 
lib/std/os.zig
@@ -2553,10 +2553,42 @@ pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) Acces
 }
 
 /// Same as `faccessat` except asserts the target is Windows and the path parameter
-/// is null-terminated WTF-16 encoded.
+/// is NtDll-prefixed, null-terminated, WTF-16 encoded.
 /// TODO currently this ignores `mode` and `flags`
-pub fn faccessatW(dirfd: fd_t, path: [*:0]const u16, mode: u32, flags: u32) AccessError!void {
-    @compileError("TODO implement faccessatW on Windows");
+pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32) AccessError!void {
+    if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
+        return;
+    }
+    if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
+        return;
+    }
+
+    const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) {
+        error.Overflow => return error.NameTooLong,
+    };
+    var nt_name = windows.UNICODE_STRING{
+        .Length = path_len_bytes,
+        .MaximumLength = path_len_bytes,
+        .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
+    };
+    var attr = windows.OBJECT_ATTRIBUTES{
+        .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
+        .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
+        .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
+        .ObjectName = &nt_name,
+        .SecurityDescriptor = null,
+        .SecurityQualityOfService = null,
+    };
+    var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
+    switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
+        .SUCCESS => return,
+        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
+        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
+        .INVALID_PARAMETER => unreachable,
+        .ACCESS_DENIED => return error.PermissionDenied,
+        .OBJECT_PATH_SYNTAX_BAD => unreachable,
+        else => |rc| return windows.unexpectedStatus(rc),
+    }
 }
 
 pub const PipeError = error{