mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
Io: Add processSetCurrentPath
The logic used to allow providing a path for setting the CWD of a child process in https://codeberg.org/ziglang/zig/pulls/31090 applies here as well: - Windows must provide a path when setting the CWD, so the path of an `Io.Dir` must be resolved before actually calling RtlSetCurrentDirectory_U - A directory handle may have multiple paths associated with it, so providing the CWD as a string retains a legitimate use case in cases where the precise path matters
This commit is contained in:
committed by
Andrew Kelley
parent
09bf51092b
commit
6be202f466
@@ -218,6 +218,7 @@ pub const VTable = struct {
|
||||
unlockStderr: *const fn (?*anyopaque) void,
|
||||
processCurrentPath: *const fn (?*anyopaque, buffer: []u8) std.process.CurrentPathError!usize,
|
||||
processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void,
|
||||
processSetCurrentPath: *const fn (?*anyopaque, []const u8) std.process.SetCurrentPathError!void,
|
||||
processReplace: *const fn (?*anyopaque, std.process.ReplaceOptions) std.process.ReplaceError,
|
||||
processReplacePath: *const fn (?*anyopaque, Dir, std.process.ReplaceOptions) std.process.ReplaceError,
|
||||
processSpawn: *const fn (?*anyopaque, std.process.SpawnOptions) std.process.SpawnError!std.process.Child,
|
||||
|
||||
@@ -434,6 +434,7 @@ pub fn io(ev: *Evented) Io {
|
||||
.unlockStderr = unlockStderr,
|
||||
.processCurrentPath = processCurrentPath,
|
||||
.processSetCurrentDir = processSetCurrentDir,
|
||||
.processSetCurrentPath = processSetCurrentPath,
|
||||
.processReplace = processReplace,
|
||||
.processReplacePath = processReplacePath,
|
||||
.processSpawn = processSpawn,
|
||||
@@ -4046,7 +4047,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr
|
||||
};
|
||||
}
|
||||
|
||||
fn processSetCurrentPath(userdata: ?*anyopaque, dir_path: []const u8) ChdirError!void {
|
||||
fn processSetCurrentPath(userdata: ?*anyopaque, dir_path: []const u8) process.SetCurrentPathError!void {
|
||||
const ev: *Evented = @ptrCast(@alignCast(userdata));
|
||||
_ = ev;
|
||||
var path_buffer: [c.PATH_MAX]u8 = undefined;
|
||||
|
||||
@@ -1841,6 +1841,7 @@ pub fn io(t: *Threaded) Io {
|
||||
.unlockStderr = unlockStderr,
|
||||
.processCurrentPath = processCurrentPath,
|
||||
.processSetCurrentDir = processSetCurrentDir,
|
||||
.processSetCurrentPath = processSetCurrentPath,
|
||||
.processReplace = processReplace,
|
||||
.processReplacePath = processReplacePath,
|
||||
.processSpawn = processSpawn,
|
||||
@@ -2006,6 +2007,7 @@ pub fn ioBasic(t: *Threaded) Io {
|
||||
.unlockStderr = unlockStderr,
|
||||
.processCurrentPath = processCurrentPath,
|
||||
.processSetCurrentDir = processSetCurrentDir,
|
||||
.processSetCurrentPath = processSetCurrentPath,
|
||||
.processReplace = processReplace,
|
||||
.processReplacePath = processReplacePath,
|
||||
.processSpawn = processSpawn,
|
||||
@@ -14176,6 +14178,43 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr
|
||||
return fchdir(dir.handle);
|
||||
}
|
||||
|
||||
fn processSetCurrentPath(userdata: ?*anyopaque, path: []const u8) process.SetCurrentPathError!void {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
|
||||
if (native_os == .wasi) return error.OperationUnsupported;
|
||||
|
||||
if (is_windows) {
|
||||
var path_w_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
|
||||
const len = std.unicode.calcWtf16LeLen(path) catch return error.InvalidWtf8;
|
||||
if (len > path_w_buf.len) return error.NameTooLong;
|
||||
const path_w_len = std.unicode.wtf8ToWtf16Le(&path_w_buf, path) catch |err| switch (err) {
|
||||
error.InvalidWtf8 => unreachable, // already validated
|
||||
};
|
||||
const path_w = path_w_buf[0..path_w_len];
|
||||
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&.init(path_w))) {
|
||||
.SUCCESS => return syscall.finish(),
|
||||
.OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
|
||||
.OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
|
||||
.OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
|
||||
.NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
|
||||
.INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
|
||||
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
|
||||
.OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
|
||||
.NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
|
||||
.CANCELLED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |status| return syscall.unexpectedNtstatus(status),
|
||||
};
|
||||
}
|
||||
|
||||
return chdir(path);
|
||||
}
|
||||
|
||||
pub const PosixAddress = extern union {
|
||||
any: posix.sockaddr,
|
||||
in: posix.sockaddr.in,
|
||||
|
||||
@@ -752,6 +752,7 @@ pub fn io(ev: *Evented) Io {
|
||||
.unlockStderr = unlockStderr,
|
||||
.processCurrentPath = processCurrentPath,
|
||||
.processSetCurrentDir = processSetCurrentDir,
|
||||
.processSetCurrentPath = processSetCurrentPath,
|
||||
.processReplace = processReplace,
|
||||
.processReplacePath = processReplacePath,
|
||||
.processSpawn = processSpawn,
|
||||
@@ -4176,7 +4177,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr
|
||||
return fchdir(&sync, dir.handle);
|
||||
}
|
||||
|
||||
fn processSetCurrentPath(userdata: ?*anyopaque, dir_path: []const u8) ChdirError!void {
|
||||
fn processSetCurrentPath(userdata: ?*anyopaque, dir_path: []const u8) process.SetCurrentPathError!void {
|
||||
const ev: *Evented = @ptrCast(@alignCast(userdata));
|
||||
var path_buffer: [PATH_MAX]u8 = undefined;
|
||||
const dir_path_posix = try pathToPosix(dir_path, &path_buffer);
|
||||
|
||||
@@ -900,6 +900,35 @@ pub fn setCurrentDir(io: Io, dir: Io.Dir) !void {
|
||||
return io.vtable.processSetCurrentDir(io.userdata, dir);
|
||||
}
|
||||
|
||||
pub const SetCurrentPathError = error{
|
||||
AccessDenied,
|
||||
SymLinkLoop,
|
||||
SystemResources,
|
||||
BadPathName,
|
||||
FileNotFound,
|
||||
FileSystem,
|
||||
NoDevice,
|
||||
NotDir,
|
||||
NameTooLong,
|
||||
OperationUnsupported,
|
||||
/// Windows-only. The path is invalid WTF-8.
|
||||
/// https://wtf-8.codeberg.page/
|
||||
InvalidWtf8,
|
||||
} || Io.Cancelable || Io.UnexpectedError;
|
||||
|
||||
/// Changes the current working directory to the given path.
|
||||
/// Corresponds to "chdir" in libc.
|
||||
///
|
||||
/// This modifies global process state and can have surprising effects in
|
||||
/// multithreaded applications. Most applications and especially libraries
|
||||
/// should not call this function as a general rule, however it can have use
|
||||
/// cases in, for example, implementing a shell, or child process execution.
|
||||
///
|
||||
/// Calling this function makes code less portable and less reusable.
|
||||
pub fn setCurrentPath(io: Io, path: []const u8) !void {
|
||||
return io.vtable.processSetCurrentPath(io.userdata, path);
|
||||
}
|
||||
|
||||
pub const LockMemoryError = error{
|
||||
UnsupportedOperation,
|
||||
PermissionDenied,
|
||||
|
||||
@@ -37,7 +37,7 @@ fn test_chdir_self(io: Io) !void {
|
||||
const old_cwd = old_cwd_buf[0..try std.process.currentPath(io, &old_cwd_buf)];
|
||||
|
||||
// Try changing to the current directory
|
||||
try std.Io.Threaded.chdir(old_cwd);
|
||||
try std.process.setCurrentPath(io, old_cwd);
|
||||
try expect_cwd(io, old_cwd);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ fn test_chdir_absolute(io: Io) !void {
|
||||
const parent = std.fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute
|
||||
|
||||
// Try changing to the parent via a full path
|
||||
try std.Io.Threaded.chdir(parent);
|
||||
try std.process.setCurrentPath(io, parent);
|
||||
|
||||
try expect_cwd(io, parent);
|
||||
}
|
||||
@@ -68,7 +68,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io, tmp_dir: Io.Dir) !void {
|
||||
defer gpa.free(expected_path);
|
||||
|
||||
// change current working directory to new test directory
|
||||
try std.Io.Threaded.chdir(subdir_path);
|
||||
try std.process.setCurrentPath(io, subdir_path);
|
||||
|
||||
var new_cwd_buf: [path_max]u8 = undefined;
|
||||
const new_cwd = new_cwd_buf[0..try std.process.currentPath(io, &new_cwd_buf)];
|
||||
|
||||
@@ -9,8 +9,6 @@ pub fn main(init: std.process.Init) !void {
|
||||
const gpa = init.gpa;
|
||||
const io = init.io;
|
||||
const process_cwd_path = try std.process.currentPathAlloc(io, init.arena.allocator());
|
||||
var initial_process_cwd = try Io.Dir.cwd().openDir(io, ".", .{});
|
||||
defer initial_process_cwd.close(io);
|
||||
|
||||
var it = try init.minimal.args.iterateAllocator(gpa);
|
||||
defer it.deinit();
|
||||
@@ -127,7 +125,7 @@ pub fn main(init: std.process.Init) !void {
|
||||
|
||||
// Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir
|
||||
try std.process.setCurrentDir(io, tmp_dir);
|
||||
defer std.process.setCurrentDir(io, initial_process_cwd) catch {};
|
||||
defer std.process.setCurrentPath(io, process_cwd_path) catch {};
|
||||
const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0);
|
||||
defer gpa.free(something_subdir_abs_path);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user