std.Io.Threaded: implement dirHardLink

This commit is contained in:
Andrew Kelley 2025-12-18 22:21:35 -08:00
parent 7ce5ee2e92
commit 651ff9f9ee
3 changed files with 115 additions and 2 deletions

View file

@ -683,6 +683,7 @@ pub const VTable = struct {
dirSetFilePermissions: *const fn (?*anyopaque, Dir, []const u8, File.Permissions, Dir.SetFilePermissionsOptions) Dir.SetFilePermissionsError!void,
dirSetTimestamps: *const fn (?*anyopaque, Dir, []const u8, last_accessed: Timestamp, last_modified: Timestamp, Dir.SetTimestampsOptions) Dir.SetTimestampsError!void,
dirSetTimestampsNow: *const fn (?*anyopaque, Dir, []const u8, Dir.SetTimestampsOptions) Dir.SetTimestampsError!void,
dirHardLink: *const fn (?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8, Dir.HardLinkOptions) Dir.HardLinkError!void,
fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
fileLength: *const fn (?*anyopaque, File) File.LengthError!u64,

View file

@ -728,6 +728,7 @@ pub fn io(t: *Threaded) Io {
.dirSetFilePermissions = dirSetFilePermissions,
.dirSetTimestamps = dirSetTimestamps,
.dirSetTimestampsNow = dirSetTimestampsNow,
.dirHardLink = dirHardLink,
.fileStat = fileStat,
.fileLength = fileLength,
@ -862,6 +863,7 @@ pub fn ioBasic(t: *Threaded) Io {
.dirSetFilePermissions = dirSetFilePermissions,
.dirSetTimestamps = dirSetTimestamps,
.dirSetTimestampsNow = dirSetTimestampsNow,
.dirHardLink = dirHardLink,
.fileStat = fileStat,
.fileLength = fileLength,
@ -6264,6 +6266,116 @@ fn dirOpenDirWasi(
}
}
fn dirHardLink(
userdata: ?*anyopaque,
old_dir: Dir,
old_sub_path: []const u8,
new_dir: Dir,
new_sub_path: []const u8,
options: Dir.HardLinkOptions,
) Dir.HardLinkError!void {
if (is_windows) return error.OperationUnsupported;
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
if (native_os == .wasi and !builtin.link_libc) {
const flags: std.os.wasi.lookupflags_t = .{
.SYMLINK_FOLLOW = options.follow_symlinks,
};
try current_thread.beginSyscall();
while (true) {
switch (std.os.wasi.path_link(
old_dir.handle,
flags,
old_sub_path.ptr,
old_sub_path.len,
new_dir.handle,
new_sub_path.ptr,
new_sub_path.len,
)) {
.SUCCESS => return current_thread.endSyscall(),
.INTR => {
try current_thread.checkCancel();
continue;
},
.CANCELED => return current_thread.endSyscallCanceled(),
else => |e| {
current_thread.endSyscall();
switch (e) {
.ACCES => return error.AccessDenied,
.DQUOT => return error.DiskQuota,
.EXIST => return error.PathAlreadyExists,
.FAULT => |err| return errnoBug(err),
.IO => return error.HardwareFailure,
.LOOP => return error.SymLinkLoop,
.MLINK => return error.LinkQuotaExceeded,
.NAMETOOLONG => return error.NameTooLong,
.NOENT => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOSPC => return error.NoSpaceLeft,
.NOTDIR => return error.NotDir,
.PERM => return error.PermissionDenied,
.ROFS => return error.ReadOnlyFileSystem,
.XDEV => return error.NotSameFileSystem,
.INVAL => |err| return errnoBug(err),
.ILSEQ => return error.BadPathName,
else => |err| return posix.unexpectedErrno(err),
}
},
}
}
}
var old_path_buffer: [posix.PATH_MAX]u8 = undefined;
var new_path_buffer: [posix.PATH_MAX]u8 = undefined;
const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);
const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
try current_thread.beginSyscall();
while (true) {
switch (posix.errno(posix.system.linkat(
old_dir.handle,
old_sub_path_posix,
new_dir.handle,
new_sub_path_posix,
flags,
))) {
.SUCCESS => return current_thread.endSyscall(),
.INTR => {
try current_thread.checkCancel();
continue;
},
.CANCELED => return current_thread.endSyscallCanceled(),
else => |e| {
current_thread.endSyscall();
switch (e) {
.ACCES => return error.AccessDenied,
.DQUOT => return error.DiskQuota,
.EXIST => return error.PathAlreadyExists,
.FAULT => |err| return errnoBug(err),
.IO => return error.HardwareFailure,
.LOOP => return error.SymLinkLoop,
.MLINK => return error.LinkQuotaExceeded,
.NAMETOOLONG => return error.NameTooLong,
.NOENT => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOSPC => return error.NoSpaceLeft,
.NOTDIR => return error.NotDir,
.PERM => return error.PermissionDenied,
.ROFS => return error.ReadOnlyFileSystem,
.XDEV => return error.NotSameFileSystem,
.INVAL => |err| return errnoBug(err),
.ILSEQ => return error.BadPathName,
else => |err| return posix.unexpectedErrno(err),
}
},
}
}
}
fn fileClose(userdata: ?*anyopaque, files: []const File) void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;

View file

@ -1561,14 +1561,14 @@ pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8) usize {
}
}
pub fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: i32) usize {
pub fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: u32) usize {
return syscall5(
.linkat,
@as(usize, @bitCast(@as(isize, oldfd))),
@intFromPtr(oldpath),
@as(usize, @bitCast(@as(isize, newfd))),
@intFromPtr(newpath),
@as(usize, @bitCast(@as(isize, flags))),
flags,
);
}