Commit 32c998e03c

Lee Cannon <leecannon@leecannon.xyz>
2020-11-07 12:55:55
Switch to RtlSetCurrentDirectory_U
1 parent 80b1e82
Changed files (4)
lib/std/os/windows/kernel32.zig
@@ -93,7 +93,6 @@ pub extern "kernel32" fn FillConsoleOutputCharacterA(hConsoleOutput: HANDLE, cCh
 pub extern "kernel32" fn FillConsoleOutputAttribute(hConsoleOutput: HANDLE, wAttribute: WORD, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfAttrsWritten: LPDWORD) callconv(.Stdcall) BOOL;
 pub extern "kernel32" fn SetConsoleCursorPosition(hConsoleOutput: HANDLE, dwCursorPosition: COORD) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" fn SetCurrentDirectoryW(lpPathName: [*:0]const u16) callconv(.Stdcall) BOOL;
 pub extern "kernel32" fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) callconv(.Stdcall) DWORD;
 
 pub extern "kernel32" fn GetCurrentThread() callconv(.Stdcall) HANDLE;
lib/std/os/windows/ntdll.zig
@@ -111,3 +111,7 @@ pub extern "NtDll" fn NtWaitForKeyedEvent(
     Alertable: BOOLEAN,
     Timeout: ?*LARGE_INTEGER,
 ) callconv(.Stdcall) NTSTATUS;
+
+pub extern "NtDll" fn RtlSetCurrentDirectory_U(
+    PathName: *UNICODE_STRING
+) callconv(.Stdcall) NTSTATUS;
lib/std/os/windows.zig
@@ -556,29 +556,42 @@ pub fn WriteFile(
 }
 
 pub const SetCurrentDirectoryError = error{
-    PathNotFound,
-    InvalidName,
-    InvalidUtf8,
     NameTooLong,
+    InvalidUtf8,
+    FileNotFound,
     NotDir,
+    AccessDenied,
+    NoDevice,
     Unexpected,
 };
 
 pub fn SetCurrentDirectory(path_name: []const u8) SetCurrentDirectoryError!void {
-    // PATH_MAX_WIDE - 1 as we need to ensure there is space for the null terminator
-    if (path_name.len >= PATH_MAX_WIDE - 1) return error.NameTooLong;
+    var path_space: PathSpace = undefined;
+    path_space.len = try std.unicode.utf8ToUtf16Le(path_space.data[0..], path_name);
+    if (path_space.len > path_space.data.len) return error.NameTooLong;
     
-    var utf16le_buf: [PATH_MAX_WIDE]u16 = undefined;
-    const end = try std.unicode.utf8ToUtf16Le(utf16le_buf[0..], path_name);
-    utf16le_buf[end] = 0;
+    const path_len_bytes = math.cast(u16, path_space.len * 2) catch |err| switch (err) {
+        error.Overflow => return error.NameTooLong,
+    };
     
-    if (kernel32.SetCurrentDirectoryW(@ptrCast([*:0]const u16, &utf16le_buf)) == 0) {
-        switch (kernel32.GetLastError()) {
-            .PATH_NOT_FOUND, .FILE_NOT_FOUND => return error.PathNotFound,
-            .DIRECTORY => return error.NotDir,
-            .INVALID_NAME => return error.InvalidName,
-            else => |err| return unexpectedError(err),
-        }
+    var nt_name = UNICODE_STRING{
+        .Length = path_len_bytes,
+        .MaximumLength = path_len_bytes,
+        .Buffer = @intToPtr([*]u16, @ptrToInt(&path_space.data)),
+    };
+    
+    const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name);
+    switch (rc) {
+        .SUCCESS => {},
+        .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,
+        .NOT_A_DIRECTORY => return error.NotDir,
+        else => return unexpectedStatus(rc),
     }
 }
 
lib/std/os.zig
@@ -2297,6 +2297,9 @@ pub const ChangeCurDirError = error{
     FileNotFound,
     SystemResources,
     NotDir,
+    
+    /// On Windows, file paths must be valid Unicode.
+    InvalidUtf8,
 } || UnexpectedError;
 
 /// Changes the current working directory of the calling process.
@@ -2306,9 +2309,12 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
         @compileError("chdir is not supported in WASI");
     } else if (builtin.os.tag == .windows) {
         windows.SetCurrentDirectory(dir_path) catch |err| switch (err) {
-            error.PathNotFound, error.InvalidName, error.InvalidUtf8 => return error.FileNotFound,
+            error.InvalidUtf8 => return error.InvalidUtf8,
             error.NameTooLong => return error.NameTooLong,
+            error.FileNotFound => return error.FileNotFound,
             error.NotDir => return error.NotDir,
+            error.NoDevice => return error.FileSystem,
+            error.AccessDenied => return error.AccessDenied,
             error.Unexpected => return error.Unexpected,
         };
     } else {