Commit 87f632b08a

Andrew Kelley <andrew@ziglang.org>
2019-10-22 01:17:33
fs.Dir.openDir: use empty object name for "." on Windows
1 parent ef67c49
lib/std/os/windows/bits.zig
@@ -872,3 +872,5 @@ pub const CURDIR = extern struct {
     DosPath: UNICODE_STRING,
     Handle: HANDLE,
 };
+
+pub const DUPLICATE_SAME_ACCESS = 2;
\ No newline at end of file
lib/std/os/windows/kernel32.zig
@@ -47,6 +47,8 @@ pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_
 
 pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
 
+pub extern "kernel32" stdcallcc fn DuplicateHandle(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, hTargetProcessHandle: HANDLE, lpTargetHandle: *HANDLE, dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwOptions: DWORD) BOOL;
+
 pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
 
 pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
lib/std/os/windows/status.zig
@@ -8,4 +8,5 @@ pub const INVALID_PARAMETER = 0xC000000D;
 pub const ACCESS_DENIED = 0xC0000022;
 pub const OBJECT_NAME_INVALID = 0xC0000033;
 pub const OBJECT_NAME_NOT_FOUND = 0xC0000034;
+pub const OBJECT_PATH_NOT_FOUND = 0xC000003A;
 pub const OBJECT_PATH_SYNTAX_BAD = 0xC000003B;
lib/std/os/windows.zig
@@ -20,6 +20,8 @@ pub const shell32 = @import("windows/shell32.zig");
 
 pub usingnamespace @import("windows/bits.zig");
 
+pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
+
 /// `builtin` is missing `subsystem` when the subsystem is automatically detected,
 /// so Zig standard library has the subsystem detection logic here. This should generally be
 /// used rather than `builtin.subsystem`.
@@ -898,7 +900,7 @@ pub fn unexpectedError(err: DWORD) std.os.UnexpectedError {
 /// and you get an unexpected status.
 pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {
     if (std.os.unexpected_error_tracing) {
-        std.debug.warn("error.Unexpected NTSTATUS={}\n", status);
+        std.debug.warn("error.Unexpected NTSTATUS=0x{x}\n", status);
         std.debug.dumpCurrentStackTrace(null);
     }
     return error.Unexpected;
lib/std/fs.zig
@@ -590,6 +590,7 @@ pub const Dir = struct {
                         self.end_index = io.Information;
                         switch (rc) {
                             w.STATUS.SUCCESS => {},
+                            w.STATUS.ACCESS_DENIED => return error.AccessDenied,
                             else => return w.unexpectedStatus(rc),
                         }
                     }
@@ -708,7 +709,7 @@ pub const Dir = struct {
 
     /// Call `close` on the result when done.
     pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {
-        std.debug.warn("openDir {}\n", sub_path);
+        // std.debug.warn("openDir {}\n", sub_path);
         if (os.windows.is_the_target) {
             const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
             return self.openDirW(&sub_path_w);
@@ -740,9 +741,26 @@ pub const Dir = struct {
     /// This function is Windows-only.
     pub fn openDirW(self: Dir, sub_path_w: [*]const u16) OpenError!Dir {
         const w = os.windows;
+
         var result = Dir{
             .fd = undefined,
         };
+
+        const desired_access = w.GENERIC_READ | w.SYNCHRONIZE;
+
+        // if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
+        //     // Windows gives me STATUS_OBJECT_NAME_INVALID with "." as the object name.
+
+        //     if (w.kernel32.DuplicateHandle(w.self_process_handle, self.fd, w.self_process_handle, &result.fd, desired_access, w.TRUE, 0) == 0) {
+        //         switch (w.kernel32.GetLastError()) {
+        //             else => |err| return w.unexpectedError(err),
+        //         }
+
+        //         @panic("handle DuplicateHandle error");
+        //     }
+        //     return result;
+        // }
+
         //var mask: ?[*]const u16 = undefined;
         //var nt_name: w.UNICODE_STRING = undefined;
         //if (w.ntdll.RtlDosPathNameToNtPathName_U(sub_path_w, &nt_name, null, null) == 0) {
@@ -760,7 +778,7 @@ pub const Dir = struct {
         //}
 
         const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2);
-        std.debug.warn("path_len_bytes = {}\n", path_len_bytes);
+        // std.debug.warn("path_len_bytes = {}\n", path_len_bytes);
         var nt_name = w.UNICODE_STRING{
             .Length = path_len_bytes,
             .MaximumLength = path_len_bytes,
@@ -774,7 +792,11 @@ pub const Dir = struct {
             .SecurityDescriptor = null,
             .SecurityQualityOfService = null,
         };
-        std.debug.warn("RootDirectory = {}\n", attr.RootDirectory);
+        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;
+        }
+        // std.debug.warn("RootDirectory = {}\n", attr.RootDirectory);
         var io: w.IO_STATUS_BLOCK = undefined;
         const wide_slice = nt_name.Buffer[0 .. nt_name.Length / 2];
         //const wide_slice2 = std.mem.toSliceConst(u16, mask.?);
@@ -782,11 +804,11 @@ pub const Dir = struct {
         //var buf2: [200]u8 = undefined;
         const len = std.unicode.utf16leToUtf8(&buf, wide_slice) catch unreachable;
         //const len2 = std.unicode.utf16leToUtf8(&buf2, wide_slice2) catch unreachable;
-        std.debug.warn("path: {}\n", buf[0..len]);
+        // std.debug.warn("path: {}\n", buf[0..len]);
         //std.debug.warn("path: {}\nmask: {}\n", buf[0..len], buf2[0..len2]);
         const rc = w.ntdll.NtCreateFile(
             &result.fd,
-            w.GENERIC_READ | w.SYNCHRONIZE,
+            desired_access,
             &attr,
             &io,
             null,
@@ -797,11 +819,12 @@ pub const Dir = struct {
             null,
             0,
         );
-        std.debug.warn("result.fd = {}\n", result.fd);
+        // std.debug.warn("result.fd = {}\n", result.fd);
         switch (rc) {
             w.STATUS.SUCCESS => return result,
             w.STATUS.OBJECT_NAME_INVALID => @panic("openDirW invalid object name"),
             w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
+            w.STATUS.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
             w.STATUS.INVALID_PARAMETER => {
                 @panic("invalid parameter");
             },
CMakeLists.txt
@@ -630,5 +630,8 @@ set_target_properties(zig PROPERTIES
     LINK_FLAGS ${EXE_LDFLAGS}
 )
 target_link_libraries(zig compiler "${LIBUSERLAND}")
+if(MSVC)
+  target_link_libraries(zig ntdll.lib)
+endif()
 add_dependencies(zig zig_build_libuserland)
 install(TARGETS zig DESTINATION bin)