| author | |
| committer | |
| log | 753e71e2f57d488c055bdd34e0f591b62fb8a7bd |
| tree | f875a5723cf9daad4205ba8918f0e2d22038ad26 |
| parent | e5454ff780ae4571cfa71a2edb6f4287eb8cf4de |
10 files changed, 841 insertions(+), 981 deletions(-)
lib/std/Build/Watch.zig+6-11| ... | ... | @@ -366,15 +366,7 @@ const Os = switch (builtin.os.tag) { |
| 366 | 366 | .MaximumLength = @intCast(path_len_bytes), |
| 367 | 367 | .Buffer = @constCast(sub_path_w.span().ptr), |
| 368 | 368 | }; |
| 369 | var attr = windows.OBJECT_ATTRIBUTES{ | |
| 370 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | |
| 371 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else root_fd, | |
| 372 | .Attributes = .{}, | |
| 373 | .ObjectName = &nt_name, | |
| 374 | .SecurityDescriptor = null, | |
| 375 | .SecurityQualityOfService = null, | |
| 376 | }; | |
| 377 | var io: windows.IO_STATUS_BLOCK = undefined; | |
| 369 | var iosb: windows.IO_STATUS_BLOCK = undefined; | |
| 378 | 370 | |
| 379 | 371 | switch (windows.ntdll.NtCreateFile( |
| 380 | 372 | &dir_handle, |
| ... | ... | @@ -385,8 +377,11 @@ const Os = switch (builtin.os.tag) { |
| 385 | 377 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| 386 | 378 | .GENERIC = .{ .READ = true }, |
| 387 | 379 | }, |
| 388 | &attr, | |
| 389 | &io, | |
| 380 | &.{ | |
| 381 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else root_fd, | |
| 382 | .ObjectName = &nt_name, | |
| 383 | }, | |
| 384 | &iosb, | |
| 390 | 385 | null, |
| 391 | 386 | .{}, |
| 392 | 387 | .VALID_FLAGS, |
lib/std/Io/Threaded.zig+356-338| ... | ... | @@ -73,6 +73,7 @@ environ: Environ, |
| 73 | 73 | |
| 74 | 74 | null_file: NullFile = .{}, |
| 75 | 75 | random_file: RandomFile = .{}, |
| 76 | pipe_file: PipeFile = .{}, | |
| 76 | 77 | |
| 77 | 78 | csprng: Csprng = .{}, |
| 78 | 79 | |
| ... | ... | @@ -118,7 +119,7 @@ pub const Argv0 = switch (native_os) { |
| 118 | 119 | |
| 119 | 120 | const Environ = struct { |
| 120 | 121 | /// Unmodified data directly from the OS. |
| 121 | process_environ: process.Environ = .empty, | |
| 122 | process_environ: process.Environ, | |
| 122 | 123 | /// Protected by `mutex`. Determines whether the other fields have been |
| 123 | 124 | /// memoized based on `process_environ`. |
| 124 | 125 | initialized: bool = false, |
| ... | ... | @@ -190,6 +191,24 @@ pub const RandomFile = switch (native_os) { |
| 190 | 191 | }, |
| 191 | 192 | }; |
| 192 | 193 | |
| 194 | pub const PipeFile = switch (native_os) { | |
| 195 | .windows => struct { | |
| 196 | handle: ?windows.HANDLE = null, | |
| 197 | ||
| 198 | fn deinit(this: *@This()) void { | |
| 199 | if (this.handle) |handle| { | |
| 200 | windows.CloseHandle(handle); | |
| 201 | this.handle = null; | |
| 202 | } | |
| 203 | } | |
| 204 | }, | |
| 205 | else => struct { | |
| 206 | fn deinit(this: @This()) void { | |
| 207 | _ = this; | |
| 208 | } | |
| 209 | }, | |
| 210 | }; | |
| 211 | ||
| 193 | 212 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { |
| 194 | 213 | unknown = 0, |
| 195 | 214 | _, |
| ... | ... | @@ -1467,7 +1486,9 @@ pub const init_single_threaded: Threaded = .{ |
| 1467 | 1486 | .old_sig_pipe = undefined, |
| 1468 | 1487 | .have_signal_handler = false, |
| 1469 | 1488 | .argv0 = .empty, |
| 1470 | .environ = .{}, | |
| 1489 | .environ = .{ .process_environ = .{ | |
| 1490 | .block = if (process.Environ.Block == process.Environ.GlobalBlock) .global else .empty, | |
| 1491 | } }, | |
| 1471 | 1492 | .worker_threads = .init(null), |
| 1472 | 1493 | .disable_memory_mapping = false, |
| 1473 | 1494 | }; |
| ... | ... | @@ -1502,6 +1523,7 @@ pub fn deinit(t: *Threaded) void { |
| 1502 | 1523 | } |
| 1503 | 1524 | t.null_file.deinit(); |
| 1504 | 1525 | t.random_file.deinit(); |
| 1526 | t.pipe_file.deinit(); | |
| 1505 | 1527 | t.* = undefined; |
| 1506 | 1528 | } |
| 1507 | 1529 | |
| ... | ... | @@ -1544,14 +1566,7 @@ fn worker(t: *Threaded) void { |
| 1544 | 1566 | }, |
| 1545 | 1567 | }, |
| 1546 | 1568 | }, |
| 1547 | &.{ | |
| 1548 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | |
| 1549 | .RootDirectory = null, | |
| 1550 | .ObjectName = null, | |
| 1551 | .Attributes = .{}, | |
| 1552 | .SecurityDescriptor = null, | |
| 1553 | .SecurityQualityOfService = null, | |
| 1554 | }, | |
| 1569 | &.{ .ObjectName = null }, | |
| 1555 | 1570 | &windows.teb().ClientId, |
| 1556 | 1571 | ) == .SUCCESS); |
| 1557 | 1572 | } |
| ... | ... | @@ -3288,12 +3303,8 @@ fn dirCreateDirPathOpenWindows( |
| 3288 | 3303 | }, |
| 3289 | 3304 | }, |
| 3290 | 3305 | &.{ |
| 3291 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | |
| 3292 | 3306 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 3293 | .Attributes = .{}, | |
| 3294 | 3307 | .ObjectName = &nt_name, |
| 3295 | .SecurityDescriptor = null, | |
| 3296 | .SecurityQualityOfService = null, | |
| 3297 | 3308 | }, |
| 3298 | 3309 | &io_status_block, |
| 3299 | 3310 | null, |
| ... | ... | @@ -3975,13 +3986,9 @@ fn dirAccessWindows( |
| 3975 | 3986 | .MaximumLength = path_len_bytes, |
| 3976 | 3987 | .Buffer = @constCast(sub_path_w.ptr), |
| 3977 | 3988 | }; |
| 3978 | var attr: windows.OBJECT_ATTRIBUTES = .{ | |
| 3979 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | |
| 3989 | const attr: windows.OBJECT_ATTRIBUTES = .{ | |
| 3980 | 3990 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 3981 | .Attributes = .{}, | |
| 3982 | 3991 | .ObjectName = &nt_name, |
| 3983 | .SecurityDescriptor = null, | |
| 3984 | .SecurityQualityOfService = null, | |
| 3985 | 3992 | }; |
| 3986 | 3993 | var basic_info: windows.FILE.BASIC_INFORMATION = undefined; |
| 3987 | 3994 | const syscall: Syscall = try .start(); |
| ... | ... | @@ -4197,14 +4204,8 @@ fn dirCreateFileWindows( |
| 4197 | 4204 | .Buffer = @constCast(sub_path_w.ptr), |
| 4198 | 4205 | }; |
| 4199 | 4206 | const attr: windows.OBJECT_ATTRIBUTES = .{ |
| 4200 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | |
| 4201 | 4207 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 4202 | .Attributes = .{ | |
| 4203 | .INHERIT = false, | |
| 4204 | }, | |
| 4205 | 4208 | .ObjectName = &nt_name, |
| 4206 | .SecurityDescriptor = null, | |
| 4207 | .SecurityQualityOfService = null, | |
| 4208 | 4209 | }; |
| 4209 | 4210 | const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive) |
| 4210 | 4211 | .CREATE |
| ... | ... | @@ -4811,17 +4812,6 @@ pub fn dirOpenFileWtf16( |
| 4811 | 4812 | .MaximumLength = path_len_bytes, |
| 4812 | 4813 | .Buffer = @constCast(sub_path_w.ptr), |
| 4813 | 4814 | }; |
| 4814 | var attr: w.OBJECT_ATTRIBUTES = .{ | |
| 4815 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | |
| 4816 | .RootDirectory = dir_handle, | |
| 4817 | .Attributes = .{ | |
| 4818 | // TODO should we set INHERIT=false? | |
| 4819 | //.INHERIT = false, | |
| 4820 | }, | |
| 4821 | .ObjectName = &nt_name, | |
| 4822 | .SecurityDescriptor = null, | |
| 4823 | .SecurityQualityOfService = null, | |
| 4824 | }; | |
| 4825 | 4815 | var io_status_block: w.IO_STATUS_BLOCK = undefined; |
| 4826 | 4816 | |
| 4827 | 4817 | // There are multiple kernel bugs being worked around with retries. |
| ... | ... | @@ -4840,7 +4830,10 @@ pub fn dirOpenFileWtf16( |
| 4840 | 4830 | .WRITE = flags.isWrite(), |
| 4841 | 4831 | }, |
| 4842 | 4832 | }, |
| 4843 | &attr, | |
| 4833 | &.{ | |
| 4834 | .RootDirectory = dir_handle, | |
| 4835 | .ObjectName = &nt_name, | |
| 4836 | }, | |
| 4844 | 4837 | &io_status_block, |
| 4845 | 4838 | null, |
| 4846 | 4839 | .{ .NORMAL = true }, |
| ... | ... | @@ -5202,12 +5195,8 @@ pub fn dirOpenDirWindows( |
| 5202 | 5195 | }, |
| 5203 | 5196 | }, |
| 5204 | 5197 | &.{ |
| 5205 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | |
| 5206 | 5198 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 5207 | .Attributes = .{}, | |
| 5208 | 5199 | .ObjectName = &nt_name, |
| 5209 | .SecurityDescriptor = null, | |
| 5210 | .SecurityQualityOfService = null, | |
| 5211 | 5200 | }, |
| 5212 | 5201 | &io_status_block, |
| 5213 | 5202 | null, |
| ... | ... | @@ -6417,12 +6406,8 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov |
| 6417 | 6406 | .SYNCHRONIZE = true, |
| 6418 | 6407 | } }, |
| 6419 | 6408 | &.{ |
| 6420 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | |
| 6421 | 6409 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 6422 | .Attributes = .{}, | |
| 6423 | 6410 | .ObjectName = &nt_name, |
| 6424 | .SecurityDescriptor = null, | |
| 6425 | .SecurityQualityOfService = null, | |
| 6426 | 6411 | }, |
| 6427 | 6412 | &io_status_block, |
| 6428 | 6413 | null, |
| ... | ... | @@ -7242,14 +7227,8 @@ fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLink |
| 7242 | 7227 | .Buffer = @constCast(sub_path_w.ptr), |
| 7243 | 7228 | }; |
| 7244 | 7229 | const attr: windows.OBJECT_ATTRIBUTES = .{ |
| 7245 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | |
| 7246 | 7230 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 7247 | .Attributes = .{ | |
| 7248 | .INHERIT = false, | |
| 7249 | }, | |
| 7250 | 7231 | .ObjectName = &nt_name, |
| 7251 | .SecurityDescriptor = null, | |
| 7252 | .SecurityQualityOfService = null, | |
| 7253 | 7232 | }; |
| 7254 | 7233 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 7255 | 7234 | var result_handle: windows.HANDLE = undefined; |
| ... | ... | @@ -7800,24 +7779,19 @@ fn fileSyncWindows(userdata: ?*anyopaque, file: File) File.SyncError!void { |
| 7800 | 7779 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 7801 | 7780 | _ = t; |
| 7802 | 7781 | |
| 7782 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | |
| 7803 | 7783 | const syscall: Syscall = try .start(); |
| 7804 | 7784 | while (true) { |
| 7805 | if (windows.kernel32.FlushFileBuffers(file.handle) != 0) { | |
| 7806 | return syscall.finish(); | |
| 7807 | } | |
| 7808 | switch (windows.GetLastError()) { | |
| 7809 | .SUCCESS => unreachable, // `FlushFileBuffers` returned nonzero | |
| 7810 | .INVALID_HANDLE => unreachable, | |
| 7811 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), // a sync was performed but the system couldn't update the access time | |
| 7812 | .UNEXP_NET_ERR => return syscall.fail(error.InputOutput), | |
| 7813 | .OPERATION_ABORTED => { | |
| 7785 | switch (windows.ntdll.NtFlushBuffersFile(file.handle, &io_status_block)) { | |
| 7786 | .SUCCESS => break syscall.finish(), | |
| 7787 | .CANCELLED => { | |
| 7814 | 7788 | try syscall.checkCancel(); |
| 7815 | 7789 | continue; |
| 7816 | 7790 | }, |
| 7817 | else => |err| { | |
| 7818 | syscall.finish(); | |
| 7819 | return windows.unexpectedError(err); | |
| 7820 | }, | |
| 7791 | .INVALID_HANDLE => unreachable, | |
| 7792 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), // a sync was performed but the system couldn't update the access time | |
| 7793 | .UNEXPECTED_NETWORK_ERROR => return syscall.fail(error.InputOutput), | |
| 7794 | else => |status| return syscall.unexpectedNtstatus(status), | |
| 7821 | 7795 | } |
| 7822 | 7796 | } |
| 7823 | 7797 | } |
| ... | ... | @@ -14364,15 +14338,15 @@ fn scanEnviron(t: *Threaded) void { |
| 14364 | 14338 | comptime assert(@sizeOf(Environ.String) == 0); |
| 14365 | 14339 | } |
| 14366 | 14340 | } else { |
| 14367 | for (t.environ.process_environ.block) |opt_line| { | |
| 14368 | const line = opt_line.?; | |
| 14369 | var line_i: usize = 0; | |
| 14370 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | |
| 14371 | const key = line[0..line_i]; | |
| 14341 | for (t.environ.process_environ.block.slice) |opt_entry| { | |
| 14342 | const entry = opt_entry.?; | |
| 14343 | var entry_i: usize = 0; | |
| 14344 | while (entry[entry_i] != 0 and entry[entry_i] != '=') : (entry_i += 1) {} | |
| 14345 | const key = entry[0..entry_i]; | |
| 14372 | 14346 | |
| 14373 | var end_i: usize = line_i; | |
| 14374 | while (line[end_i] != 0) : (end_i += 1) {} | |
| 14375 | const value = line[line_i + 1 .. end_i :0]; | |
| 14347 | var end_i: usize = entry_i; | |
| 14348 | while (entry[end_i] != 0) : (end_i += 1) {} | |
| 14349 | const value = entry[entry_i + 1 .. end_i :0]; | |
| 14376 | 14350 | |
| 14377 | 14351 | if (std.mem.eql(u8, key, "NO_COLOR")) { |
| 14378 | 14352 | t.environ.exist.NO_COLOR = true; |
| ... | ... | @@ -14402,19 +14376,17 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces |
| 14402 | 14376 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); |
| 14403 | 14377 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; |
| 14404 | 14378 | |
| 14405 | const envp: [*:null]const ?[*:0]const u8 = m: { | |
| 14379 | const env_block = env_block: { | |
| 14406 | 14380 | const prog_fd: i32 = -1; |
| 14407 | if (options.environ_map) |environ_map| { | |
| 14408 | break :m (try environ_map.createBlockPosix(arena, .{ | |
| 14409 | .zig_progress_fd = prog_fd, | |
| 14410 | })).ptr; | |
| 14411 | } | |
| 14412 | break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{ | |
| 14381 | if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{ | |
| 14382 | .zig_progress_fd = prog_fd, | |
| 14383 | }); | |
| 14384 | break :env_block try t.environ.process_environ.createPosixBlock(arena, .{ | |
| 14413 | 14385 | .zig_progress_fd = prog_fd, |
| 14414 | })).ptr; | |
| 14386 | }); | |
| 14415 | 14387 | }; |
| 14416 | 14388 | |
| 14417 | return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); | |
| 14389 | return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); | |
| 14418 | 14390 | } |
| 14419 | 14391 | |
| 14420 | 14392 | fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError { |
| ... | ... | @@ -14513,16 +14485,14 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14513 | 14485 | const prog_fileno = 3; |
| 14514 | 14486 | comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno); |
| 14515 | 14487 | |
| 14516 | const envp: [*:null]const ?[*:0]const u8 = m: { | |
| 14488 | const env_block = env_block: { | |
| 14517 | 14489 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; |
| 14518 | if (options.environ_map) |environ_map| { | |
| 14519 | break :m (try environ_map.createBlockPosix(arena, .{ | |
| 14520 | .zig_progress_fd = prog_fd, | |
| 14521 | })).ptr; | |
| 14522 | } | |
| 14523 | break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{ | |
| 14490 | if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{ | |
| 14491 | .zig_progress_fd = prog_fd, | |
| 14492 | }); | |
| 14493 | break :env_block try t.environ.process_environ.createPosixBlock(arena, .{ | |
| 14524 | 14494 | .zig_progress_fd = prog_fd, |
| 14525 | })).ptr; | |
| 14495 | }); | |
| 14526 | 14496 | }; |
| 14527 | 14497 | |
| 14528 | 14498 | // This pipe communicates to the parent errors in the child between `fork` and `execvpe`. |
| ... | ... | @@ -14601,7 +14571,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14601 | 14571 | } |
| 14602 | 14572 | } |
| 14603 | 14573 | |
| 14604 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); | |
| 14574 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); | |
| 14605 | 14575 | forkBail(ep1, err); |
| 14606 | 14576 | } |
| 14607 | 14577 | |
| ... | ... | @@ -14615,7 +14585,6 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14615 | 14585 | if (options.stderr == .pipe) posix.close(stderr_pipe[1]); |
| 14616 | 14586 | |
| 14617 | 14587 | if (prog_pipe[1] != -1) posix.close(prog_pipe[1]); |
| 14618 | ||
| 14619 | 14588 | options.progress_node.setIpcFd(prog_pipe[0]); |
| 14620 | 14589 | |
| 14621 | 14590 | return .{ |
| ... | ... | @@ -14739,42 +14708,44 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT |
| 14739 | 14708 | // some rare edge cases where our process handle no longer has the |
| 14740 | 14709 | // PROCESS_TERMINATE access right, so let's do another check to make |
| 14741 | 14710 | // sure the process is really no longer running: |
| 14742 | windows.WaitForSingleObjectEx(handle, 0, false) catch return error.AccessDenied; | |
| 14743 | return error.AlreadyTerminated; | |
| 14711 | const minimal_timeout: windows.LARGE_INTEGER = -1; | |
| 14712 | switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) { | |
| 14713 | .SUCCESS => return error.AlreadyTerminated, | |
| 14714 | else => return error.AccessDenied, | |
| 14715 | } | |
| 14744 | 14716 | }, |
| 14745 | 14717 | else => |err| return windows.unexpectedError(err), |
| 14746 | 14718 | } |
| 14747 | 14719 | } |
| 14748 | _ = windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE); | |
| 14720 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); | |
| 14721 | _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout); | |
| 14749 | 14722 | childCleanupWindows(child); |
| 14750 | 14723 | } |
| 14751 | 14724 | |
| 14752 | 14725 | fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { |
| 14753 | 14726 | const handle = child.id.?; |
| 14754 | 14727 | |
| 14755 | const syscall: Syscall = try .start(); | |
| 14756 | while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) { | |
| 14757 | windows.WAIT_OBJECT_0 => break syscall.finish(), | |
| 14758 | windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => { | |
| 14759 | try syscall.checkCancel(); | |
| 14728 | const alertable_syscall: AlertableSyscall = try .start(); | |
| 14729 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); | |
| 14730 | while (true) switch (windows.ntdll.NtWaitForSingleObject(handle, windows.TRUE, &infinite_timeout)) { | |
| 14731 | .WAIT_0 => break alertable_syscall.finish(), | |
| 14732 | .ABANDONED_WAIT_0, .TIMEOUT => { | |
| 14733 | try alertable_syscall.checkCancel(); | |
| 14760 | 14734 | continue; |
| 14761 | 14735 | }, |
| 14762 | windows.WAIT_FAILED => { | |
| 14763 | syscall.finish(); | |
| 14764 | switch (windows.GetLastError()) { | |
| 14765 | else => |err| return windows.unexpectedError(err), | |
| 14766 | } | |
| 14767 | }, | |
| 14768 | else => return syscall.fail(error.Unexpected), | |
| 14736 | else => |status| return alertable_syscall.unexpectedNtstatus(status), | |
| 14769 | 14737 | }; |
| 14770 | 14738 | |
| 14771 | const term: process.Child.Term = x: { | |
| 14772 | var exit_code: windows.DWORD = undefined; | |
| 14773 | if (windows.kernel32.GetExitCodeProcess(handle, &exit_code) == 0) { | |
| 14774 | break :x .{ .unknown = 0 }; | |
| 14775 | } else { | |
| 14776 | break :x .{ .exited = @as(u8, @truncate(exit_code)) }; | |
| 14777 | } | |
| 14739 | var info: windows.PROCESS_BASIC_INFORMATION = undefined; | |
| 14740 | const term: process.Child.Term = switch (windows.ntdll.NtQueryInformationProcess( | |
| 14741 | handle, | |
| 14742 | .BasicInformation, | |
| 14743 | &info, | |
| 14744 | @sizeOf(windows.PROCESS_BASIC_INFORMATION), | |
| 14745 | null, | |
| 14746 | )) { | |
| 14747 | .SUCCESS => .{ .exited = @as(u8, @truncate(@intFromEnum(info.ExitStatus))) }, | |
| 14748 | else => .{ .unknown = 0 }, | |
| 14778 | 14749 | }; |
| 14779 | 14750 | |
| 14780 | 14751 | childCleanupWindows(child); |
| ... | ... | @@ -15037,88 +15008,70 @@ fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32 |
| 15037 | 15008 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| 15038 | 15009 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 15039 | 15010 | |
| 15040 | var saAttr: windows.SECURITY_ATTRIBUTES = .{ | |
| 15041 | .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), | |
| 15042 | .bInheritHandle = windows.TRUE, | |
| 15043 | .lpSecurityDescriptor = null, | |
| 15044 | }; | |
| 15045 | ||
| 15046 | 15011 | const any_ignore = |
| 15047 | 15012 | options.stdin == .ignore or |
| 15048 | 15013 | options.stdout == .ignore or |
| 15049 | 15014 | options.stderr == .ignore; |
| 15050 | ||
| 15051 | const nul_handle = if (any_ignore) try getNulHandle(t) else undefined; | |
| 15052 | ||
| 15053 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; | |
| 15054 | var g_hChildStd_IN_Wr: ?windows.HANDLE = null; | |
| 15055 | switch (options.stdin) { | |
| 15056 | .pipe => { | |
| 15057 | try windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr); | |
| 15058 | }, | |
| 15059 | .ignore => { | |
| 15060 | g_hChildStd_IN_Rd = nul_handle; | |
| 15061 | }, | |
| 15062 | .inherit => { | |
| 15063 | g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch null; | |
| 15064 | }, | |
| 15065 | .close => { | |
| 15066 | g_hChildStd_IN_Rd = null; | |
| 15067 | }, | |
| 15068 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | |
| 15069 | } | |
| 15070 | errdefer if (options.stdin == .pipe) { | |
| 15071 | windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); | |
| 15072 | }; | |
| 15073 | ||
| 15074 | var g_hChildStd_OUT_Rd: ?windows.HANDLE = null; | |
| 15075 | var g_hChildStd_OUT_Wr: ?windows.HANDLE = null; | |
| 15076 | switch (options.stdout) { | |
| 15077 | .pipe => { | |
| 15078 | try windowsMakeAsyncPipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr); | |
| 15079 | }, | |
| 15080 | .ignore => { | |
| 15081 | g_hChildStd_OUT_Wr = nul_handle; | |
| 15082 | }, | |
| 15083 | .inherit => { | |
| 15084 | g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch null; | |
| 15085 | }, | |
| 15086 | .close => { | |
| 15087 | g_hChildStd_OUT_Wr = null; | |
| 15088 | }, | |
| 15089 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | |
| 15090 | } | |
| 15091 | errdefer if (options.stdout == .pipe) { | |
| 15092 | windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); | |
| 15093 | }; | |
| 15094 | ||
| 15095 | var g_hChildStd_ERR_Rd: ?windows.HANDLE = null; | |
| 15096 | var g_hChildStd_ERR_Wr: ?windows.HANDLE = null; | |
| 15097 | switch (options.stderr) { | |
| 15098 | .pipe => { | |
| 15099 | try windowsMakeAsyncPipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr); | |
| 15100 | }, | |
| 15101 | .ignore => { | |
| 15102 | g_hChildStd_ERR_Wr = nul_handle; | |
| 15103 | }, | |
| 15104 | .inherit => { | |
| 15105 | g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null; | |
| 15106 | }, | |
| 15107 | .close => { | |
| 15108 | g_hChildStd_ERR_Wr = null; | |
| 15109 | }, | |
| 15110 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | |
| 15111 | } | |
| 15112 | errdefer if (options.stderr == .pipe) { | |
| 15113 | windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); | |
| 15114 | }; | |
| 15015 | const nul_handle = if (any_ignore) try getNulDevice(t) else undefined; | |
| 15016 | ||
| 15017 | const any_inherit = | |
| 15018 | options.stdin == .inherit or | |
| 15019 | options.stdout == .inherit or | |
| 15020 | options.stderr == .inherit; | |
| 15021 | const peb = if (any_inherit) windows.peb() else undefined; | |
| 15022 | ||
| 15023 | const stdin_pipe = if (options.stdin == .pipe) try t.windowsCreatePipe(.{ | |
| 15024 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, | |
| 15025 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, | |
| 15026 | .outbound = true, | |
| 15027 | }) else undefined; | |
| 15028 | errdefer if (options.stdin == .pipe) for (stdin_pipe) |handle| windows.CloseHandle(handle); | |
| 15029 | ||
| 15030 | const stdout_pipe = if (options.stdout == .pipe) try t.windowsCreatePipe(.{ | |
| 15031 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } }, | |
| 15032 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, | |
| 15033 | .inbound = true, | |
| 15034 | }) else undefined; | |
| 15035 | errdefer if (options.stdout == .pipe) for (stdout_pipe) |handle| windows.CloseHandle(handle); | |
| 15036 | ||
| 15037 | const stderr_pipe = if (options.stderr == .pipe) try t.windowsCreatePipe(.{ | |
| 15038 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } }, | |
| 15039 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, | |
| 15040 | .inbound = true, | |
| 15041 | }) else undefined; | |
| 15042 | errdefer if (options.stderr == .pipe) for (stderr_pipe) |handle| windows.CloseHandle(handle); | |
| 15043 | ||
| 15044 | const prog_pipe = if (options.progress_node.index != .none) try t.windowsCreatePipe(.{ | |
| 15045 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } }, | |
| 15046 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, | |
| 15047 | .inbound = true, | |
| 15048 | }) else undefined; | |
| 15049 | errdefer if (options.progress_node.index != .none) for (prog_pipe) |handle| windows.CloseHandle(handle); | |
| 15115 | 15050 | |
| 15116 | 15051 | var siStartInfo: windows.STARTUPINFOW = .{ |
| 15117 | 15052 | .cb = @sizeOf(windows.STARTUPINFOW), |
| 15118 | .hStdError = g_hChildStd_ERR_Wr, | |
| 15119 | .hStdOutput = g_hChildStd_OUT_Wr, | |
| 15120 | .hStdInput = g_hChildStd_IN_Rd, | |
| 15121 | 15053 | .dwFlags = windows.STARTF_USESTDHANDLES, |
| 15054 | .hStdInput = switch (options.stdin) { | |
| 15055 | .inherit => peb.ProcessParameters.hStdInput, | |
| 15056 | .file => |file| file.handle, | |
| 15057 | .ignore => nul_handle, | |
| 15058 | .pipe => stdin_pipe[1], | |
| 15059 | .close => null, | |
| 15060 | }, | |
| 15061 | .hStdOutput = switch (options.stdout) { | |
| 15062 | .inherit => peb.ProcessParameters.hStdOutput, | |
| 15063 | .file => |file| file.handle, | |
| 15064 | .ignore => nul_handle, | |
| 15065 | .pipe => stdout_pipe[1], | |
| 15066 | .close => null, | |
| 15067 | }, | |
| 15068 | .hStdError = switch (options.stdin) { | |
| 15069 | .inherit => peb.ProcessParameters.hStdError, | |
| 15070 | .file => |file| file.handle, | |
| 15071 | .ignore => nul_handle, | |
| 15072 | .pipe => stderr_pipe[1], | |
| 15073 | .close => null, | |
| 15074 | }, | |
| 15122 | 15075 | |
| 15123 | 15076 | .lpReserved = null, |
| 15124 | 15077 | .lpDesktop = null, |
| ... | ... | @@ -15143,8 +15096,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15143 | 15096 | const cwd_w = if (options.cwd) |cwd| try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd) else null; |
| 15144 | 15097 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; |
| 15145 | 15098 | |
| 15146 | const maybe_envp_buf = if (options.environ_map) |environ_map| try environ_map.createBlockWindows(arena) else null; | |
| 15147 | const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; | |
| 15099 | const env_block = env_block: { | |
| 15100 | const prog_handle = if (options.progress_node.index != .none) | |
| 15101 | prog_pipe[1] | |
| 15102 | else | |
| 15103 | windows.INVALID_HANDLE_VALUE; | |
| 15104 | if (options.environ_map) |environ_map| break :env_block try environ_map.createWindowsBlock(arena, .{ | |
| 15105 | .zig_progress_handle = prog_handle, | |
| 15106 | }); | |
| 15107 | break :env_block try t.environ.process_environ.createWindowsBlock(arena, .{ | |
| 15108 | .zig_progress_handle = if (options.progress_node.index != .none) prog_pipe[1] else windows.INVALID_HANDLE_VALUE, | |
| 15109 | }); | |
| 15110 | }; | |
| 15148 | 15111 | |
| 15149 | 15112 | const app_name_wtf8 = options.argv[0]; |
| 15150 | 15113 | const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8); |
| ... | ... | @@ -15222,7 +15185,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15222 | 15185 | &app_buf, |
| 15223 | 15186 | PATHEXT, |
| 15224 | 15187 | &cmd_line_cache, |
| 15225 | envp_ptr, | |
| 15188 | env_block, | |
| 15226 | 15189 | cwd_w_ptr, |
| 15227 | 15190 | flags, |
| 15228 | 15191 | &siStartInfo, |
| ... | ... | @@ -15257,7 +15220,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15257 | 15220 | &app_buf, |
| 15258 | 15221 | PATHEXT, |
| 15259 | 15222 | &cmd_line_cache, |
| 15260 | envp_ptr, | |
| 15223 | env_block, | |
| 15261 | 15224 | cwd_w_ptr, |
| 15262 | 15225 | flags, |
| 15263 | 15226 | &siStartInfo, |
| ... | ... | @@ -15277,21 +15240,40 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15277 | 15240 | }; |
| 15278 | 15241 | } |
| 15279 | 15242 | |
| 15280 | if (options.stdin == .pipe) windows.CloseHandle(g_hChildStd_IN_Rd.?); | |
| 15281 | if (options.stderr == .pipe) windows.CloseHandle(g_hChildStd_ERR_Wr.?); | |
| 15282 | if (options.stdout == .pipe) windows.CloseHandle(g_hChildStd_OUT_Wr.?); | |
| 15243 | if (options.progress_node.index != .none) { | |
| 15244 | windows.CloseHandle(prog_pipe[1]); | |
| 15245 | options.progress_node.setIpcFd(prog_pipe[0]); | |
| 15246 | } | |
| 15283 | 15247 | |
| 15284 | 15248 | return .{ |
| 15285 | 15249 | .id = piProcInfo.hProcess, |
| 15286 | 15250 | .thread_handle = piProcInfo.hThread, |
| 15287 | .stdin = if (g_hChildStd_IN_Wr) |h| .{ .handle = h, .flags = .{ .nonblocking = false } } else null, | |
| 15288 | .stdout = if (g_hChildStd_OUT_Rd) |h| .{ .handle = h, .flags = .{ .nonblocking = true } } else null, | |
| 15289 | .stderr = if (g_hChildStd_ERR_Rd) |h| .{ .handle = h, .flags = .{ .nonblocking = true } } else null, | |
| 15251 | .stdin = stdin: switch (options.stdin) { | |
| 15252 | .pipe => { | |
| 15253 | windows.CloseHandle(stdin_pipe[1]); | |
| 15254 | break :stdin .{ .handle = stdin_pipe[0], .flags = .{ .nonblocking = false } }; | |
| 15255 | }, | |
| 15256 | else => null, | |
| 15257 | }, | |
| 15258 | .stdout = stdout: switch (options.stdout) { | |
| 15259 | .pipe => { | |
| 15260 | windows.CloseHandle(stdout_pipe[1]); | |
| 15261 | break :stdout .{ .handle = stdout_pipe[0], .flags = .{ .nonblocking = true } }; | |
| 15262 | }, | |
| 15263 | else => null, | |
| 15264 | }, | |
| 15265 | .stderr = stderr: switch (options.stderr) { | |
| 15266 | .pipe => { | |
| 15267 | windows.CloseHandle(stderr_pipe[1]); | |
| 15268 | break :stderr .{ .handle = stderr_pipe[0], .flags = .{ .nonblocking = true } }; | |
| 15269 | }, | |
| 15270 | else => null, | |
| 15271 | }, | |
| 15290 | 15272 | .request_resource_usage_statistics = options.request_resource_usage_statistics, |
| 15291 | 15273 | }; |
| 15292 | 15274 | } |
| 15293 | 15275 | |
| 15294 | fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | |
| 15276 | fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | |
| 15295 | 15277 | { |
| 15296 | 15278 | t.mutex.lock(); |
| 15297 | 15279 | defer t.mutex.unlock(); |
| ... | ... | @@ -15299,12 +15281,6 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15299 | 15281 | } |
| 15300 | 15282 | |
| 15301 | 15283 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' }; |
| 15302 | ||
| 15303 | var nt_name: windows.UNICODE_STRING = .{ | |
| 15304 | .Length = device_path.len * 2, | |
| 15305 | .MaximumLength = 0, | |
| 15306 | .Buffer = @constCast(&device_path), | |
| 15307 | }; | |
| 15308 | 15284 | var fresh_handle: windows.HANDLE = undefined; |
| 15309 | 15285 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 15310 | 15286 | var syscall: Syscall = try .start(); |
| ... | ... | @@ -15315,12 +15291,11 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15315 | 15291 | .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } }, |
| 15316 | 15292 | }, |
| 15317 | 15293 | &.{ |
| 15318 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | |
| 15319 | .RootDirectory = null, | |
| 15320 | .ObjectName = &nt_name, | |
| 15321 | .Attributes = .{}, | |
| 15322 | .SecurityDescriptor = null, | |
| 15323 | .SecurityQualityOfService = null, | |
| 15294 | .ObjectName = @constCast(&windows.UNICODE_STRING{ | |
| 15295 | .Length = @sizeOf(@TypeOf(device_path)), | |
| 15296 | .MaximumLength = 0, | |
| 15297 | .Buffer = @constCast(&device_path), | |
| 15298 | }), | |
| 15324 | 15299 | }, |
| 15325 | 15300 | &io_status_block, |
| 15326 | 15301 | .VALID_FLAGS, |
| ... | ... | @@ -15347,7 +15322,7 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15347 | 15322 | }; |
| 15348 | 15323 | } |
| 15349 | 15324 | |
| 15350 | fn getNulHandle(t: *Threaded) !windows.HANDLE { | |
| 15325 | fn getNulDevice(t: *Threaded) !windows.HANDLE { | |
| 15351 | 15326 | { |
| 15352 | 15327 | t.mutex.lock(); |
| 15353 | 15328 | defer t.mutex.unlock(); |
| ... | ... | @@ -15355,44 +15330,26 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15355 | 15330 | } |
| 15356 | 15331 | |
| 15357 | 15332 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }; |
| 15358 | var nt_name: windows.UNICODE_STRING = .{ | |
| 15359 | .Length = device_path.len * 2, | |
| 15360 | .MaximumLength = 0, | |
| 15361 | .Buffer = @constCast(&device_path), | |
| 15362 | }; | |
| 15363 | const attr: windows.OBJECT_ATTRIBUTES = .{ | |
| 15364 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | |
| 15365 | .RootDirectory = null, | |
| 15366 | .Attributes = .{ | |
| 15367 | .INHERIT = true, | |
| 15368 | }, | |
| 15369 | .ObjectName = &nt_name, | |
| 15370 | .SecurityDescriptor = null, | |
| 15371 | .SecurityQualityOfService = null, | |
| 15372 | }; | |
| 15373 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | |
| 15374 | 15333 | var fresh_handle: windows.HANDLE = undefined; |
| 15334 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | |
| 15375 | 15335 | var syscall: Syscall = try .start(); |
| 15376 | while (true) switch (windows.ntdll.NtCreateFile( | |
| 15336 | while (true) switch (windows.ntdll.NtOpenFile( | |
| 15377 | 15337 | &fresh_handle, |
| 15378 | 15338 | .{ |
| 15379 | 15339 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| 15380 | .GENERIC = .{ .WRITE = true, .READ = true }, | |
| 15340 | .SPECIFIC = .{ .FILE = .{ .READ_DATA = true, .WRITE_DATA = true } }, | |
| 15341 | }, | |
| 15342 | &.{ | |
| 15343 | .Attributes = .{ .INHERIT = true }, | |
| 15344 | .ObjectName = @constCast(&windows.UNICODE_STRING{ | |
| 15345 | .Length = @sizeOf(@TypeOf(device_path)), | |
| 15346 | .MaximumLength = 0, | |
| 15347 | .Buffer = @constCast(&device_path), | |
| 15348 | }), | |
| 15381 | 15349 | }, |
| 15382 | &attr, | |
| 15383 | 15350 | &io_status_block, |
| 15384 | null, | |
| 15385 | .{ .NORMAL = true }, | |
| 15386 | 15351 | .VALID_FLAGS, |
| 15387 | .OPEN, | |
| 15388 | .{ | |
| 15389 | .DIRECTORY_FILE = false, | |
| 15390 | .NON_DIRECTORY_FILE = true, | |
| 15391 | .IO = .SYNCHRONOUS_NONALERT, | |
| 15392 | .OPEN_REPARSE_POINT = false, | |
| 15393 | }, | |
| 15394 | null, | |
| 15395 | 0, | |
| 15352 | .{ .IO = .SYNCHRONOUS_NONALERT }, | |
| 15396 | 15353 | )) { |
| 15397 | 15354 | .SUCCESS => { |
| 15398 | 15355 | syscall.finish(); |
| ... | ... | @@ -15406,18 +15363,64 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15406 | 15363 | return fresh_handle; |
| 15407 | 15364 | } |
| 15408 | 15365 | }, |
| 15409 | .DELETE_PENDING => { | |
| 15410 | // This error means that there *was* a file in this location on | |
| 15411 | // the file system, but it was deleted. However, the OS is not | |
| 15412 | // finished with the deletion operation, and so this CreateFile | |
| 15413 | // call has failed. There is not really a sane way to handle | |
| 15414 | // this other than retrying the creation after the OS finishes | |
| 15415 | // the deletion. | |
| 15416 | syscall.finish(); | |
| 15417 | try parking_sleep.windowsRetrySleep(1); | |
| 15418 | syscall = try .start(); | |
| 15366 | .CANCELLED => { | |
| 15367 | try syscall.checkCancel(); | |
| 15419 | 15368 | continue; |
| 15420 | 15369 | }, |
| 15370 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), | |
| 15371 | .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status), | |
| 15372 | .INVALID_HANDLE => |status| return syscall.ntstatusBug(status), | |
| 15373 | .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName), | |
| 15374 | .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound), | |
| 15375 | .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound), | |
| 15376 | .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice), | |
| 15377 | .SHARING_VIOLATION => return syscall.fail(error.AccessDenied), | |
| 15378 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), | |
| 15379 | .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice), | |
| 15380 | .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir), | |
| 15381 | .NOT_A_DIRECTORY => return syscall.fail(error.NotDir), | |
| 15382 | .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied), | |
| 15383 | else => |status| return syscall.unexpectedNtstatus(status), | |
| 15384 | }; | |
| 15385 | } | |
| 15386 | ||
| 15387 | fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { | |
| 15388 | { | |
| 15389 | t.mutex.lock(); | |
| 15390 | defer t.mutex.unlock(); | |
| 15391 | if (t.pipe_file.handle) |handle| return handle; | |
| 15392 | } | |
| 15393 | ||
| 15394 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' }; | |
| 15395 | var fresh_handle: windows.HANDLE = undefined; | |
| 15396 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | |
| 15397 | var syscall: Syscall = try .start(); | |
| 15398 | while (true) switch (windows.ntdll.NtOpenFile( | |
| 15399 | &fresh_handle, | |
| 15400 | .{ .STANDARD = .{ .SYNCHRONIZE = true } }, | |
| 15401 | &.{ | |
| 15402 | .ObjectName = @constCast(&windows.UNICODE_STRING{ | |
| 15403 | .Length = @sizeOf(@TypeOf(device_path)), | |
| 15404 | .MaximumLength = 0, | |
| 15405 | .Buffer = @constCast(&device_path), | |
| 15406 | }), | |
| 15407 | }, | |
| 15408 | &io_status_block, | |
| 15409 | .VALID_FLAGS, | |
| 15410 | .{ .IO = .SYNCHRONOUS_NONALERT }, | |
| 15411 | )) { | |
| 15412 | .SUCCESS => { | |
| 15413 | syscall.finish(); | |
| 15414 | t.mutex.lock(); // Another thread might have won the race. | |
| 15415 | defer t.mutex.unlock(); | |
| 15416 | if (t.pipe_file.handle) |prev_handle| { | |
| 15417 | windows.CloseHandle(fresh_handle); | |
| 15418 | return prev_handle; | |
| 15419 | } else { | |
| 15420 | t.pipe_file.handle = fresh_handle; | |
| 15421 | return fresh_handle; | |
| 15422 | } | |
| 15423 | }, | |
| 15421 | 15424 | .CANCELLED => { |
| 15422 | 15425 | try syscall.checkCancel(); |
| 15423 | 15426 | continue; |
| ... | ... | @@ -15449,7 +15452,7 @@ fn windowsCreateProcessPathExt( |
| 15449 | 15452 | app_buf: *std.ArrayList(u16), |
| 15450 | 15453 | pathext: [:0]const u16, |
| 15451 | 15454 | cmd_line_cache: *WindowsCommandLineCache, |
| 15452 | envp_ptr: ?[*:0]const u16, | |
| 15455 | env_block: ?process.Environ.WindowsBlock, | |
| 15453 | 15456 | cwd_ptr: ?[*:0]u16, |
| 15454 | 15457 | flags: windows.CreateProcessFlags, |
| 15455 | 15458 | lpStartupInfo: *windows.STARTUPINFOW, |
| ... | ... | @@ -15626,7 +15629,7 @@ fn windowsCreateProcessPathExt( |
| 15626 | 15629 | if (windowsCreateProcess( |
| 15627 | 15630 | app_name_w.ptr, |
| 15628 | 15631 | cmd_line_w.ptr, |
| 15629 | envp_ptr, | |
| 15632 | env_block, | |
| 15630 | 15633 | cwd_ptr, |
| 15631 | 15634 | flags, |
| 15632 | 15635 | lpStartupInfo, |
| ... | ... | @@ -15686,7 +15689,7 @@ fn windowsCreateProcessPathExt( |
| 15686 | 15689 | else |
| 15687 | 15690 | full_app_name; |
| 15688 | 15691 | |
| 15689 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { | |
| 15692 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, env_block, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { | |
| 15690 | 15693 | return; |
| 15691 | 15694 | } else |err| switch (err) { |
| 15692 | 15695 | error.FileNotFound => continue, |
| ... | ... | @@ -15710,7 +15713,7 @@ fn windowsCreateProcessPathExt( |
| 15710 | 15713 | fn windowsCreateProcess( |
| 15711 | 15714 | app_name: [*:0]u16, |
| 15712 | 15715 | cmd_line: [*:0]u16, |
| 15713 | env_ptr: ?[*:0]const u16, | |
| 15716 | env_block: ?process.Environ.WindowsBlock, | |
| 15714 | 15717 | cwd_ptr: ?[*:0]u16, |
| 15715 | 15718 | flags: windows.CreateProcessFlags, |
| 15716 | 15719 | lpStartupInfo: *windows.STARTUPINFOW, |
| ... | ... | @@ -15725,7 +15728,7 @@ fn windowsCreateProcess( |
| 15725 | 15728 | null, |
| 15726 | 15729 | windows.TRUE, |
| 15727 | 15730 | flags, |
| 15728 | env_ptr, | |
| 15731 | if (env_block) |block| block.slice.ptr else null, | |
| 15729 | 15732 | cwd_ptr, |
| 15730 | 15733 | lpStartupInfo, |
| 15731 | 15734 | lpProcessInformation, |
| ... | ... | @@ -16246,11 +16249,11 @@ fn posixExecv( |
| 16246 | 16249 | arg0_expand: process.ArgExpansion, |
| 16247 | 16250 | file: [*:0]const u8, |
| 16248 | 16251 | child_argv: [*:null]?[*:0]const u8, |
| 16249 | envp: [*:null]const ?[*:0]const u8, | |
| 16252 | env_block: process.Environ.PosixBlock, | |
| 16250 | 16253 | PATH: []const u8, |
| 16251 | 16254 | ) process.ReplaceError { |
| 16252 | 16255 | const file_slice = std.mem.sliceTo(file, 0); |
| 16253 | if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, envp); | |
| 16256 | if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, env_block); | |
| 16254 | 16257 | |
| 16255 | 16258 | // Use of PATH_MAX here is valid as the path_buf will be passed |
| 16256 | 16259 | // directly to the operating system in posixExecvPath. |
| ... | ... | @@ -16278,7 +16281,7 @@ fn posixExecv( |
| 16278 | 16281 | .expand => child_argv[0] = full_path, |
| 16279 | 16282 | .no_expand => {}, |
| 16280 | 16283 | } |
| 16281 | err = posixExecvPath(full_path, child_argv, envp); | |
| 16284 | err = posixExecvPath(full_path, child_argv, env_block); | |
| 16282 | 16285 | switch (err) { |
| 16283 | 16286 | error.AccessDenied => seen_eacces = true, |
| 16284 | 16287 | error.FileNotFound, error.NotDir => {}, |
| ... | ... | @@ -16293,10 +16296,10 @@ fn posixExecv( |
| 16293 | 16296 | pub fn posixExecvPath( |
| 16294 | 16297 | path: [*:0]const u8, |
| 16295 | 16298 | child_argv: [*:null]const ?[*:0]const u8, |
| 16296 | envp: [*:null]const ?[*:0]const u8, | |
| 16299 | env_block: process.Environ.PosixBlock, | |
| 16297 | 16300 | ) process.ReplaceError { |
| 16298 | 16301 | try Thread.checkCancel(); |
| 16299 | switch (posix.errno(posix.system.execve(path, child_argv, envp))) { | |
| 16302 | switch (posix.errno(posix.system.execve(path, child_argv, env_block.slice.ptr))) { | |
| 16300 | 16303 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. |
| 16301 | 16304 | .@"2BIG" => return error.SystemResources, |
| 16302 | 16305 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | ... | @@ -16328,85 +16331,100 @@ pub fn posixExecvPath( |
| 16328 | 16331 | } |
| 16329 | 16332 | } |
| 16330 | 16333 | |
| 16331 | fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | |
| 16332 | var rd_h: windows.HANDLE = undefined; | |
| 16333 | var wr_h: windows.HANDLE = undefined; | |
| 16334 | try windows.CreatePipe(&rd_h, &wr_h, sattr); | |
| 16335 | errdefer windowsDestroyPipe(rd_h, wr_h); | |
| 16336 | try windows.SetHandleInformation(wr_h, windows.HANDLE_FLAG_INHERIT, 0); | |
| 16337 | rd.* = rd_h; | |
| 16338 | wr.* = wr_h; | |
| 16339 | } | |
| 16340 | ||
| 16341 | fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { | |
| 16342 | if (rd) |h| posix.close(h); | |
| 16343 | if (wr) |h| posix.close(h); | |
| 16344 | } | |
| 16345 | ||
| 16346 | fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | |
| 16347 | var tmp_bufw: [128]u16 = undefined; | |
| 16348 | ||
| 16349 | // Anonymous pipes are built upon Named pipes. | |
| 16350 | // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe | |
| 16351 | // Asynchronous (overlapped) read and write operations are not supported by anonymous pipes. | |
| 16352 | // https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations | |
| 16353 | const pipe_path = blk: { | |
| 16354 | var tmp_buf: [128]u8 = undefined; | |
| 16355 | // Forge a random path for the pipe. | |
| 16356 | const pipe_path = std.fmt.bufPrintSentinel( | |
| 16357 | &tmp_buf, | |
| 16358 | "\\\\.\\pipe\\zig-childprocess-{d}-{d}", | |
| 16359 | .{ windows.GetCurrentProcessId(), pipe_name_counter.fetchAdd(1, .monotonic) }, | |
| 16360 | 0, | |
| 16361 | ) catch unreachable; | |
| 16362 | const len = std.unicode.wtf8ToWtf16Le(&tmp_bufw, pipe_path) catch unreachable; | |
| 16363 | tmp_bufw[len] = 0; | |
| 16364 | break :blk tmp_bufw[0..len :0]; | |
| 16334 | const PipeOptions = struct { | |
| 16335 | attributes: windows.OBJECT_ATTRIBUTES.ATTRIBUTES, | |
| 16336 | mode: windows.FILE.MODE, | |
| 16337 | }; | |
| 16338 | pub const CreatePipeOptions = struct { | |
| 16339 | server: PipeOptions, | |
| 16340 | client: PipeOptions, | |
| 16341 | inbound: bool = false, | |
| 16342 | outbound: bool = false, | |
| 16343 | maximum_instances: u32 = 1, | |
| 16344 | quota: u32 = 4096, | |
| 16345 | default_timeout: windows.LARGE_INTEGER = -120 * std.time.ns_per_s / 100, | |
| 16346 | }; | |
| 16347 | fn windowsCreatePipe(t: *Threaded, options: CreatePipeOptions) ![2]windows.HANDLE { | |
| 16348 | const named_pipe_device = try t.getNamedPipeDevice(); | |
| 16349 | const server_handle = server_handle: { | |
| 16350 | var handle: windows.HANDLE = undefined; | |
| 16351 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | |
| 16352 | const syscall: Syscall = try .start(); | |
| 16353 | while (true) switch (windows.ntdll.NtCreateNamedPipeFile( | |
| 16354 | &handle, | |
| 16355 | .{ | |
| 16356 | .SPECIFIC = .{ .FILE_PIPE = .{ | |
| 16357 | .READ_DATA = options.inbound, | |
| 16358 | .WRITE_DATA = options.outbound, | |
| 16359 | .WRITE_ATTRIBUTES = true, | |
| 16360 | } }, | |
| 16361 | .STANDARD = .{ .SYNCHRONIZE = true }, | |
| 16362 | }, | |
| 16363 | &.{ | |
| 16364 | .RootDirectory = named_pipe_device, | |
| 16365 | .Attributes = options.server.attributes, | |
| 16366 | }, | |
| 16367 | &io_status_block, | |
| 16368 | .{ .READ = true, .WRITE = true }, | |
| 16369 | .CREATE, | |
| 16370 | options.server.mode, | |
| 16371 | .{ .TYPE = .BYTE_STREAM }, | |
| 16372 | .{ .MODE = .BYTE_STREAM }, | |
| 16373 | .{ .OPERATION = .QUEUE }, | |
| 16374 | options.maximum_instances, | |
| 16375 | if (options.inbound) options.quota else 0, | |
| 16376 | if (options.outbound) options.quota else 0, | |
| 16377 | &options.default_timeout, | |
| 16378 | )) { | |
| 16379 | .SUCCESS => syscall.finish(), | |
| 16380 | .CANCELLED => { | |
| 16381 | try syscall.checkCancel(); | |
| 16382 | continue; | |
| 16383 | }, | |
| 16384 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), | |
| 16385 | .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources), | |
| 16386 | else => |status| return syscall.unexpectedNtstatus(status), | |
| 16387 | }; | |
| 16388 | break :server_handle handle; | |
| 16365 | 16389 | }; |
| 16366 | ||
| 16367 | // Create the read handle that can be used with overlapped IO ops. | |
| 16368 | const read_handle = windows.kernel32.CreateNamedPipeW( | |
| 16369 | pipe_path.ptr, | |
| 16370 | windows.PIPE_ACCESS_INBOUND | windows.FILE_FLAG_OVERLAPPED, | |
| 16371 | windows.PIPE_TYPE_BYTE, | |
| 16372 | 1, | |
| 16373 | 4096, | |
| 16374 | 4096, | |
| 16375 | 0, | |
| 16376 | sattr, | |
| 16377 | ); | |
| 16378 | if (read_handle == windows.INVALID_HANDLE_VALUE) { | |
| 16379 | switch (windows.GetLastError()) { | |
| 16380 | else => |err| return windows.unexpectedError(err), | |
| 16381 | } | |
| 16382 | } | |
| 16383 | errdefer posix.close(read_handle); | |
| 16384 | ||
| 16385 | var sattr_copy = sattr.*; | |
| 16386 | const write_handle = windows.kernel32.CreateFileW( | |
| 16387 | pipe_path.ptr, | |
| 16388 | .{ .GENERIC = .{ .WRITE = true } }, | |
| 16389 | 0, | |
| 16390 | &sattr_copy, | |
| 16391 | windows.OPEN_EXISTING, | |
| 16392 | @bitCast(windows.FILE.ATTRIBUTE{ .NORMAL = true }), | |
| 16393 | null, | |
| 16394 | ); | |
| 16395 | if (write_handle == windows.INVALID_HANDLE_VALUE) { | |
| 16396 | switch (windows.GetLastError()) { | |
| 16397 | else => |err| return windows.unexpectedError(err), | |
| 16398 | } | |
| 16399 | } | |
| 16400 | errdefer posix.close(write_handle); | |
| 16401 | ||
| 16402 | try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0); | |
| 16403 | ||
| 16404 | rd.* = read_handle; | |
| 16405 | wr.* = write_handle; | |
| 16390 | errdefer windows.CloseHandle(server_handle); | |
| 16391 | const client_handle = client_handle: { | |
| 16392 | var handle: windows.HANDLE = undefined; | |
| 16393 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | |
| 16394 | const syscall: Syscall = try .start(); | |
| 16395 | while (true) switch (windows.ntdll.NtOpenFile( | |
| 16396 | &handle, | |
| 16397 | .{ | |
| 16398 | .SPECIFIC = .{ .FILE_PIPE = .{ | |
| 16399 | .READ_DATA = options.outbound, | |
| 16400 | .WRITE_DATA = options.inbound, | |
| 16401 | .WRITE_ATTRIBUTES = true, | |
| 16402 | } }, | |
| 16403 | .STANDARD = .{ .SYNCHRONIZE = true }, | |
| 16404 | }, | |
| 16405 | &.{ | |
| 16406 | .RootDirectory = server_handle, | |
| 16407 | .Attributes = options.client.attributes, | |
| 16408 | }, | |
| 16409 | &io_status_block, | |
| 16410 | .{ .READ = true, .WRITE = true }, | |
| 16411 | options.client.mode, | |
| 16412 | )) { | |
| 16413 | .SUCCESS => syscall.finish(), | |
| 16414 | .CANCELED => { | |
| 16415 | try syscall.checkCancel(); | |
| 16416 | continue; | |
| 16417 | }, | |
| 16418 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), | |
| 16419 | .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources), | |
| 16420 | else => |status| return syscall.unexpectedNtstatus(status), | |
| 16421 | }; | |
| 16422 | break :client_handle handle; | |
| 16423 | }; | |
| 16424 | errdefer windows.CloseHandle(client_handle); | |
| 16425 | return .{ server_handle, client_handle }; | |
| 16406 | 16426 | } |
| 16407 | 16427 | |
| 16408 | var pipe_name_counter = std.atomic.Value(u32).init(1); | |
| 16409 | ||
| 16410 | 16428 | fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { |
| 16411 | 16429 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16412 | 16430 | |
| ... | ... | @@ -16517,7 +16535,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { |
| 16517 | 16535 | // despite the function being documented to always return TRUE |
| 16518 | 16536 | // * reads from "\\Device\\CNG" which then seeds a per-CPU AES CSPRNG |
| 16519 | 16537 | // Therefore, that function is avoided in favor of using the device directly. |
| 16520 | const cng_device = try getCngHandle(t); | |
| 16538 | const cng_device = try getCngDevice(t); | |
| 16521 | 16539 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 16522 | 16540 | var i: usize = 0; |
| 16523 | 16541 | const syscall: Syscall = try .start(); |
lib/std/Progress.zig+20-17| ... | ... | @@ -139,7 +139,7 @@ pub const Node = struct { |
| 139 | 139 | fn setIpcFd(s: *Storage, fd: Io.File.Handle) void { |
| 140 | 140 | const integer: u32 = switch (@typeInfo(Io.File.Handle)) { |
| 141 | 141 | .int => @bitCast(fd), |
| 142 | .pointer => @intFromPtr(fd), | |
| 142 | .pointer => @intCast(@intFromPtr(fd)), | |
| 143 | 143 | else => @compileError("unsupported fd_t of " ++ @typeName(Io.File.Handle)), |
| 144 | 144 | }; |
| 145 | 145 | // `estimated_total_count` max int indicates the special state that |
| ... | ... | @@ -342,10 +342,18 @@ pub const Node = struct { |
| 342 | 342 | /// Posix-only. Used by `std.process.Child`. Thread-safe. |
| 343 | 343 | pub fn setIpcFd(node: Node, fd: Io.File.Handle) void { |
| 344 | 344 | const index = node.index.unwrap() orelse return; |
| 345 | assert(fd >= 0); | |
| 346 | assert(fd != posix.STDOUT_FILENO); | |
| 347 | assert(fd != posix.STDIN_FILENO); | |
| 348 | assert(fd != posix.STDERR_FILENO); | |
| 345 | switch (@typeInfo(Io.File.Handle)) { | |
| 346 | .int => { | |
| 347 | assert(fd >= 0); | |
| 348 | assert(fd != posix.STDOUT_FILENO); | |
| 349 | assert(fd != posix.STDIN_FILENO); | |
| 350 | assert(fd != posix.STDERR_FILENO); | |
| 351 | }, | |
| 352 | .pointer => { | |
| 353 | assert(fd != windows.INVALID_HANDLE_VALUE); | |
| 354 | }, | |
| 355 | else => @compileError("unsupported fd_t of " ++ @typeName(Io.File.Handle)), | |
| 356 | } | |
| 349 | 357 | storageByIndex(index).setIpcFd(fd); |
| 350 | 358 | } |
| 351 | 359 | |
| ... | ... | @@ -477,21 +485,18 @@ pub fn start(io: Io, options: Options) Node { |
| 477 | 485 | global_progress.refresh_rate_ns = @intCast(options.refresh_rate_ns.toNanoseconds()); |
| 478 | 486 | global_progress.initial_delay_ns = @intCast(options.initial_delay_ns.toNanoseconds()); |
| 479 | 487 | |
| 480 | if (noop_impl) | |
| 481 | return Node.none; | |
| 488 | if (noop_impl) return .none; | |
| 482 | 489 | |
| 483 | 490 | global_progress.io = io; |
| 484 | 491 | |
| 485 | 492 | if (io.vtable.progressParentFile(io.userdata)) |ipc_file| { |
| 486 | 493 | global_progress.update_worker = io.concurrent(ipcThreadRun, .{ io, ipc_file }) catch |err| { |
| 487 | 494 | global_progress.start_failure = .{ .spawn_ipc_worker = err }; |
| 488 | return Node.none; | |
| 495 | return .none; | |
| 489 | 496 | }; |
| 490 | 497 | } else |env_err| switch (env_err) { |
| 491 | 498 | error.EnvironmentVariableMissing => { |
| 492 | if (options.disable_printing) { | |
| 493 | return Node.none; | |
| 494 | } | |
| 499 | if (options.disable_printing) return .none; | |
| 495 | 500 | const stderr: Io.File = .stderr(); |
| 496 | 501 | global_progress.terminal = stderr; |
| 497 | 502 | if (stderr.enableAnsiEscapeCodes(io)) |_| { |
| ... | ... | @@ -504,14 +509,12 @@ pub fn start(io: Io, options: Options) Node { |
| 504 | 509 | } else |err| switch (err) { |
| 505 | 510 | error.Canceled => { |
| 506 | 511 | io.recancel(); |
| 507 | return Node.none; | |
| 512 | return .none; | |
| 508 | 513 | }, |
| 509 | 514 | } |
| 510 | 515 | } |
| 511 | 516 | |
| 512 | if (global_progress.terminal_mode == .off) { | |
| 513 | return Node.none; | |
| 514 | } | |
| 517 | if (global_progress.terminal_mode == .off) return .none; | |
| 515 | 518 | |
| 516 | 519 | if (have_sigwinch) { |
| 517 | 520 | const act: posix.Sigaction = .{ |
| ... | ... | @@ -530,12 +533,12 @@ pub fn start(io: Io, options: Options) Node { |
| 530 | 533 | global_progress.update_worker = future; |
| 531 | 534 | } else |err| { |
| 532 | 535 | global_progress.start_failure = .{ .spawn_update_worker = err }; |
| 533 | return Node.none; | |
| 536 | return .none; | |
| 534 | 537 | } |
| 535 | 538 | }, |
| 536 | 539 | else => |e| { |
| 537 | 540 | global_progress.start_failure = .{ .parent_ipc = e }; |
| 538 | return Node.none; | |
| 541 | return .none; | |
| 539 | 542 | }, |
| 540 | 543 | } |
| 541 | 544 |
lib/std/Thread.zig+5-1| ... | ... | @@ -726,7 +726,11 @@ const WindowsThreadImpl = struct { |
| 726 | 726 | } |
| 727 | 727 | |
| 728 | 728 | fn join(self: Impl) void { |
| 729 | windows.WaitForSingleObjectEx(self.thread.thread_handle, windows.INFINITE, false) catch unreachable; | |
| 729 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); | |
| 730 | switch (windows.ntdll.NtWaitForSingleObject(self.thread.thread_handle, windows.FALSE, &infinite_timeout)) { | |
| 731 | .WAIT_0 => {}, | |
| 732 | else => |status| windows.unexpectedStatus(status) catch unreachable, | |
| 733 | } | |
| 730 | 734 | windows.CloseHandle(self.thread.thread_handle); |
| 731 | 735 | assert(self.thread.completion.load(.seq_cst) == .completed); |
| 732 | 736 | self.thread.free(); |
lib/std/os/windows.zig+18-249| ... | ... | @@ -2324,12 +2324,12 @@ pub fn GetProcessHeap() ?*HEAP { |
| 2324 | 2324 | // ref: um/winternl.h |
| 2325 | 2325 | |
| 2326 | 2326 | pub const OBJECT_ATTRIBUTES = extern struct { |
| 2327 | Length: ULONG, | |
| 2328 | RootDirectory: ?HANDLE, | |
| 2329 | ObjectName: ?*UNICODE_STRING, | |
| 2330 | Attributes: ATTRIBUTES, | |
| 2331 | SecurityDescriptor: ?*anyopaque, | |
| 2332 | SecurityQualityOfService: ?*anyopaque, | |
| 2327 | Length: ULONG = @sizeOf(OBJECT_ATTRIBUTES), | |
| 2328 | RootDirectory: ?HANDLE = null, | |
| 2329 | ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty), | |
| 2330 | Attributes: ATTRIBUTES = .{}, | |
| 2331 | SecurityDescriptor: ?*anyopaque = null, | |
| 2332 | SecurityQualityOfService: ?*anyopaque = null, | |
| 2333 | 2333 | |
| 2334 | 2334 | // Valid values for the Attributes field |
| 2335 | 2335 | pub const ATTRIBUTES = packed struct(ULONG) { |
| ... | ... | @@ -2420,14 +2420,10 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 2420 | 2420 | .Buffer = @constCast(sub_path_w.ptr), |
| 2421 | 2421 | }; |
| 2422 | 2422 | const attr: OBJECT_ATTRIBUTES = .{ |
| 2423 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | |
| 2424 | 2423 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir, |
| 2425 | .Attributes = .{ | |
| 2426 | .INHERIT = if (options.sa) |sa| sa.bInheritHandle != FALSE else false, | |
| 2427 | }, | |
| 2424 | .Attributes = .{ .INHERIT = if (options.sa) |sa| sa.bInheritHandle != FALSE else false }, | |
| 2428 | 2425 | .ObjectName = &nt_name, |
| 2429 | 2426 | .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null, |
| 2430 | .SecurityQualityOfService = null, | |
| 2431 | 2427 | }; |
| 2432 | 2428 | var io: IO_STATUS_BLOCK = undefined; |
| 2433 | 2429 | while (true) { |
| ... | ... | @@ -2475,7 +2471,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 2475 | 2471 | // call has failed. There is not really a sane way to handle |
| 2476 | 2472 | // this other than retrying the creation after the OS finishes |
| 2477 | 2473 | // the deletion. |
| 2478 | _ = kernel32.SleepEx(1, TRUE); | |
| 2474 | const delay_one_ms: LARGE_INTEGER = -(std.time.ns_per_ms / 100); | |
| 2475 | _ = ntdll.NtDelayExecution(TRUE, &delay_one_ms); | |
| 2479 | 2476 | continue; |
| 2480 | 2477 | }, |
| 2481 | 2478 | .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference, |
| ... | ... | @@ -2506,151 +2503,6 @@ pub fn GetCurrentThreadId() DWORD { |
| 2506 | 2503 | pub fn GetLastError() Win32Error { |
| 2507 | 2504 | return @enumFromInt(teb().LastErrorValue); |
| 2508 | 2505 | } |
| 2509 | ||
| 2510 | pub const CreatePipeError = error{ Unexpected, SystemResources }; | |
| 2511 | ||
| 2512 | var npfs: ?HANDLE = null; | |
| 2513 | ||
| 2514 | /// A Zig wrapper around `NtCreateNamedPipeFile` and `NtCreateFile` syscalls. | |
| 2515 | /// It implements similar behavior to `CreatePipe` and is meant to serve | |
| 2516 | /// as a direct substitute for that call. | |
| 2517 | pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void { | |
| 2518 | // Up to NT 5.2 (Windows XP/Server 2003), `CreatePipe` would generate a pipe similar to: | |
| 2519 | // | |
| 2520 | // \??\pipe\Win32Pipes.{pid}.{count} | |
| 2521 | // | |
| 2522 | // where `pid` is the process id and count is a incrementing counter. | |
| 2523 | // The implementation was changed after NT 6.0 (Vista) to open a handle to the Named Pipe File System | |
| 2524 | // and use that as the root directory for `NtCreateNamedPipeFile`. | |
| 2525 | // This object is visible under the NPFS but has no filename attached to it. | |
| 2526 | // | |
| 2527 | // This implementation replicates how `CreatePipe` works in modern Windows versions. | |
| 2528 | const opt_dev_handle = @atomicLoad(?HANDLE, &npfs, .seq_cst); | |
| 2529 | const dev_handle = opt_dev_handle orelse blk: { | |
| 2530 | const str = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\NamedPipe\\"); | |
| 2531 | const len: u16 = @truncate(str.len * @sizeOf(u16)); | |
| 2532 | const name: UNICODE_STRING = .{ | |
| 2533 | .Length = len, | |
| 2534 | .MaximumLength = len, | |
| 2535 | .Buffer = @ptrCast(@constCast(str)), | |
| 2536 | }; | |
| 2537 | const attrs: OBJECT_ATTRIBUTES = .{ | |
| 2538 | .ObjectName = @constCast(&name), | |
| 2539 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | |
| 2540 | .RootDirectory = null, | |
| 2541 | .Attributes = .{}, | |
| 2542 | .SecurityDescriptor = null, | |
| 2543 | .SecurityQualityOfService = null, | |
| 2544 | }; | |
| 2545 | ||
| 2546 | var iosb: IO_STATUS_BLOCK = undefined; | |
| 2547 | var handle: HANDLE = undefined; | |
| 2548 | switch (ntdll.NtCreateFile( | |
| 2549 | &handle, | |
| 2550 | .{ | |
| 2551 | .STANDARD = .{ .SYNCHRONIZE = true }, | |
| 2552 | .GENERIC = .{ .READ = true }, | |
| 2553 | }, | |
| 2554 | @constCast(&attrs), | |
| 2555 | &iosb, | |
| 2556 | null, | |
| 2557 | .{}, | |
| 2558 | .VALID_FLAGS, | |
| 2559 | .OPEN, | |
| 2560 | .{ .IO = .SYNCHRONOUS_NONALERT }, | |
| 2561 | null, | |
| 2562 | 0, | |
| 2563 | )) { | |
| 2564 | .SUCCESS => {}, | |
| 2565 | // Judging from the ReactOS sources this is technically possible. | |
| 2566 | .INSUFFICIENT_RESOURCES => return error.SystemResources, | |
| 2567 | .INVALID_PARAMETER => unreachable, | |
| 2568 | else => |e| return unexpectedStatus(e), | |
| 2569 | } | |
| 2570 | if (@cmpxchgStrong(?HANDLE, &npfs, null, handle, .seq_cst, .seq_cst)) |xchg| { | |
| 2571 | CloseHandle(handle); | |
| 2572 | break :blk xchg.?; | |
| 2573 | } else break :blk handle; | |
| 2574 | }; | |
| 2575 | ||
| 2576 | const name: UNICODE_STRING = .{ .Buffer = null, .Length = 0, .MaximumLength = 0 }; | |
| 2577 | var attrs: OBJECT_ATTRIBUTES = .{ | |
| 2578 | .ObjectName = @constCast(&name), | |
| 2579 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | |
| 2580 | .RootDirectory = dev_handle, | |
| 2581 | .Attributes = .{ .INHERIT = sattr.bInheritHandle != FALSE }, | |
| 2582 | .SecurityDescriptor = sattr.lpSecurityDescriptor, | |
| 2583 | .SecurityQualityOfService = null, | |
| 2584 | }; | |
| 2585 | ||
| 2586 | // 120 second relative timeout in 100ns units. | |
| 2587 | const default_timeout: LARGE_INTEGER = (-120 * std.time.ns_per_s) / 100; | |
| 2588 | var iosb: IO_STATUS_BLOCK = undefined; | |
| 2589 | var read: HANDLE = undefined; | |
| 2590 | switch (ntdll.NtCreateNamedPipeFile( | |
| 2591 | &read, | |
| 2592 | .{ | |
| 2593 | .SPECIFIC = .{ .FILE_PIPE = .{ | |
| 2594 | .WRITE_ATTRIBUTES = true, | |
| 2595 | } }, | |
| 2596 | .STANDARD = .{ .SYNCHRONIZE = true }, | |
| 2597 | .GENERIC = .{ .READ = true }, | |
| 2598 | }, | |
| 2599 | &attrs, | |
| 2600 | &iosb, | |
| 2601 | .{ .READ = true, .WRITE = true }, | |
| 2602 | .CREATE, | |
| 2603 | .{ .IO = .SYNCHRONOUS_NONALERT }, | |
| 2604 | .{ .TYPE = .BYTE_STREAM }, | |
| 2605 | .{ .MODE = .BYTE_STREAM }, | |
| 2606 | .{ .OPERATION = .QUEUE }, | |
| 2607 | 1, | |
| 2608 | 4096, | |
| 2609 | 4096, | |
| 2610 | @constCast(&default_timeout), | |
| 2611 | )) { | |
| 2612 | .SUCCESS => {}, | |
| 2613 | .INVALID_PARAMETER => unreachable, | |
| 2614 | .INSUFFICIENT_RESOURCES => return error.SystemResources, | |
| 2615 | else => |e| return unexpectedStatus(e), | |
| 2616 | } | |
| 2617 | errdefer CloseHandle(read); | |
| 2618 | ||
| 2619 | attrs.RootDirectory = read; | |
| 2620 | ||
| 2621 | var write: HANDLE = undefined; | |
| 2622 | switch (ntdll.NtCreateFile( | |
| 2623 | &write, | |
| 2624 | .{ | |
| 2625 | .SPECIFIC = .{ .FILE_PIPE = .{ | |
| 2626 | .READ_ATTRIBUTES = true, | |
| 2627 | } }, | |
| 2628 | .STANDARD = .{ .SYNCHRONIZE = true }, | |
| 2629 | .GENERIC = .{ .WRITE = true }, | |
| 2630 | }, | |
| 2631 | &attrs, | |
| 2632 | &iosb, | |
| 2633 | null, | |
| 2634 | .{}, | |
| 2635 | .VALID_FLAGS, | |
| 2636 | .OPEN, | |
| 2637 | .{ | |
| 2638 | .IO = .SYNCHRONOUS_NONALERT, | |
| 2639 | .NON_DIRECTORY_FILE = true, | |
| 2640 | }, | |
| 2641 | null, | |
| 2642 | 0, | |
| 2643 | )) { | |
| 2644 | .SUCCESS => {}, | |
| 2645 | .INVALID_PARAMETER => unreachable, | |
| 2646 | .INSUFFICIENT_RESOURCES => return error.SystemResources, | |
| 2647 | else => |e| return unexpectedStatus(e), | |
| 2648 | } | |
| 2649 | ||
| 2650 | rd.* = read; | |
| 2651 | wr.* = write; | |
| 2652 | } | |
| 2653 | ||
| 2654 | 2506 | /// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls. |
| 2655 | 2507 | /// It implements similar behavior to `DeviceIoControl` and is meant to serve |
| 2656 | 2508 | /// as a direct substitute for that call. |
| ... | ... | @@ -2707,66 +2559,6 @@ pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWOR |
| 2707 | 2559 | return bytes; |
| 2708 | 2560 | } |
| 2709 | 2561 | |
| 2710 | pub const SetHandleInformationError = error{Unexpected}; | |
| 2711 | ||
| 2712 | pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void { | |
| 2713 | if (kernel32.SetHandleInformation(h, mask, flags) == 0) { | |
| 2714 | switch (GetLastError()) { | |
| 2715 | else => |err| return unexpectedError(err), | |
| 2716 | } | |
| 2717 | } | |
| 2718 | } | |
| 2719 | ||
| 2720 | pub const WaitForSingleObjectError = error{ | |
| 2721 | WaitAbandoned, | |
| 2722 | WaitTimeOut, | |
| 2723 | Unexpected, | |
| 2724 | }; | |
| 2725 | ||
| 2726 | pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void { | |
| 2727 | return WaitForSingleObjectEx(handle, milliseconds, false); | |
| 2728 | } | |
| 2729 | ||
| 2730 | pub fn WaitForSingleObjectEx(handle: HANDLE, milliseconds: DWORD, alertable: bool) WaitForSingleObjectError!void { | |
| 2731 | switch (kernel32.WaitForSingleObjectEx(handle, milliseconds, @intFromBool(alertable))) { | |
| 2732 | WAIT_ABANDONED => return error.WaitAbandoned, | |
| 2733 | WAIT_OBJECT_0 => return, | |
| 2734 | WAIT_TIMEOUT => return error.WaitTimeOut, | |
| 2735 | WAIT_FAILED => switch (GetLastError()) { | |
| 2736 | else => |err| return unexpectedError(err), | |
| 2737 | }, | |
| 2738 | else => return error.Unexpected, | |
| 2739 | } | |
| 2740 | } | |
| 2741 | ||
| 2742 | pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, milliseconds: DWORD, alertable: bool) !u32 { | |
| 2743 | assert(handles.len > 0 and handles.len <= MAXIMUM_WAIT_OBJECTS); | |
| 2744 | const nCount: DWORD = @as(DWORD, @intCast(handles.len)); | |
| 2745 | switch (kernel32.WaitForMultipleObjectsEx( | |
| 2746 | nCount, | |
| 2747 | handles.ptr, | |
| 2748 | @intFromBool(waitAll), | |
| 2749 | milliseconds, | |
| 2750 | @intFromBool(alertable), | |
| 2751 | )) { | |
| 2752 | WAIT_OBJECT_0...WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS => |n| { | |
| 2753 | const handle_index = n - WAIT_OBJECT_0; | |
| 2754 | assert(handle_index < nCount); | |
| 2755 | return handle_index; | |
| 2756 | }, | |
| 2757 | WAIT_ABANDONED_0...WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS => |n| { | |
| 2758 | const handle_index = n - WAIT_ABANDONED_0; | |
| 2759 | assert(handle_index < nCount); | |
| 2760 | return error.WaitAbandoned; | |
| 2761 | }, | |
| 2762 | WAIT_TIMEOUT => return error.WaitTimeOut, | |
| 2763 | WAIT_FAILED => switch (GetLastError()) { | |
| 2764 | else => |err| return unexpectedError(err), | |
| 2765 | }, | |
| 2766 | else => return error.Unexpected, | |
| 2767 | } | |
| 2768 | } | |
| 2769 | ||
| 2770 | 2562 | pub const CreateIoCompletionPortError = error{Unexpected}; |
| 2771 | 2563 | |
| 2772 | 2564 | pub fn CreateIoCompletionPort( |
| ... | ... | @@ -2878,21 +2670,6 @@ pub fn CloseHandle(hObject: HANDLE) void { |
| 2878 | 2670 | assert(ntdll.NtClose(hObject) == .SUCCESS); |
| 2879 | 2671 | } |
| 2880 | 2672 | |
| 2881 | pub const GetStdHandleError = error{ | |
| 2882 | NoStandardHandleAttached, | |
| 2883 | Unexpected, | |
| 2884 | }; | |
| 2885 | ||
| 2886 | pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE { | |
| 2887 | const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached; | |
| 2888 | if (handle == INVALID_HANDLE_VALUE) { | |
| 2889 | switch (GetLastError()) { | |
| 2890 | else => |err| return unexpectedError(err), | |
| 2891 | } | |
| 2892 | } | |
| 2893 | return handle; | |
| 2894 | } | |
| 2895 | ||
| 2896 | 2673 | pub const QueryObjectNameError = error{ |
| 2897 | 2674 | AccessDenied, |
| 2898 | 2675 | InvalidHandle, |
| ... | ... | @@ -3545,6 +3322,12 @@ pub fn nanoSecondsToFileTime(ns: Io.Timestamp) FILETIME { |
| 3545 | 3322 | }; |
| 3546 | 3323 | } |
| 3547 | 3324 | |
| 3325 | /// Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a | |
| 3326 | /// redundant copy of the uppercase data. | |
| 3327 | pub inline fn toUpperWtf16(c: u16) u16 { | |
| 3328 | return (if (builtin.os.tag != .windows or @inComptime()) nls.upcaseW else ntdll.RtlUpcaseUnicodeChar)(c); | |
| 3329 | } | |
| 3330 | ||
| 3548 | 3331 | /// Compares two WTF16 strings using the equivalent functionality of |
| 3549 | 3332 | /// `RtlEqualUnicodeString` (with case insensitive comparison enabled). |
| 3550 | 3333 | /// This function can be called on any target. |
| ... | ... | @@ -3598,19 +3381,12 @@ pub fn eqlIgnoreCaseWtf8(a: []const u8, b: []const u8) bool { |
| 3598 | 3381 | var a_wtf8_it = std.unicode.Wtf8View.initUnchecked(a).iterator(); |
| 3599 | 3382 | var b_wtf8_it = std.unicode.Wtf8View.initUnchecked(b).iterator(); |
| 3600 | 3383 | |
| 3601 | // Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a | |
| 3602 | // redundant copy of the uppercase data. | |
| 3603 | const upcaseImpl = switch (builtin.os.tag) { | |
| 3604 | .windows => if (@inComptime()) nls.upcaseW else ntdll.RtlUpcaseUnicodeChar, | |
| 3605 | else => nls.upcaseW, | |
| 3606 | }; | |
| 3607 | ||
| 3608 | 3384 | while (true) { |
| 3609 | 3385 | const a_cp = a_wtf8_it.nextCodepoint() orelse break; |
| 3610 | 3386 | const b_cp = b_wtf8_it.nextCodepoint() orelse return false; |
| 3611 | 3387 | |
| 3612 | 3388 | if (a_cp <= maxInt(u16) and b_cp <= maxInt(u16)) { |
| 3613 | if (a_cp != b_cp and upcaseImpl(@intCast(a_cp)) != upcaseImpl(@intCast(b_cp))) { | |
| 3389 | if (a_cp != b_cp and toUpperWtf16(@intCast(a_cp)) != toUpperWtf16(@intCast(b_cp))) { | |
| 3614 | 3390 | return false; |
| 3615 | 3391 | } |
| 3616 | 3392 | } else if (a_cp != b_cp) { |
| ... | ... | @@ -4098,15 +3874,6 @@ pub const Win32Error = @import("windows/win32error.zig").Win32Error; |
| 4098 | 3874 | pub const LANG = @import("windows/lang.zig"); |
| 4099 | 3875 | pub const SUBLANG = @import("windows/sublang.zig"); |
| 4100 | 3876 | |
| 4101 | /// The standard input device. Initially, this is the console input buffer, CONIN$. | |
| 4102 | pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; | |
| 4103 | ||
| 4104 | /// The standard output device. Initially, this is the active console screen buffer, CONOUT$. | |
| 4105 | pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; | |
| 4106 | ||
| 4107 | /// The standard error device. Initially, this is the active console screen buffer, CONOUT$. | |
| 4108 | pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; | |
| 4109 | ||
| 4110 | 3877 | pub const BOOL = c_int; |
| 4111 | 3878 | pub const BOOLEAN = BYTE; |
| 4112 | 3879 | pub const BYTE = u8; |
| ... | ... | @@ -5244,6 +5011,8 @@ pub const UNICODE_STRING = extern struct { |
| 5244 | 5011 | Length: c_ushort, |
| 5245 | 5012 | MaximumLength: c_ushort, |
| 5246 | 5013 | Buffer: ?[*]WCHAR, |
| 5014 | ||
| 5015 | pub const empty: UNICODE_STRING = .{ .Length = 0, .MaximumLength = 0, .Buffer = null }; | |
| 5247 | 5016 | }; |
| 5248 | 5017 | |
| 5249 | 5018 | pub const ACTIVATION_CONTEXT_DATA = opaque {}; |
lib/std/os/windows/kernel32.zig-108| ... | ... | @@ -12,8 +12,6 @@ const FILETIME = windows.FILETIME; |
| 12 | 12 | const HANDLE = windows.HANDLE; |
| 13 | 13 | const HANDLER_ROUTINE = windows.HANDLER_ROUTINE; |
| 14 | 14 | const HMODULE = windows.HMODULE; |
| 15 | const INIT_ONCE = windows.INIT_ONCE; | |
| 16 | const INIT_ONCE_FN = windows.INIT_ONCE_FN; | |
| 17 | 15 | const LARGE_INTEGER = windows.LARGE_INTEGER; |
| 18 | 16 | const LPCSTR = windows.LPCSTR; |
| 19 | 17 | const LPCVOID = windows.LPCVOID; |
| ... | ... | @@ -24,7 +22,6 @@ const LPWSTR = windows.LPWSTR; |
| 24 | 22 | const MODULEENTRY32 = windows.MODULEENTRY32; |
| 25 | 23 | const OVERLAPPED = windows.OVERLAPPED; |
| 26 | 24 | const OVERLAPPED_ENTRY = windows.OVERLAPPED_ENTRY; |
| 27 | const PMEMORY_BASIC_INFORMATION = windows.PMEMORY_BASIC_INFORMATION; | |
| 28 | 25 | const PROCESS_INFORMATION = windows.PROCESS_INFORMATION; |
| 29 | 26 | const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; |
| 30 | 27 | const SIZE_T = windows.SIZE_T; |
| ... | ... | @@ -37,7 +34,6 @@ const ULONG = windows.ULONG; |
| 37 | 34 | const ULONG_PTR = windows.ULONG_PTR; |
| 38 | 35 | const va_list = windows.va_list; |
| 39 | 36 | const WCHAR = windows.WCHAR; |
| 40 | const WIN32_FIND_DATAW = windows.WIN32_FIND_DATAW; | |
| 41 | 37 | const Win32Error = windows.Win32Error; |
| 42 | 38 | const WORD = windows.WORD; |
| 43 | 39 | |
| ... | ... | @@ -59,39 +55,6 @@ pub extern "kernel32" fn CancelIo( |
| 59 | 55 | hFile: HANDLE, |
| 60 | 56 | ) callconv(.winapi) BOOL; |
| 61 | 57 | |
| 62 | // TODO: Wrapper around NtCancelIoFileEx. | |
| 63 | pub extern "kernel32" fn CancelIoEx( | |
| 64 | hFile: HANDLE, | |
| 65 | lpOverlapped: ?*OVERLAPPED, | |
| 66 | ) callconv(.winapi) BOOL; | |
| 67 | ||
| 68 | pub extern "kernel32" fn CreateFileW( | |
| 69 | lpFileName: LPCWSTR, | |
| 70 | dwDesiredAccess: ACCESS_MASK, | |
| 71 | dwShareMode: DWORD, | |
| 72 | lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, | |
| 73 | dwCreationDisposition: DWORD, | |
| 74 | dwFlagsAndAttributes: DWORD, | |
| 75 | hTemplateFile: ?HANDLE, | |
| 76 | ) callconv(.winapi) HANDLE; | |
| 77 | ||
| 78 | // TODO A bunch of logic around NtCreateNamedPipe | |
| 79 | pub extern "kernel32" fn CreateNamedPipeW( | |
| 80 | lpName: LPCWSTR, | |
| 81 | dwOpenMode: DWORD, | |
| 82 | dwPipeMode: DWORD, | |
| 83 | nMaxInstances: DWORD, | |
| 84 | nOutBufferSize: DWORD, | |
| 85 | nInBufferSize: DWORD, | |
| 86 | nDefaultTimeOut: DWORD, | |
| 87 | lpSecurityAttributes: ?*const SECURITY_ATTRIBUTES, | |
| 88 | ) callconv(.winapi) HANDLE; | |
| 89 | ||
| 90 | // TODO: Matches `STD_*_HANDLE` to peb().ProcessParameters.Standard* | |
| 91 | pub extern "kernel32" fn GetStdHandle( | |
| 92 | nStdHandle: DWORD, | |
| 93 | ) callconv(.winapi) ?HANDLE; | |
| 94 | ||
| 95 | 58 | // TODO: Wrapper around NtSetInformationFile + `FILE_POSITION_INFORMATION`. |
| 96 | 59 | // `FILE_STANDARD_INFORMATION` is also used if dwMoveMethod is `FILE_END` |
| 97 | 60 | pub extern "kernel32" fn SetFilePointerEx( |
| ... | ... | @@ -117,11 +80,6 @@ pub extern "kernel32" fn WriteFile( |
| 117 | 80 | in_out_lpOverlapped: ?*OVERLAPPED, |
| 118 | 81 | ) callconv(.winapi) BOOL; |
| 119 | 82 | |
| 120 | // TODO: Wrapper around GetStdHandle + NtFlushBuffersFile. | |
| 121 | pub extern "kernel32" fn FlushFileBuffers( | |
| 122 | hFile: HANDLE, | |
| 123 | ) callconv(.winapi) BOOL; | |
| 124 | ||
| 125 | 83 | // TODO: Wrapper around NtSetInformationFile + `FILE_IO_COMPLETION_NOTIFICATION_INFORMATION`. |
| 126 | 84 | pub extern "kernel32" fn SetFileCompletionNotificationModes( |
| 127 | 85 | FileHandle: HANDLE, |
| ... | ... | @@ -143,24 +101,6 @@ pub extern "kernel32" fn GetSystemDirectoryW( |
| 143 | 101 | |
| 144 | 102 | // I/O - Kernel Objects |
| 145 | 103 | |
| 146 | // TODO: Wrapper around GetStdHandle + NtDuplicateObject. | |
| 147 | pub extern "kernel32" fn DuplicateHandle( | |
| 148 | hSourceProcessHandle: HANDLE, | |
| 149 | hSourceHandle: HANDLE, | |
| 150 | hTargetProcessHandle: HANDLE, | |
| 151 | lpTargetHandle: *HANDLE, | |
| 152 | dwDesiredAccess: ACCESS_MASK, | |
| 153 | bInheritHandle: BOOL, | |
| 154 | dwOptions: DWORD, | |
| 155 | ) callconv(.winapi) BOOL; | |
| 156 | ||
| 157 | // TODO: Wrapper around GetStdHandle + NtQueryObject + NtSetInformationObject with .ObjectHandleFlagInformation. | |
| 158 | pub extern "kernel32" fn SetHandleInformation( | |
| 159 | hObject: HANDLE, | |
| 160 | dwMask: DWORD, | |
| 161 | dwFlags: DWORD, | |
| 162 | ) callconv(.winapi) BOOL; | |
| 163 | ||
| 164 | 104 | // TODO: Wrapper around NtRemoveIoCompletion. |
| 165 | 105 | pub extern "kernel32" fn GetQueuedCompletionStatus( |
| 166 | 106 | CompletionPort: HANDLE, |
| ... | ... | @@ -210,37 +150,6 @@ pub extern "kernel32" fn TerminateProcess( |
| 210 | 150 | uExitCode: UINT, |
| 211 | 151 | ) callconv(.winapi) BOOL; |
| 212 | 152 | |
| 213 | // TODO: WaitForSingleObjectEx with bAlertable=false. | |
| 214 | pub extern "kernel32" fn WaitForSingleObject( | |
| 215 | hHandle: HANDLE, | |
| 216 | dwMilliseconds: DWORD, | |
| 217 | ) callconv(.winapi) DWORD; | |
| 218 | ||
| 219 | // TODO: Wrapper for GetStdHandle + NtWaitForSingleObject. | |
| 220 | // Sets up an activation context before calling NtWaitForSingleObject. | |
| 221 | pub extern "kernel32" fn WaitForSingleObjectEx( | |
| 222 | hHandle: HANDLE, | |
| 223 | dwMilliseconds: DWORD, | |
| 224 | bAlertable: BOOL, | |
| 225 | ) callconv(.winapi) DWORD; | |
| 226 | ||
| 227 | // TODO: WaitForMultipleObjectsEx with alertable=false | |
| 228 | pub extern "kernel32" fn WaitForMultipleObjects( | |
| 229 | nCount: DWORD, | |
| 230 | lpHandle: [*]const HANDLE, | |
| 231 | bWaitAll: BOOL, | |
| 232 | dwMilliseconds: DWORD, | |
| 233 | ) callconv(.winapi) DWORD; | |
| 234 | ||
| 235 | // TODO: Wrapper around NtWaitForMultipleObjects. | |
| 236 | pub extern "kernel32" fn WaitForMultipleObjectsEx( | |
| 237 | nCount: DWORD, | |
| 238 | lpHandle: [*]const HANDLE, | |
| 239 | bWaitAll: BOOL, | |
| 240 | dwMilliseconds: DWORD, | |
| 241 | bAlertable: BOOL, | |
| 242 | ) callconv(.winapi) DWORD; | |
| 243 | ||
| 244 | 153 | // Process Management |
| 245 | 154 | |
| 246 | 155 | pub extern "kernel32" fn CreateProcessW( |
| ... | ... | @@ -256,12 +165,6 @@ pub extern "kernel32" fn CreateProcessW( |
| 256 | 165 | lpProcessInformation: *PROCESS_INFORMATION, |
| 257 | 166 | ) callconv(.winapi) BOOL; |
| 258 | 167 | |
| 259 | // TODO: implement via ntdll instead | |
| 260 | pub extern "kernel32" fn SleepEx( | |
| 261 | dwMilliseconds: DWORD, | |
| 262 | bAlertable: BOOL, | |
| 263 | ) callconv(.winapi) DWORD; | |
| 264 | ||
| 265 | 168 | // TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`. |
| 266 | 169 | pub extern "kernel32" fn GetExitCodeProcess( |
| 267 | 170 | hProcess: HANDLE, |
| ... | ... | @@ -436,14 +339,3 @@ pub extern "kernel32" fn FormatMessageW( |
| 436 | 339 | |
| 437 | 340 | // TODO: Getter for teb().LastErrorValue. |
| 438 | 341 | pub extern "kernel32" fn GetLastError() callconv(.winapi) Win32Error; |
| 439 | ||
| 440 | // TODO: Wrapper around RtlSetLastWin32Error. | |
| 441 | pub extern "kernel32" fn SetLastError( | |
| 442 | dwErrCode: Win32Error, | |
| 443 | ) callconv(.winapi) void; | |
| 444 | ||
| 445 | // Everything Else | |
| 446 | ||
| 447 | pub extern "kernel32" fn GetSystemInfo( | |
| 448 | lpSystemInfo: *SYSTEM_INFO, | |
| 449 | ) callconv(.winapi) void; |
lib/std/os/windows/ntdll.zig+9-4| ... | ... | @@ -407,6 +407,11 @@ pub extern "ntdll" fn NtCreateNamedPipeFile( |
| 407 | 407 | DefaultTimeout: ?*const LARGE_INTEGER, |
| 408 | 408 | ) callconv(.winapi) NTSTATUS; |
| 409 | 409 | |
| 410 | pub extern "ntdll" fn NtFlushBuffersFile( | |
| 411 | FileHandle: HANDLE, | |
| 412 | IoStatusBlock: *IO_STATUS_BLOCK, | |
| 413 | ) callconv(.winapi) NTSTATUS; | |
| 414 | ||
| 410 | 415 | pub extern "ntdll" fn NtMapViewOfSection( |
| 411 | 416 | SectionHandle: HANDLE, |
| 412 | 417 | ProcessHandle: HANDLE, |
| ... | ... | @@ -590,7 +595,7 @@ pub extern "ntdll" fn NtOpenThread( |
| 590 | 595 | |
| 591 | 596 | pub extern "ntdll" fn NtCancelSynchronousIoFile( |
| 592 | 597 | ThreadHandle: HANDLE, |
| 593 | RequestToCancel: ?*IO_STATUS_BLOCK, | |
| 598 | IoRequestToCancel: ?*IO_STATUS_BLOCK, | |
| 594 | 599 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 595 | 600 | ) callconv(.winapi) NTSTATUS; |
| 596 | 601 | |
| ... | ... | @@ -606,13 +611,13 @@ pub extern "ntdll" fn NtDelayExecution( |
| 606 | 611 | DelayInterval: *const LARGE_INTEGER, |
| 607 | 612 | ) callconv(.winapi) NTSTATUS; |
| 608 | 613 | |
| 609 | pub extern "ntdll" fn NtCancelIoFileEx( | |
| 614 | pub extern "ntdll" fn NtCancelIoFile( | |
| 610 | 615 | FileHandle: HANDLE, |
| 611 | IoRequestToCancel: *const IO_STATUS_BLOCK, | |
| 612 | 616 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 613 | 617 | ) callconv(.winapi) NTSTATUS; |
| 614 | 618 | |
| 615 | pub extern "ntdll" fn NtCancelIoFile( | |
| 619 | pub extern "ntdll" fn NtCancelIoFileEx( | |
| 616 | 620 | FileHandle: HANDLE, |
| 621 | IoRequestToCancel: *const IO_STATUS_BLOCK, | |
| 617 | 622 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 618 | 623 | ) callconv(.winapi) NTSTATUS; |
lib/std/process/Environ.zig+411-245| ... | ... | @@ -4,7 +4,7 @@ const builtin = @import("builtin"); |
| 4 | 4 | const native_os = builtin.os.tag; |
| 5 | 5 | |
| 6 | 6 | const std = @import("../std.zig"); |
| 7 | const Allocator = std.mem.Allocator; | |
| 7 | const Allocator = mem.Allocator; | |
| 8 | 8 | const assert = std.debug.assert; |
| 9 | 9 | const testing = std.testing; |
| 10 | 10 | const unicode = std.unicode; |
| ... | ... | @@ -14,13 +14,6 @@ const mem = std.mem; |
| 14 | 14 | /// Unmodified, unprocessed data provided by the operating system. |
| 15 | 15 | block: Block, |
| 16 | 16 | |
| 17 | pub const empty: Environ = .{ | |
| 18 | .block = switch (Block) { | |
| 19 | void => {}, | |
| 20 | else => &.{}, | |
| 21 | }, | |
| 22 | }; | |
| 23 | ||
| 24 | 17 | /// On WASI without libc, this is `void` because the environment has to be |
| 25 | 18 | /// queried and heap-allocated at runtime. |
| 26 | 19 | /// |
| ... | ... | @@ -28,13 +21,62 @@ pub const empty: Environ = .{ |
| 28 | 21 | /// is modified, so a long-lived pointer cannot be used. Therefore, on this |
| 29 | 22 | /// operating system `void` is also used. |
| 30 | 23 | pub const Block = switch (native_os) { |
| 31 | .windows => void, | |
| 24 | .windows => GlobalBlock, | |
| 32 | 25 | .wasi => switch (builtin.link_libc) { |
| 33 | false => void, | |
| 34 | true => [:null]const ?[*:0]const u8, | |
| 26 | false => GlobalBlock, | |
| 27 | true => PosixBlock, | |
| 35 | 28 | }, |
| 36 | .freestanding, .other => void, | |
| 37 | else => [:null]const ?[*:0]const u8, | |
| 29 | .freestanding, .other => GlobalBlock, | |
| 30 | else => PosixBlock, | |
| 31 | }; | |
| 32 | ||
| 33 | pub const GlobalBlock = struct { | |
| 34 | pub const global: GlobalBlock = .{}; | |
| 35 | ||
| 36 | pub fn deinit(_: GlobalBlock, _: Allocator) void {} | |
| 37 | }; | |
| 38 | ||
| 39 | pub const PosixBlock = struct { | |
| 40 | slice: [:null]const ?[*:0]const u8, | |
| 41 | ||
| 42 | pub const empty: PosixBlock = .{ .slice = &.{} }; | |
| 43 | ||
| 44 | pub fn deinit(block: PosixBlock, gpa: Allocator) void { | |
| 45 | for (block.slice) |entry| gpa.free(mem.span(entry.?)); | |
| 46 | gpa.free(block.slice); | |
| 47 | } | |
| 48 | ||
| 49 | pub const View = struct { | |
| 50 | slice: []const [*:0]const u8, | |
| 51 | ||
| 52 | pub fn isEmpty(v: View) bool { | |
| 53 | return v.slice.len == 0; | |
| 54 | } | |
| 55 | }; | |
| 56 | pub fn view(block: PosixBlock) View { | |
| 57 | return .{ .slice = @ptrCast(block.slice) }; | |
| 58 | } | |
| 59 | }; | |
| 60 | ||
| 61 | pub const WindowsBlock = struct { | |
| 62 | slice: [:0]const u16, | |
| 63 | ||
| 64 | pub const empty: WindowsBlock = .{ .slice = &.{0} }; | |
| 65 | ||
| 66 | pub fn deinit(block: WindowsBlock, gpa: Allocator) void { | |
| 67 | gpa.free(block.slice); | |
| 68 | } | |
| 69 | ||
| 70 | pub const View = struct { | |
| 71 | ptr: [*:0]const u16, | |
| 72 | ||
| 73 | pub fn isEmpty(v: View) bool { | |
| 74 | return v.ptr[0] == 0; | |
| 75 | } | |
| 76 | }; | |
| 77 | pub fn view(block: WindowsBlock) View { | |
| 78 | return .{ .ptr = block.slice.ptr }; | |
| 79 | } | |
| 38 | 80 | }; |
| 39 | 81 | |
| 40 | 82 | pub const Map = struct { |
| ... | ... | @@ -46,47 +88,64 @@ pub const Map = struct { |
| 46 | 88 | pub const Size = usize; |
| 47 | 89 | |
| 48 | 90 | pub const EnvNameHashContext = struct { |
| 49 | fn upcase(c: u21) u21 { | |
| 50 | if (c <= std.math.maxInt(u16)) | |
| 51 | return std.os.windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c))); | |
| 52 | return c; | |
| 53 | } | |
| 54 | ||
| 55 | 91 | pub fn hash(self: @This(), s: []const u8) u32 { |
| 56 | 92 | _ = self; |
| 57 | if (native_os == .windows) { | |
| 58 | var h = std.hash.Wyhash.init(0); | |
| 59 | var it = unicode.Wtf8View.initUnchecked(s).iterator(); | |
| 60 | while (it.nextCodepoint()) |cp| { | |
| 61 | const cp_upper = upcase(cp); | |
| 62 | h.update(&[_]u8{ | |
| 63 | @as(u8, @intCast((cp_upper >> 16) & 0xff)), | |
| 64 | @as(u8, @intCast((cp_upper >> 8) & 0xff)), | |
| 65 | @as(u8, @intCast((cp_upper >> 0) & 0xff)), | |
| 66 | }); | |
| 67 | } | |
| 68 | return @truncate(h.final()); | |
| 93 | switch (native_os) { | |
| 94 | else => return std.array_hash_map.hashString(s), | |
| 95 | .windows => { | |
| 96 | var h = std.hash.Wyhash.init(0); | |
| 97 | var it = unicode.Wtf8View.initUnchecked(s).iterator(); | |
| 98 | while (it.nextCodepoint()) |cp| { | |
| 99 | const cp_upper = if (std.math.cast(u16, cp)) |wtf16| | |
| 100 | std.os.windows.toUpperWtf16(wtf16) | |
| 101 | else | |
| 102 | cp; | |
| 103 | h.update(&[_]u8{ | |
| 104 | @truncate(cp_upper >> 0), | |
| 105 | @truncate(cp_upper >> 8), | |
| 106 | @truncate(cp_upper >> 16), | |
| 107 | }); | |
| 108 | } | |
| 109 | return @truncate(h.final()); | |
| 110 | }, | |
| 69 | 111 | } |
| 70 | return std.array_hash_map.hashString(s); | |
| 71 | 112 | } |
| 72 | 113 | |
| 73 | 114 | pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool { |
| 74 | 115 | _ = self; |
| 75 | 116 | _ = b_index; |
| 76 | if (native_os == .windows) { | |
| 77 | var it_a = unicode.Wtf8View.initUnchecked(a).iterator(); | |
| 78 | var it_b = unicode.Wtf8View.initUnchecked(b).iterator(); | |
| 79 | while (true) { | |
| 80 | const c_a = it_a.nextCodepoint() orelse break; | |
| 81 | const c_b = it_b.nextCodepoint() orelse return false; | |
| 82 | if (upcase(c_a) != upcase(c_b)) | |
| 83 | return false; | |
| 84 | } | |
| 85 | return if (it_b.nextCodepoint()) |_| false else true; | |
| 86 | } | |
| 87 | return std.array_hash_map.eqlString(a, b); | |
| 117 | return eqlKeys(a, b); | |
| 88 | 118 | } |
| 89 | 119 | }; |
| 120 | fn eqlKeys(a: []const u8, b: []const u8) bool { | |
| 121 | return switch (native_os) { | |
| 122 | else => std.array_hash_map.eqlString(a, b), | |
| 123 | .windows => std.os.windows.eqlIgnoreCaseWtf8(a, b), | |
| 124 | }; | |
| 125 | } | |
| 126 | ||
| 127 | fn validateKey(key: []const u8) bool { | |
| 128 | switch (builtin.mode) { | |
| 129 | .Debug, .ReleaseSafe => {}, | |
| 130 | .ReleaseFast, .ReleaseSmall => return key.len > 0, | |
| 131 | } | |
| 132 | switch (native_os) { | |
| 133 | else => return key.len > 0 and mem.findAny(u8, key, &.{ 0, '=' }) == null, | |
| 134 | .windows => { | |
| 135 | if (!unicode.wtf8ValidateSlice(key)) return false; | |
| 136 | var it = unicode.Wtf8View.initUnchecked(key).iterator(); | |
| 137 | switch (it.nextCodepoint() orelse return false) { | |
| 138 | 0 => return false, | |
| 139 | else => {}, | |
| 140 | } | |
| 141 | while (it.nextCodepoint()) |cp| switch (cp) { | |
| 142 | 0, '=' => return false, | |
| 143 | else => {}, | |
| 144 | }; | |
| 145 | return true; | |
| 146 | }, | |
| 147 | } | |
| 148 | } | |
| 90 | 149 | |
| 91 | 150 | /// Create a Map backed by a specific allocator. |
| 92 | 151 | /// That allocator will be used for both backing allocations |
| ... | ... | @@ -108,21 +167,65 @@ pub const Map = struct { |
| 108 | 167 | self.* = undefined; |
| 109 | 168 | } |
| 110 | 169 | |
| 111 | pub fn keys(m: *const Map) [][]const u8 { | |
| 112 | return m.array_hash_map.keys(); | |
| 170 | pub fn keys(map: *const Map) [][]const u8 { | |
| 171 | return map.array_hash_map.keys(); | |
| 113 | 172 | } |
| 114 | 173 | |
| 115 | pub fn values(m: *const Map) [][]const u8 { | |
| 116 | return m.array_hash_map.values(); | |
| 174 | pub fn values(map: *const Map) [][]const u8 { | |
| 175 | return map.array_hash_map.values(); | |
| 176 | } | |
| 177 | ||
| 178 | pub fn putPosixBlock(map: *Map, view: PosixBlock.View) Allocator.Error!void { | |
| 179 | for (view.slice) |entry| { | |
| 180 | var entry_i: usize = 0; | |
| 181 | while (entry[entry_i] != 0 and entry[entry_i] != '=') : (entry_i += 1) {} | |
| 182 | const key = entry[0..entry_i]; | |
| 183 | ||
| 184 | var end_i: usize = entry_i; | |
| 185 | while (entry[end_i] != 0) : (end_i += 1) {} | |
| 186 | const value = entry[entry_i + 1 .. end_i]; | |
| 187 | ||
| 188 | try map.put(key, value); | |
| 189 | } | |
| 190 | } | |
| 191 | ||
| 192 | pub fn putWindowsBlock(map: *Map, view: WindowsBlock.View) Allocator.Error!void { | |
| 193 | var i: usize = 0; | |
| 194 | while (view.ptr[i] != 0) { | |
| 195 | const key_start = i; | |
| 196 | ||
| 197 | // There are some special environment variables that start with =, | |
| 198 | // so we need a special case to not treat = as a key/value separator | |
| 199 | // if it's the first character. | |
| 200 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | |
| 201 | if (view.ptr[key_start] == '=') i += 1; | |
| 202 | ||
| 203 | while (view.ptr[i] != 0 and view.ptr[i] != '=') : (i += 1) {} | |
| 204 | const key_w = view.ptr[key_start..i]; | |
| 205 | const key = try unicode.wtf16LeToWtf8Alloc(map.allocator, key_w); | |
| 206 | errdefer map.allocator.free(key); | |
| 207 | ||
| 208 | if (view.ptr[i] == '=') i += 1; | |
| 209 | ||
| 210 | const value_start = i; | |
| 211 | while (view.ptr[i] != 0) : (i += 1) {} | |
| 212 | const value_w = view.ptr[value_start..i]; | |
| 213 | const value = try unicode.wtf16LeToWtf8Alloc(map.allocator, value_w); | |
| 214 | errdefer map.allocator.free(value); | |
| 215 | ||
| 216 | i += 1; // skip over null byte | |
| 217 | ||
| 218 | try map.putMove(key, value); | |
| 219 | } | |
| 117 | 220 | } |
| 118 | 221 | |
| 119 | 222 | /// Same as `put` but the key and value become owned by the Map rather |
| 120 | 223 | /// than being copied. |
| 121 | 224 | /// If `putMove` fails, the ownership of key and value does not transfer. |
| 122 | 225 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 123 | pub fn putMove(self: *Map, key: []u8, value: []u8) !void { | |
| 226 | pub fn putMove(self: *Map, key: []u8, value: []u8) Allocator.Error!void { | |
| 227 | assert(validateKey(key)); | |
| 124 | 228 | const gpa = self.allocator; |
| 125 | assert(unicode.wtf8ValidateSlice(key)); | |
| 126 | 229 | const get_or_put = try self.array_hash_map.getOrPut(gpa, key); |
| 127 | 230 | if (get_or_put.found_existing) { |
| 128 | 231 | gpa.free(get_or_put.key_ptr.*); |
| ... | ... | @@ -134,8 +237,8 @@ pub const Map = struct { |
| 134 | 237 | |
| 135 | 238 | /// `key` and `value` are copied into the Map. |
| 136 | 239 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 137 | pub fn put(self: *Map, key: []const u8, value: []const u8) !void { | |
| 138 | assert(unicode.wtf8ValidateSlice(key)); | |
| 240 | pub fn put(self: *Map, key: []const u8, value: []const u8) Allocator.Error!void { | |
| 241 | assert(validateKey(key)); | |
| 139 | 242 | const gpa = self.allocator; |
| 140 | 243 | const value_copy = try gpa.dupe(u8, value); |
| 141 | 244 | errdefer gpa.free(value_copy); |
| ... | ... | @@ -155,7 +258,7 @@ pub const Map = struct { |
| 155 | 258 | /// The returned pointer is invalidated if the map resizes. |
| 156 | 259 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 157 | 260 | pub fn getPtr(self: Map, key: []const u8) ?*[]const u8 { |
| 158 | assert(unicode.wtf8ValidateSlice(key)); | |
| 261 | assert(validateKey(key)); | |
| 159 | 262 | return self.array_hash_map.getPtr(key); |
| 160 | 263 | } |
| 161 | 264 | |
| ... | ... | @@ -164,11 +267,12 @@ pub const Map = struct { |
| 164 | 267 | /// key is removed from the map. |
| 165 | 268 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 166 | 269 | pub fn get(self: Map, key: []const u8) ?[]const u8 { |
| 167 | assert(unicode.wtf8ValidateSlice(key)); | |
| 270 | assert(validateKey(key)); | |
| 168 | 271 | return self.array_hash_map.get(key); |
| 169 | 272 | } |
| 170 | 273 | |
| 171 | 274 | pub fn contains(m: *const Map, key: []const u8) bool { |
| 275 | assert(validateKey(key)); | |
| 172 | 276 | return m.array_hash_map.contains(key); |
| 173 | 277 | } |
| 174 | 278 | |
| ... | ... | @@ -181,7 +285,7 @@ pub const Map = struct { |
| 181 | 285 | /// This invalidates the value returned by get() for this key. |
| 182 | 286 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 183 | 287 | pub fn swapRemove(self: *Map, key: []const u8) bool { |
| 184 | assert(unicode.wtf8ValidateSlice(key)); | |
| 288 | assert(validateKey(key)); | |
| 185 | 289 | const kv = self.array_hash_map.fetchSwapRemove(key) orelse return false; |
| 186 | 290 | const gpa = self.allocator; |
| 187 | 291 | gpa.free(kv.key); |
| ... | ... | @@ -198,7 +302,7 @@ pub const Map = struct { |
| 198 | 302 | /// This invalidates the value returned by get() for this key. |
| 199 | 303 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 200 | 304 | pub fn orderedRemove(self: *Map, key: []const u8) bool { |
| 201 | assert(unicode.wtf8ValidateSlice(key)); | |
| 305 | assert(validateKey(key)); | |
| 202 | 306 | const kv = self.array_hash_map.fetchOrderedRemove(key) orelse return false; |
| 203 | 307 | const gpa = self.allocator; |
| 204 | 308 | gpa.free(kv.key); |
| ... | ... | @@ -233,39 +337,41 @@ pub const Map = struct { |
| 233 | 337 | |
| 234 | 338 | /// Creates a null-delimited environment variable block in the format |
| 235 | 339 | /// expected by POSIX, from a hash map plus options. |
| 236 | pub fn createBlockPosix( | |
| 340 | pub fn createPosixBlock( | |
| 237 | 341 | map: *const Map, |
| 238 | arena: Allocator, | |
| 239 | options: CreateBlockPosixOptions, | |
| 240 | ) Allocator.Error![:null]?[*:0]u8 { | |
| 342 | gpa: Allocator, | |
| 343 | options: CreatePosixBlockOptions, | |
| 344 | ) Allocator.Error!PosixBlock { | |
| 241 | 345 | const ZigProgressAction = enum { nothing, edit, delete, add }; |
| 242 | const zig_progress_action: ZigProgressAction = a: { | |
| 243 | const fd = options.zig_progress_fd orelse break :a .nothing; | |
| 244 | const exists = map.get("ZIG_PROGRESS") != null; | |
| 346 | const zig_progress_action: ZigProgressAction = action: { | |
| 347 | const fd = options.zig_progress_fd orelse break :action .nothing; | |
| 348 | const exists = map.contains("ZIG_PROGRESS"); | |
| 245 | 349 | if (fd >= 0) { |
| 246 | break :a if (exists) .edit else .add; | |
| 350 | break :action if (exists) .edit else .add; | |
| 247 | 351 | } else { |
| 248 | if (exists) break :a .delete; | |
| 352 | if (exists) break :action .delete; | |
| 249 | 353 | } |
| 250 | break :a .nothing; | |
| 354 | break :action .nothing; | |
| 251 | 355 | }; |
| 252 | 356 | |
| 253 | const envp_count: usize = c: { | |
| 254 | var c: usize = map.count(); | |
| 357 | const envp = try gpa.allocSentinel(?[*:0]u8, len: { | |
| 358 | var len: usize = map.count(); | |
| 255 | 359 | switch (zig_progress_action) { |
| 256 | .add => c += 1, | |
| 257 | .delete => c -= 1, | |
| 360 | .add => len += 1, | |
| 361 | .delete => len -= 1, | |
| 258 | 362 | .nothing, .edit => {}, |
| 259 | 363 | } |
| 260 | break :c c; | |
| 261 | }; | |
| 262 | ||
| 263 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | |
| 264 | var i: usize = 0; | |
| 364 | break :len len; | |
| 365 | }, null); | |
| 366 | var envp_len: usize = 0; | |
| 367 | errdefer { | |
| 368 | envp[envp_len] = null; | |
| 369 | PosixBlock.deinit(.{ .slice = envp[0..envp_len :null] }, gpa); | |
| 370 | } | |
| 265 | 371 | |
| 266 | 372 | if (zig_progress_action == .add) { |
| 267 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | |
| 268 | i += 1; | |
| 373 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | |
| 374 | envp_len += 1; | |
| 269 | 375 | } |
| 270 | 376 | |
| 271 | 377 | { |
| ... | ... | @@ -275,63 +381,81 @@ pub const Map = struct { |
| 275 | 381 | .add => unreachable, |
| 276 | 382 | .delete => continue, |
| 277 | 383 | .edit => { |
| 278 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={d}", .{ | |
| 384 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "{s}={d}", .{ | |
| 279 | 385 | pair.key_ptr.*, options.zig_progress_fd.?, |
| 280 | 386 | }, 0); |
| 281 | i += 1; | |
| 387 | envp_len += 1; | |
| 282 | 388 | continue; |
| 283 | 389 | }, |
| 284 | 390 | .nothing => {}, |
| 285 | 391 | }; |
| 286 | 392 | |
| 287 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0); | |
| 288 | i += 1; | |
| 393 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0); | |
| 394 | envp_len += 1; | |
| 289 | 395 | } |
| 290 | 396 | } |
| 291 | 397 | |
| 292 | assert(i == envp_count); | |
| 293 | return envp_buf; | |
| 398 | assert(envp_len == envp.len); | |
| 399 | return .{ .slice = envp }; | |
| 294 | 400 | } |
| 295 | 401 | |
| 296 | 402 | /// Caller owns result. |
| 297 | pub fn createBlockWindows(map: *const Map, gpa: Allocator) error{ OutOfMemory, InvalidWtf8 }![:0]u16 { | |
| 403 | pub fn createWindowsBlock( | |
| 404 | map: *const Map, | |
| 405 | gpa: Allocator, | |
| 406 | options: CreateWindowsBlockOptions, | |
| 407 | ) error{ OutOfMemory, InvalidWtf8 }!WindowsBlock { | |
| 298 | 408 | // count bytes needed |
| 299 | const max_chars_needed = x: { | |
| 300 | // Only need 2 trailing NUL code units for an empty environment | |
| 301 | var max_chars_needed: usize = if (map.count() == 0) 2 else 1; | |
| 409 | const max_chars_needed = max_chars_needed: { | |
| 410 | var max_chars_needed: usize = "\x00".len; | |
| 302 | 411 | var it = map.iterator(); |
| 412 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { | |
| 413 | max_chars_needed += std.fmt.count("ZIG_PROGRESS={d}\x00", .{@intFromPtr(handle)}); | |
| 414 | }; | |
| 303 | 415 | while (it.next()) |pair| { |
| 304 | // +1 for '=' | |
| 305 | // +1 for null byte | |
| 306 | max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2; | |
| 416 | if (options.zig_progress_handle != null and | |
| 417 | eqlKeys(pair.key_ptr.*, "ZIG_PROGRESS")) continue; | |
| 418 | max_chars_needed += pair.key_ptr.len + "=".len + pair.value_ptr.len + "\x00".len; | |
| 307 | 419 | } |
| 308 | break :x max_chars_needed; | |
| 420 | break :max_chars_needed @max("\x00\x00".len, max_chars_needed); | |
| 309 | 421 | }; |
| 310 | const result = try gpa.alloc(u16, max_chars_needed); | |
| 311 | errdefer gpa.free(result); | |
| 422 | const block = try gpa.alloc(u16, max_chars_needed); | |
| 423 | errdefer gpa.free(block); | |
| 312 | 424 | |
| 313 | var it = map.iterator(); | |
| 314 | 425 | var i: usize = 0; |
| 426 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { | |
| 427 | @memcpy( | |
| 428 | block[i..][0.."ZIG_PROGRESS=".len], | |
| 429 | &[_]u16{ 'Z', 'I', 'G', '_', 'P', 'R', 'O', 'G', 'R', 'E', 'S', 'S', '=' }, | |
| 430 | ); | |
| 431 | i += "ZIG_PROGRESS=".len; | |
| 432 | var value_buf: [std.fmt.count("{d}", .{std.math.maxInt(usize)})]u8 = undefined; | |
| 433 | const value = std.fmt.bufPrint(&value_buf, "{d}", .{@intFromPtr(handle)}) catch unreachable; | |
| 434 | for (block[i..][0..value.len], value) |*r, v| r.* = v; | |
| 435 | i += value.len; | |
| 436 | block[i] = 0; | |
| 437 | i += 1; | |
| 438 | }; | |
| 439 | var it = map.iterator(); | |
| 315 | 440 | while (it.next()) |pair| { |
| 316 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.key_ptr.*); | |
| 317 | result[i] = '='; | |
| 441 | i += try unicode.wtf8ToWtf16Le(block[i..], pair.key_ptr.*); | |
| 442 | block[i] = '='; | |
| 318 | 443 | i += 1; |
| 319 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.value_ptr.*); | |
| 320 | result[i] = 0; | |
| 444 | i += try unicode.wtf8ToWtf16Le(block[i..], pair.value_ptr.*); | |
| 445 | block[i] = 0; | |
| 321 | 446 | i += 1; |
| 322 | 447 | } |
| 323 | result[i] = 0; | |
| 324 | i += 1; | |
| 325 | 448 | // An empty environment is a special case that requires a redundant |
| 326 | 449 | // NUL terminator. CreateProcess will read the second code unit even |
| 327 | 450 | // though theoretically the first should be enough to recognize that the |
| 328 | 451 | // environment is empty (see https://nullprogram.com/blog/2023/08/23/) |
| 329 | if (map.count() == 0) { | |
| 330 | result[i] = 0; | |
| 452 | for (0..2) |_| { | |
| 453 | block[i] = 0; | |
| 331 | 454 | i += 1; |
| 332 | } | |
| 333 | const reallocated = try gpa.realloc(result, i); | |
| 334 | return reallocated[0 .. i - 1 :0]; | |
| 455 | if (i >= 2) break; | |
| 456 | } else unreachable; | |
| 457 | const reallocated = try gpa.realloc(block, i); | |
| 458 | return .{ .slice = reallocated[0 .. i - 1 :0] }; | |
| 335 | 459 | } |
| 336 | 460 | }; |
| 337 | 461 | |
| ... | ... | @@ -344,13 +468,14 @@ pub const CreateMapError = error{ |
| 344 | 468 | |
| 345 | 469 | /// Allocates a `Map` and copies environment block into it. |
| 346 | 470 | pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 347 | if (native_os == .windows) | |
| 348 | return createMapWide(std.os.windows.peb().ProcessParameters.Environment, allocator); | |
| 349 | ||
| 350 | var result = Map.init(allocator); | |
| 351 | errdefer result.deinit(); | |
| 352 | ||
| 353 | if (native_os == .wasi and !builtin.link_libc) { | |
| 471 | var map = Map.init(allocator); | |
| 472 | errdefer map.deinit(); | |
| 473 | if (native_os == .windows) { | |
| 474 | const peb = std.os.windows.peb(); | |
| 475 | assert(std.os.windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS); | |
| 476 | defer assert(std.os.windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS); | |
| 477 | try map.putWindowsBlock(.{ .ptr = peb.ProcessParameters.Environment }); | |
| 478 | } else if (native_os == .wasi and !builtin.link_libc) { | |
| 354 | 479 | var environ_count: usize = undefined; |
| 355 | 480 | var environ_buf_size: usize = undefined; |
| 356 | 481 | |
| ... | ... | @@ -360,7 +485,7 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 360 | 485 | } |
| 361 | 486 | |
| 362 | 487 | if (environ_count == 0) { |
| 363 | return result; | |
| 488 | return map; | |
| 364 | 489 | } |
| 365 | 490 | |
| 366 | 491 | const environ = try allocator.alloc([*:0]u8, environ_count); |
| ... | ... | @@ -373,63 +498,9 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 373 | 498 | return posix.unexpectedErrno(environ_get_ret); |
| 374 | 499 | } |
| 375 | 500 | |
| 376 | for (environ) |line| { | |
| 377 | const pair = mem.sliceTo(line, 0); | |
| 378 | var parts = mem.splitScalar(u8, pair, '='); | |
| 379 | const key = parts.first(); | |
| 380 | const value = parts.rest(); | |
| 381 | try result.put(key, value); | |
| 382 | } | |
| 383 | return result; | |
| 384 | } else { | |
| 385 | for (env.block) |opt_line| { | |
| 386 | const line = opt_line.?; | |
| 387 | var line_i: usize = 0; | |
| 388 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | |
| 389 | const key = line[0..line_i]; | |
| 390 | ||
| 391 | var end_i: usize = line_i; | |
| 392 | while (line[end_i] != 0) : (end_i += 1) {} | |
| 393 | const value = line[line_i + 1 .. end_i]; | |
| 394 | ||
| 395 | try result.put(key, value); | |
| 396 | } | |
| 397 | return result; | |
| 398 | } | |
| 399 | } | |
| 400 | ||
| 401 | pub fn createMapWide(ptr: [*:0]u16, gpa: Allocator) CreateMapError!Map { | |
| 402 | var result = Map.init(gpa); | |
| 403 | errdefer result.deinit(); | |
| 404 | ||
| 405 | var i: usize = 0; | |
| 406 | while (ptr[i] != 0) { | |
| 407 | const key_start = i; | |
| 408 | ||
| 409 | // There are some special environment variables that start with =, | |
| 410 | // so we need a special case to not treat = as a key/value separator | |
| 411 | // if it's the first character. | |
| 412 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | |
| 413 | if (ptr[key_start] == '=') i += 1; | |
| 414 | ||
| 415 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | |
| 416 | const key_w = ptr[key_start..i]; | |
| 417 | const key = try unicode.wtf16LeToWtf8Alloc(gpa, key_w); | |
| 418 | errdefer gpa.free(key); | |
| 419 | ||
| 420 | if (ptr[i] == '=') i += 1; | |
| 421 | ||
| 422 | const value_start = i; | |
| 423 | while (ptr[i] != 0) : (i += 1) {} | |
| 424 | const value_w = ptr[value_start..i]; | |
| 425 | const value = try unicode.wtf16LeToWtf8Alloc(gpa, value_w); | |
| 426 | errdefer gpa.free(value); | |
| 427 | ||
| 428 | i += 1; // skip over null byte | |
| 429 | ||
| 430 | try result.putMove(key, value); | |
| 431 | } | |
| 432 | return result; | |
| 501 | try map.putPosixBlock(.{ .slice = environ }); | |
| 502 | } else try map.putPosixBlock(env.block.view()); | |
| 503 | return map; | |
| 433 | 504 | } |
| 434 | 505 | |
| 435 | 506 | pub const ContainsError = error{ |
| ... | ... | @@ -516,16 +587,15 @@ pub inline fn containsUnemptyConstant(environ: Environ, comptime key: []const u8 |
| 516 | 587 | /// * `createMap` |
| 517 | 588 | pub fn getPosix(environ: Environ, key: []const u8) ?[:0]const u8 { |
| 518 | 589 | if (mem.findScalar(u8, key, '=') != null) return null; |
| 519 | for (environ.block) |opt_line| { | |
| 520 | const line = opt_line.?; | |
| 521 | var line_i: usize = 0; | |
| 522 | while (line[line_i] != 0) : (line_i += 1) { | |
| 523 | if (line_i == key.len) break; | |
| 524 | if (line[line_i] != key[line_i]) break; | |
| 590 | for (environ.block.view().slice) |entry| { | |
| 591 | var entry_i: usize = 0; | |
| 592 | while (entry[entry_i] != 0) : (entry_i += 1) { | |
| 593 | if (entry_i == key.len) break; | |
| 594 | if (entry[entry_i] != key[entry_i]) break; | |
| 525 | 595 | } |
| 526 | if ((line_i != key.len) or (line[line_i] != '=')) continue; | |
| 596 | if ((entry_i != key.len) or (entry[entry_i] != '=')) continue; | |
| 527 | 597 | |
| 528 | return mem.sliceTo(line + line_i + 1, 0); | |
| 598 | return mem.sliceTo(entry + entry_i + 1, 0); | |
| 529 | 599 | } |
| 530 | 600 | return null; |
| 531 | 601 | } |
| ... | ... | @@ -548,7 +618,10 @@ pub fn getWindows(environ: Environ, key: [*:0]const u16) ?[:0]const u16 { |
| 548 | 618 | const key_slice = mem.sliceTo(key, 0); |
| 549 | 619 | if (key_slice.len > 0 and mem.findScalar(u16, key_slice[1..], '=') != null) return null; |
| 550 | 620 | |
| 551 | const ptr = std.os.windows.peb().ProcessParameters.Environment; | |
| 621 | const peb = std.os.windows.peb(); | |
| 622 | assert(std.os.windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS); | |
| 623 | defer assert(std.os.windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS); | |
| 624 | const ptr = peb.ProcessParameters.Environment; | |
| 552 | 625 | |
| 553 | 626 | var i: usize = 0; |
| 554 | 627 | while (ptr[i] != 0) { |
| ... | ... | @@ -604,7 +677,7 @@ pub fn getAlloc(environ: Environ, gpa: Allocator, key: []const u8) GetAllocError |
| 604 | 677 | return gpa.dupe(u8, val); |
| 605 | 678 | } |
| 606 | 679 | |
| 607 | pub const CreateBlockPosixOptions = struct { | |
| 680 | pub const CreatePosixBlockOptions = struct { | |
| 608 | 681 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. |
| 609 | 682 | /// If non-null, negative means to remove the environment variable, and >= 0 |
| 610 | 683 | /// means to provide it with the given integer. |
| ... | ... | @@ -613,67 +686,145 @@ pub const CreateBlockPosixOptions = struct { |
| 613 | 686 | |
| 614 | 687 | /// Creates a null-delimited environment variable block in the format expected |
| 615 | 688 | /// by POSIX, from a different one. |
| 616 | pub fn createBlockPosix( | |
| 689 | pub fn createPosixBlock( | |
| 617 | 690 | existing: Environ, |
| 618 | arena: Allocator, | |
| 619 | options: CreateBlockPosixOptions, | |
| 620 | ) Allocator.Error![:null]?[*:0]u8 { | |
| 621 | const contains_zig_progress = for (existing.block) |opt_line| { | |
| 622 | if (mem.eql(u8, mem.sliceTo(opt_line.?, '='), "ZIG_PROGRESS")) break true; | |
| 691 | gpa: Allocator, | |
| 692 | options: CreatePosixBlockOptions, | |
| 693 | ) Allocator.Error!PosixBlock { | |
| 694 | const contains_zig_progress = for (existing.block.view().slice) |entry| { | |
| 695 | if (mem.eql(u8, mem.sliceTo(entry, '='), "ZIG_PROGRESS")) break true; | |
| 623 | 696 | } else false; |
| 624 | 697 | |
| 625 | 698 | const ZigProgressAction = enum { nothing, edit, delete, add }; |
| 626 | const zig_progress_action: ZigProgressAction = a: { | |
| 627 | const fd = options.zig_progress_fd orelse break :a .nothing; | |
| 699 | const zig_progress_action: ZigProgressAction = action: { | |
| 700 | const fd = options.zig_progress_fd orelse break :action .nothing; | |
| 628 | 701 | if (fd >= 0) { |
| 629 | break :a if (contains_zig_progress) .edit else .add; | |
| 702 | break :action if (contains_zig_progress) .edit else .add; | |
| 630 | 703 | } else { |
| 631 | if (contains_zig_progress) break :a .delete; | |
| 704 | if (contains_zig_progress) break :action .delete; | |
| 632 | 705 | } |
| 633 | break :a .nothing; | |
| 706 | break :action .nothing; | |
| 634 | 707 | }; |
| 635 | 708 | |
| 636 | const envp_count: usize = c: { | |
| 637 | var count: usize = existing.block.len; | |
| 709 | const envp = try gpa.allocSentinel(?[*:0]u8, len: { | |
| 710 | var len: usize = existing.block.slice.len; | |
| 638 | 711 | switch (zig_progress_action) { |
| 639 | .add => count += 1, | |
| 640 | .delete => count -= 1, | |
| 712 | .add => len += 1, | |
| 713 | .delete => len -= 1, | |
| 641 | 714 | .nothing, .edit => {}, |
| 642 | 715 | } |
| 643 | break :c count; | |
| 644 | }; | |
| 645 | ||
| 646 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | |
| 647 | var i: usize = 0; | |
| 648 | var existing_index: usize = 0; | |
| 649 | ||
| 716 | break :len len; | |
| 717 | }, null); | |
| 718 | var envp_len: usize = 0; | |
| 719 | errdefer { | |
| 720 | envp[envp_len] = null; | |
| 721 | PosixBlock.deinit(.{ .slice = envp[0..envp_len :null] }, gpa); | |
| 722 | } | |
| 650 | 723 | if (zig_progress_action == .add) { |
| 651 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | |
| 652 | i += 1; | |
| 724 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | |
| 725 | envp_len += 1; | |
| 653 | 726 | } |
| 654 | 727 | |
| 655 | while (existing.block[existing_index]) |line| : (existing_index += 1) { | |
| 656 | if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { | |
| 728 | var existing_index: usize = 0; | |
| 729 | while (existing.block.slice[existing_index]) |entry| : (existing_index += 1) { | |
| 730 | if (mem.eql(u8, mem.sliceTo(entry, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { | |
| 657 | 731 | .add => unreachable, |
| 658 | 732 | .delete => continue, |
| 659 | 733 | .edit => { |
| 660 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | |
| 661 | i += 1; | |
| 734 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | |
| 735 | envp_len += 1; | |
| 662 | 736 | continue; |
| 663 | 737 | }, |
| 664 | 738 | .nothing => {}, |
| 665 | 739 | }; |
| 666 | envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); | |
| 667 | i += 1; | |
| 740 | envp[envp_len] = try gpa.dupeZ(u8, mem.span(entry)); | |
| 741 | envp_len += 1; | |
| 668 | 742 | } |
| 669 | 743 | |
| 670 | assert(i == envp_count); | |
| 671 | return envp_buf; | |
| 744 | assert(envp_len == envp.len); | |
| 745 | return .{ .slice = envp }; | |
| 746 | } | |
| 747 | ||
| 748 | pub const CreateWindowsBlockOptions = struct { | |
| 749 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. | |
| 750 | /// If non-null, `std.os.windows.INVALID_HANDLE_VALUE` means to remove the | |
| 751 | /// environment variable, otherwise provide it with the given handle as an integer. | |
| 752 | zig_progress_handle: ?std.os.windows.HANDLE = null, | |
| 753 | }; | |
| 754 | ||
| 755 | /// Creates a null-delimited environment variable block in the format expected | |
| 756 | /// by POSIX, from a different one. | |
| 757 | pub fn createWindowsBlock( | |
| 758 | existing: Environ, | |
| 759 | gpa: Allocator, | |
| 760 | options: CreateWindowsBlockOptions, | |
| 761 | ) Allocator.Error!WindowsBlock { | |
| 762 | _ = existing; | |
| 763 | const peb = std.os.windows.peb(); | |
| 764 | assert(std.os.windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS); | |
| 765 | defer assert(std.os.windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS); | |
| 766 | const existing_block = peb.ProcessParameters.Environment; | |
| 767 | var ranges: [2]struct { start: usize, end: usize } = undefined; | |
| 768 | var ranges_len: usize = 0; | |
| 769 | ranges[ranges_len].start = 0; | |
| 770 | const zig_progress_key = [_]u16{ 'Z', 'I', 'G', '_', 'P', 'R', 'O', 'G', 'R', 'E', 'S', 'S', '=' }; | |
| 771 | const needed_len = needed_len: { | |
| 772 | var needed_len: usize = "\x00".len; | |
| 773 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { | |
| 774 | needed_len += std.fmt.count("ZIG_PROGRESS={d}\x00", .{@intFromPtr(handle)}); | |
| 775 | }; | |
| 776 | var i: usize = 0; | |
| 777 | while (existing_block[i] != 0) { | |
| 778 | const start = i; | |
| 779 | const entry = mem.sliceTo(existing_block[start..], 0); | |
| 780 | i += entry.len + "\x00".len; | |
| 781 | if (options.zig_progress_handle != null and entry.len >= zig_progress_key.len and | |
| 782 | std.os.windows.eqlIgnoreCaseWtf16(entry[0..zig_progress_key.len], &zig_progress_key)) | |
| 783 | { | |
| 784 | ranges[ranges_len].end = start; | |
| 785 | ranges_len += 1; | |
| 786 | ranges[ranges_len].start = i; | |
| 787 | } else needed_len += entry.len + "\x00".len; | |
| 788 | } | |
| 789 | ranges[ranges_len].end = i; | |
| 790 | ranges_len += 1; | |
| 791 | break :needed_len @max("\x00\x00".len, needed_len); | |
| 792 | }; | |
| 793 | const block = try gpa.alloc(u16, needed_len); | |
| 794 | errdefer gpa.free(block); | |
| 795 | var i: usize = 0; | |
| 796 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { | |
| 797 | @memcpy(block[i..][0..zig_progress_key.len], &zig_progress_key); | |
| 798 | i += zig_progress_key.len; | |
| 799 | var value_buf: [std.fmt.count("{d}", .{std.math.maxInt(usize)})]u8 = undefined; | |
| 800 | const value = std.fmt.bufPrint(&value_buf, "{d}", .{@intFromPtr(handle)}) catch unreachable; | |
| 801 | for (block[i..][0..value.len], value) |*r, v| r.* = v; | |
| 802 | i += value.len; | |
| 803 | block[i] = 0; | |
| 804 | i += 1; | |
| 805 | }; | |
| 806 | for (ranges[0..ranges_len]) |range| { | |
| 807 | const range_len = range.end - range.start; | |
| 808 | @memcpy(block[i..][0..range_len], existing_block[range.start..range.end]); | |
| 809 | i += range_len; | |
| 810 | } | |
| 811 | // An empty environment is a special case that requires a redundant | |
| 812 | // NUL terminator. CreateProcess will read the second code unit even | |
| 813 | // though theoretically the first should be enough to recognize that the | |
| 814 | // environment is empty (see https://nullprogram.com/blog/2023/08/23/) | |
| 815 | for (0..2) |_| { | |
| 816 | block[i] = 0; | |
| 817 | i += 1; | |
| 818 | if (i >= 2) break; | |
| 819 | } else unreachable; | |
| 820 | assert(i == block.len); | |
| 821 | return .{ .slice = block[0 .. i - 1 :0] }; | |
| 672 | 822 | } |
| 673 | 823 | |
| 674 | test "Map.createBlock" { | |
| 675 | const allocator = testing.allocator; | |
| 676 | var envmap = Map.init(allocator); | |
| 824 | test "Map.createPosixBlock" { | |
| 825 | const gpa = testing.allocator; | |
| 826 | ||
| 827 | var envmap = Map.init(gpa); | |
| 677 | 828 | defer envmap.deinit(); |
| 678 | 829 | |
| 679 | 830 | try envmap.put("HOME", "/home/ifreund"); |
| ... | ... | @@ -682,29 +833,24 @@ test "Map.createBlock" { |
| 682 | 833 | try envmap.put("DEBUGINFOD_URLS", " "); |
| 683 | 834 | try envmap.put("XCURSOR_SIZE", "24"); |
| 684 | 835 | |
| 685 | var arena = std.heap.ArenaAllocator.init(allocator); | |
| 686 | defer arena.deinit(); | |
| 687 | const environ = try envmap.createBlockPosix(arena.allocator(), .{}); | |
| 836 | const block = try envmap.createPosixBlock(gpa, .{}); | |
| 837 | defer block.deinit(gpa); | |
| 688 | 838 | |
| 689 | try testing.expectEqual(@as(usize, 5), environ.len); | |
| 839 | try testing.expectEqual(@as(usize, 5), block.slice.len); | |
| 690 | 840 | |
| 691 | inline for (.{ | |
| 841 | for (&[_][]const u8{ | |
| 692 | 842 | "HOME=/home/ifreund", |
| 693 | 843 | "WAYLAND_DISPLAY=wayland-1", |
| 694 | 844 | "DISPLAY=:1", |
| 695 | 845 | "DEBUGINFOD_URLS= ", |
| 696 | 846 | "XCURSOR_SIZE=24", |
| 697 | }) |target| { | |
| 698 | for (environ) |variable| { | |
| 699 | if (mem.eql(u8, mem.span(variable orelse continue), target)) break; | |
| 700 | } else { | |
| 701 | try testing.expect(false); // Environment variable not found | |
| 702 | } | |
| 703 | } | |
| 847 | }, block.slice) |expected, actual| try testing.expectEqualStrings(expected, mem.span(actual.?)); | |
| 704 | 848 | } |
| 705 | 849 | |
| 706 | 850 | test Map { |
| 707 | var env = Map.init(testing.allocator); | |
| 851 | const gpa = testing.allocator; | |
| 852 | ||
| 853 | var env: Map = .init(gpa); | |
| 708 | 854 | defer env.deinit(); |
| 709 | 855 | |
| 710 | 856 | try env.put("SOMETHING_NEW", "hello"); |
| ... | ... | @@ -740,6 +886,7 @@ test Map { |
| 740 | 886 | try testing.expect(env.swapRemove("SOMETHING_NEW")); |
| 741 | 887 | try testing.expect(!env.swapRemove("SOMETHING_NEW")); |
| 742 | 888 | try testing.expect(env.get("SOMETHING_NEW") == null); |
| 889 | try testing.expect(!env.contains("SOMETHING_NEW")); | |
| 743 | 890 | |
| 744 | 891 | try testing.expectEqual(@as(Map.Size, 1), env.count()); |
| 745 | 892 | |
| ... | ... | @@ -749,10 +896,10 @@ test Map { |
| 749 | 896 | try testing.expectEqualStrings("something else", env.get("кириллица").?); |
| 750 | 897 | |
| 751 | 898 | // and WTF-8 that's not valid UTF-8 |
| 752 | const wtf8_with_surrogate_pair = try unicode.wtf16LeToWtf8Alloc(testing.allocator, &[_]u16{ | |
| 899 | const wtf8_with_surrogate_pair = try unicode.wtf16LeToWtf8Alloc(gpa, &[_]u16{ | |
| 753 | 900 | mem.nativeToLittle(u16, 0xD83D), // unpaired high surrogate |
| 754 | 901 | }); |
| 755 | defer testing.allocator.free(wtf8_with_surrogate_pair); | |
| 902 | defer gpa.free(wtf8_with_surrogate_pair); | |
| 756 | 903 | |
| 757 | 904 | try env.put(wtf8_with_surrogate_pair, wtf8_with_surrogate_pair); |
| 758 | 905 | try testing.expectEqualSlices(u8, wtf8_with_surrogate_pair, env.get(wtf8_with_surrogate_pair).?); |
| ... | ... | @@ -769,13 +916,9 @@ test "convert from Environ to Map and back again" { |
| 769 | 916 | defer map.deinit(); |
| 770 | 917 | try map.put("FOO", "BAR"); |
| 771 | 918 | try map.put("A", ""); |
| 772 | try map.put("", "B"); | |
| 773 | ||
| 774 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | |
| 775 | defer arena_allocator.deinit(); | |
| 776 | const arena = arena_allocator.allocator(); | |
| 777 | 919 | |
| 778 | const environ: Environ = .{ .block = try map.createBlockPosix(arena, .{}) }; | |
| 920 | const environ: Environ = .{ .block = try map.createPosixBlock(gpa, .{}) }; | |
| 921 | defer environ.block.deinit(gpa); | |
| 779 | 922 | |
| 780 | 923 | try testing.expectEqual(true, environ.contains(gpa, "FOO")); |
| 781 | 924 | try testing.expectEqual(false, environ.contains(gpa, "BAR")); |
| ... | ... | @@ -783,7 +926,6 @@ test "convert from Environ to Map and back again" { |
| 783 | 926 | try testing.expectEqual(true, environ.containsConstant("A")); |
| 784 | 927 | try testing.expectEqual(false, environ.containsUnempty(gpa, "A")); |
| 785 | 928 | try testing.expectEqual(false, environ.containsUnemptyConstant("A")); |
| 786 | try testing.expectEqual(true, environ.contains(gpa, "")); | |
| 787 | 929 | try testing.expectEqual(false, environ.contains(gpa, "B")); |
| 788 | 930 | |
| 789 | 931 | try testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(gpa, "BOGUS")); |
| ... | ... | @@ -800,23 +942,47 @@ test "convert from Environ to Map and back again" { |
| 800 | 942 | try testing.expectEqualDeep(map.values(), map2.values()); |
| 801 | 943 | } |
| 802 | 944 | |
| 803 | test createMapWide { | |
| 804 | if (builtin.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO | |
| 945 | test "Map.putPosixBlock" { | |
| 946 | const gpa = testing.allocator; | |
| 947 | ||
| 948 | var map: Map = .init(gpa); | |
| 949 | defer map.deinit(); | |
| 950 | ||
| 951 | try map.put("FOO", "BAR"); | |
| 952 | try map.put("A", ""); | |
| 953 | try map.put("ZIG_PROGRESS", "unchanged"); | |
| 954 | ||
| 955 | const block = try map.createPosixBlock(gpa, .{}); | |
| 956 | defer block.deinit(gpa); | |
| 957 | ||
| 958 | var map2: Map = .init(gpa); | |
| 959 | defer map2.deinit(); | |
| 960 | try map2.putPosixBlock(block.view()); | |
| 961 | ||
| 962 | try testing.expectEqualDeep(&[_][]const u8{ "FOO", "A", "ZIG_PROGRESS" }, map2.keys()); | |
| 963 | try testing.expectEqualDeep(&[_][]const u8{ "BAR", "", "unchanged" }, map2.values()); | |
| 964 | } | |
| 965 | ||
| 966 | test "Map.putWindowsBlock" { | |
| 967 | if (native_os != .windows) return; | |
| 805 | 968 | |
| 806 | 969 | const gpa = testing.allocator; |
| 807 | 970 | |
| 808 | 971 | var map: Map = .init(gpa); |
| 809 | 972 | defer map.deinit(); |
| 973 | ||
| 810 | 974 | try map.put("FOO", "BAR"); |
| 811 | 975 | try map.put("A", ""); |
| 812 | try map.put("", "B"); | |
| 976 | try map.put("=B", ""); | |
| 977 | try map.put("ZIG_PROGRESS", "unchanged"); | |
| 813 | 978 | |
| 814 | const environ: [:0]u16 = try map.createBlockWindows(gpa); | |
| 815 | defer gpa.free(environ); | |
| 979 | const block = try map.createWindowsBlock(gpa, .{}); | |
| 980 | defer block.deinit(gpa); | |
| 816 | 981 | |
| 817 | var map2 = try createMapWide(environ, gpa); | |
| 982 | var map2: Map = .init(gpa); | |
| 818 | 983 | defer map2.deinit(); |
| 984 | try map2.putWindowsBlock(block.view()); | |
| 819 | 985 | |
| 820 | try testing.expectEqualDeep(&[_][]const u8{ "FOO", "A", "=B" }, map2.keys()); | |
| 821 | try testing.expectEqualDeep(&[_][]const u8{ "BAR", "", "" }, map2.values()); | |
| 986 | try testing.expectEqualDeep(&[_][]const u8{ "FOO", "A", "=B", "ZIG_PROGRESS" }, map2.keys()); | |
| 987 | try testing.expectEqualDeep(&[_][]const u8{ "BAR", "", "", "unchanged" }, map2.values()); | |
| 822 | 988 | } |
lib/std/start.zig+9-8| ... | ... | @@ -90,15 +90,15 @@ fn _DllMainCRTStartup( |
| 90 | 90 | fn wasm_freestanding_start() callconv(.c) void { |
| 91 | 91 | // This is marked inline because for some reason LLVM in |
| 92 | 92 | // release mode fails to inline it, and we want fewer call frames in stack traces. |
| 93 | _ = @call(.always_inline, callMain, .{ {}, {} }); | |
| 93 | _ = @call(.always_inline, callMain, .{ {}, std.process.Environ.Block.global }); | |
| 94 | 94 | } |
| 95 | 95 | |
| 96 | 96 | fn startWasi() callconv(.c) void { |
| 97 | 97 | // The function call is marked inline because for some reason LLVM in |
| 98 | 98 | // release mode fails to inline it, and we want fewer call frames in stack traces. |
| 99 | 99 | switch (builtin.wasi_exec_model) { |
| 100 | .reactor => _ = @call(.always_inline, callMain, .{ {}, {} }), | |
| 101 | .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{ {}, {} })), | |
| 100 | .reactor => _ = @call(.always_inline, callMain, .{ {}, std.process.Environ.Block.global }), | |
| 101 | .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{ {}, std.process.Environ.Block.global })), | |
| 102 | 102 | } |
| 103 | 103 | } |
| 104 | 104 | |
| ... | ... | @@ -476,7 +476,7 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| 476 | 476 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; |
| 477 | 477 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; |
| 478 | 478 | |
| 479 | std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, {})); | |
| 479 | std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, .global)); | |
| 480 | 480 | } |
| 481 | 481 | |
| 482 | 482 | fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| ... | ... | @@ -620,13 +620,14 @@ fn expandStackSize(phdrs: []elf.Phdr) void { |
| 620 | 620 | } |
| 621 | 621 | |
| 622 | 622 | inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [:null]?[*:0]u8) u8 { |
| 623 | const env_block: std.process.Environ.Block = .{ .slice = envp }; | |
| 623 | 624 | if (std.Options.debug_threaded_io) |t| { |
| 624 | 625 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; |
| 625 | t.environ = .{ .process_environ = .{ .block = envp } }; | |
| 626 | t.environ = .{ .process_environ = .{ .block = env_block } }; | |
| 626 | 627 | } |
| 627 | 628 | std.Thread.maybeAttachSignalStack(); |
| 628 | 629 | std.debug.maybeEnableSegfaultHandler(); |
| 629 | return callMain(argv[0..argc], envp); | |
| 630 | return callMain(argv[0..argc], env_block); | |
| 630 | 631 | } |
| 631 | 632 | |
| 632 | 633 | fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.c) c_int { |
| ... | ... | @@ -648,7 +649,7 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal |
| 648 | 649 | std.debug.maybeEnableSegfaultHandler(); |
| 649 | 650 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; |
| 650 | 651 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; |
| 651 | return callMain(cmd_line_w, {}); | |
| 652 | return callMain(cmd_line_w, .global); | |
| 652 | 653 | }, |
| 653 | 654 | else => {}, |
| 654 | 655 | } |
| ... | ... | @@ -661,7 +662,7 @@ fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { |
| 661 | 662 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) { |
| 662 | 663 | if (std.Options.debug_threaded_io) |t| t.argv0.value = argv[0]; |
| 663 | 664 | } |
| 664 | return callMain(argv, &.{}); | |
| 665 | return callMain(argv, .empty); | |
| 665 | 666 | } |
| 666 | 667 | |
| 667 | 668 | /// General error message for a malformed return type |
test/standalone/windows_argv/fuzz.zig+7| ... | ... | @@ -149,6 +149,13 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO |
| 149 | 149 | break :spawn proc_info.hProcess; |
| 150 | 150 | }; |
| 151 | 151 | defer windows.CloseHandle(child_proc); |
| 152 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); | |
| 153 | switch (windows.ntdll.NtWaitForSingleObject(child_proc, windows.FALSE, &infinite_timeout)) { | |
| 154 | .WAIT_0 => {}, | |
| 155 | .ABANDONED_WAIT_0 => return error.WaitAbandoned, | |
| 156 | .TIMEOUT => return error.WaitTimeOut, | |
| 157 | else => |status| return windows.unexpectedStatus(status), | |
| 158 | } | |
| 152 | 159 | try windows.WaitForSingleObjectEx(child_proc, windows.INFINITE, false); |
| 153 | 160 | |
| 154 | 161 | var exit_code: windows.DWORD = undefined; |