Commit b561d5f3fe

Andrew Kelley <andrew@ziglang.org>
2025-10-20 06:27:16
std.Io.Threaded: implement dirCreateFile for Windows
1 parent aa6e8ef
Changed files (5)
lib/std/fs/Dir.zig
@@ -900,64 +900,14 @@ pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File
     return file;
 }
 
-/// Creates, opens, or overwrites a file with write access.
-/// Call `File.close` on the result when done.
-/// Asserts that the path parameter has no null bytes.
-/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `sub_path` should be encoded as valid UTF-8.
-/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
+/// Deprecated in favor of `Io.Dir.createFile`.
 pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
-    if (native_os == .windows) {
-        const path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
-        return self.createFileW(path_w.span(), flags);
-    }
     var threaded: Io.Threaded = .init_single_threaded;
     const io = threaded.io();
     const new_file = try Io.Dir.createFile(self.adaptToNewApi(), io, sub_path, flags);
     return .adaptFromNewApi(new_file);
 }
 
-/// Same as `createFile` but Windows-only and the path parameter is
-/// [WTF-16](https://wtf-8.codeberg.page/#potentially-ill-formed-utf-16) encoded.
-pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags) File.OpenError!File {
-    const w = windows;
-    const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0;
-    const file: File = .{
-        .handle = try w.OpenFile(sub_path_w, .{
-            .dir = self.fd,
-            .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | read_flag,
-            .creation = if (flags.exclusive)
-                @as(u32, w.FILE_CREATE)
-            else if (flags.truncate)
-                @as(u32, w.FILE_OVERWRITE_IF)
-            else
-                @as(u32, w.FILE_OPEN_IF),
-        }),
-    };
-    errdefer file.close();
-    var io: w.IO_STATUS_BLOCK = undefined;
-    const range_off: w.LARGE_INTEGER = 0;
-    const range_len: w.LARGE_INTEGER = 1;
-    const exclusive = switch (flags.lock) {
-        .none => return file,
-        .shared => false,
-        .exclusive => true,
-    };
-    try w.LockFile(
-        file.handle,
-        null,
-        null,
-        null,
-        &io,
-        &range_off,
-        &range_len,
-        null,
-        @intFromBool(flags.lock_nonblocking),
-        @intFromBool(exclusive),
-    );
-    return file;
-}
-
 /// Deprecated in favor of `Io.Dir.MakeError`.
 pub const MakeError = Io.Dir.MakeError;
 
lib/std/Io/Dir.zig
@@ -101,6 +101,13 @@ pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) F
     return io.vtable.dirOpenFile(io.userdata, dir, sub_path, flags);
 }
 
+/// Creates, opens, or overwrites a file with write access.
+///
+/// Allocates a resource to be dellocated with `File.close`.
+///
+/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
+/// On WASI, `sub_path` should be encoded as valid UTF-8.
+/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
 pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
     return io.vtable.dirCreateFile(io.userdata, dir, sub_path, flags);
 }
lib/std/Io/Threaded.zig
@@ -187,7 +187,7 @@ pub fn io(t: *Threaded) Io {
                 else => dirAccessPosix,
             },
             .dirCreateFile = switch (builtin.os.tag) {
-                .windows => @panic("TODO"),
+                .windows => dirCreateFileWindows,
                 .wasi => dirCreateFileWasi,
                 else => dirCreateFilePosix,
             },
@@ -1483,6 +1483,54 @@ fn dirCreateFilePosix(
     return .{ .handle = fd };
 }
 
+fn dirCreateFileWindows(
+    userdata: ?*anyopaque,
+    dir: Io.Dir,
+    sub_path: []const u8,
+    flags: Io.File.CreateFlags,
+) Io.File.OpenError!Io.File {
+    const w = windows;
+    const t: *Threaded = @ptrCast(@alignCast(userdata));
+    try t.checkCancel();
+
+    const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, sub_path);
+    const sub_path_w = sub_path_w_array.span();
+
+    const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0;
+    const handle = try w.OpenFile(sub_path_w, .{
+        .dir = dir.handle,
+        .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | read_flag,
+        .creation = if (flags.exclusive)
+            @as(u32, w.FILE_CREATE)
+        else if (flags.truncate)
+            @as(u32, w.FILE_OVERWRITE_IF)
+        else
+            @as(u32, w.FILE_OPEN_IF),
+    });
+    errdefer w.CloseHandle(handle);
+    var io_status_block: w.IO_STATUS_BLOCK = undefined;
+    const range_off: w.LARGE_INTEGER = 0;
+    const range_len: w.LARGE_INTEGER = 1;
+    const exclusive = switch (flags.lock) {
+        .none => return .{ .handle = handle },
+        .shared => false,
+        .exclusive => true,
+    };
+    try w.LockFile(
+        handle,
+        null,
+        null,
+        null,
+        &io_status_block,
+        &range_off,
+        &range_len,
+        null,
+        @intFromBool(flags.lock_nonblocking),
+        @intFromBool(exclusive),
+    );
+    return .{ .handle = handle };
+}
+
 fn dirCreateFileWasi(
     userdata: ?*anyopaque,
     dir: Io.Dir,
lib/std/fs.zig
@@ -299,12 +299,6 @@ pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) Fi
     return cwd().createFile(absolute_path, flags);
 }
 
-/// Same as `createFileAbsolute` but the path parameter is WTF-16 encoded.
-pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File {
-    assert(path.isAbsoluteWindowsW(absolute_path_w));
-    return cwd().createFileW(mem.span(absolute_path_w), flags);
-}
-
 /// Delete a file name and possibly the file it refers to, based on an absolute path.
 /// Asserts that the path is absolute. See `Dir.deleteFile` for a function that
 /// operates on both absolute and relative paths.
lib/std/posix.zig
@@ -4684,9 +4684,7 @@ pub const AccessError = error{
 /// Windows. See `fs` for the cross-platform file system API.
 pub fn access(path: []const u8, mode: u32) AccessError!void {
     if (native_os == .windows) {
-        const path_w = try windows.sliceToPrefixedFileW(null, path);
-        _ = try windows.GetFileAttributesW(path_w.span().ptr);
-        return;
+        @compileError("use std.Io instead");
     } else if (native_os == .wasi and !builtin.link_libc) {
         @compileError("wasi doesn't support absolute paths");
     }
@@ -4697,9 +4695,7 @@ pub fn access(path: []const u8, mode: u32) AccessError!void {
 /// Same as `access` except `path` is null-terminated.
 pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
     if (native_os == .windows) {
-        const path_w = try windows.cStrToPrefixedFileW(null, path);
-        _ = try windows.GetFileAttributesW(path_w.span().ptr);
-        return;
+        @compileError("use std.Io instead");
     } else if (native_os == .wasi and !builtin.link_libc) {
         return access(mem.sliceTo(path, 0), mode);
     }