authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-18 13:49:56-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:10-08:00
logf3723b42e1f05d14479d6f5507943cd1905e0fcf
tree3941dddf0af4affbc29c5a2dc8f4cc16b800f74f
parent446c145ca86b014d6743f5666e9ee1d671b56045

std.Io: add unimplemented hard link API to File and Dir


4 files changed, 69 insertions(+), 18 deletions(-)

lib/std/Io/Dir.zig+33
......@@ -989,6 +989,39 @@ pub fn renameAbsolute(io: Io, old_path: []const u8, new_path: []const u8) Rename
989989 return io.vtable.dirRename(io.userdata, my_cwd, old_path, my_cwd, new_path);
990990}
991991
992pub const HardLinkOptions = struct {
993 follow_symlinks: bool = true,
994};
995
996pub const HardLinkError = error{
997 AccessDenied,
998 PermissionDenied,
999 DiskQuota,
1000 PathAlreadyExists,
1001 HardwareFailure,
1002 /// Either the OS or the filesystem does not support hard links.
1003 OperationUnsupported,
1004 SymLinkLoop,
1005 LinkQuotaExceeded,
1006 FileNotFound,
1007 SystemResources,
1008 NoSpaceLeft,
1009 ReadOnlyFileSystem,
1010 NotSameFileSystem,
1011 NotDir,
1012} || Io.Cancelable || PathNameError || Io.UnexpectedError;
1013
1014pub fn hardLink(
1015 old_dir: Dir,
1016 old_sub_path: []const u8,
1017 new_dir: Dir,
1018 new_sub_path: []const u8,
1019 io: Io,
1020 options: HardLinkOptions,
1021) HardLinkError!void {
1022 return io.vtable.dirHardLink(io.userdata, old_dir, old_sub_path, new_dir, new_sub_path, options);
1023}
1024
9921025/// Use with `symLink`, `symLinkAtomic`, and `symLinkAbsolute` to
9931026/// specify whether the symlink will point to a file or a directory. This value
9941027/// is ignored on all hosts except Windows where creating symlinks to different
lib/std/Io/File.zig+5-1
......@@ -17,6 +17,7 @@ pub const Atomic = @import("File/Atomic.zig");
1717
1818pub const Handle = std.posix.fd_t;
1919pub const INode = std.posix.ino_t;
20pub const NLink = std.posix.nlink_t;
2021pub const Uid = std.posix.uid_t;
2122pub const Gid = std.posix.gid_t;
2223
......@@ -47,6 +48,7 @@ pub const Stat = struct {
4748 /// The FileIndex on Windows is similar. It is a number for a file that
4849 /// is unique to each filesystem.
4950 inode: INode,
51 nlink: NLink,
5052 size: u64,
5153 permissions: Permissions,
5254 kind: Kind,
......@@ -101,9 +103,11 @@ pub const OpenFlags = struct {
101103 mode: OpenMode = .read_only,
102104
103105 /// Determines the behavior when opening a path that refers to a directory.
106 ///
104107 /// If set to true, directories may be opened, but `error.IsDir` is still
105108 /// possible in certain scenarios, e.g. attempting to open a directory with
106109 /// write permissions.
110 ///
107111 /// If set to false, `error.IsDir` will always be returned when opening a directory.
108112 ///
109113 /// When set to false:
......@@ -289,7 +293,7 @@ pub fn sync(file: File, io: Io) SyncError!void {
289293 return io.vtable.fileSync(io.userdata, file);
290294}
291295
292/// Test whether the file refers to a terminal.
296/// Test whether the file refers to a terminal (similar to libc "isatty").
293297///
294298/// See also:
295299/// * `enableAnsiEscapeCodes`
lib/std/Io/Threaded.zig+30-16
......@@ -1829,19 +1829,21 @@ fn dirStatFileLinux(
18291829 dir.handle,
18301830 sub_path_posix,
18311831 flags,
1832 .{ .TYPE = true, .MODE = true, .ATIME = true, .MTIME = true, .CTIME = true, .INO = true, .SIZE = true },
1832 .{
1833 .TYPE = true,
1834 .MODE = true,
1835 .ATIME = true,
1836 .MTIME = true,
1837 .CTIME = true,
1838 .INO = true,
1839 .SIZE = true,
1840 .NLINK = true,
1841 },
18331842 &statx,
18341843 );
18351844 switch (sys.errno(rc)) {
18361845 .SUCCESS => {
18371846 current_thread.endSyscall();
1838 assert(statx.mask.TYPE);
1839 assert(statx.mask.MODE);
1840 assert(statx.mask.ATIME);
1841 assert(statx.mask.MTIME);
1842 assert(statx.mask.CTIME);
1843 assert(statx.mask.INO);
1844 assert(statx.mask.SIZE);
18451847 return statFromLinux(&statx);
18461848 },
18471849 .INTR => {
......@@ -2082,19 +2084,21 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
20822084 file.handle,
20832085 "",
20842086 linux.AT.EMPTY_PATH,
2085 .{ .TYPE = true, .MODE = true, .ATIME = true, .MTIME = true, .CTIME = true, .INO = true, .SIZE = true },
2087 .{
2088 .TYPE = true,
2089 .MODE = true,
2090 .ATIME = true,
2091 .MTIME = true,
2092 .CTIME = true,
2093 .INO = true,
2094 .SIZE = true,
2095 .NLINK = true,
2096 },
20862097 &statx,
20872098 );
20882099 switch (sys.errno(rc)) {
20892100 .SUCCESS => {
20902101 current_thread.endSyscall();
2091 assert(statx.mask.TYPE);
2092 assert(statx.mask.MODE);
2093 assert(statx.mask.ATIME);
2094 assert(statx.mask.MTIME);
2095 assert(statx.mask.CTIME);
2096 assert(statx.mask.INO);
2097 assert(statx.mask.SIZE);
20982102 return statFromLinux(&statx);
20992103 },
21002104 .INTR => {
......@@ -10499,11 +10503,20 @@ fn clockToWasi(clock: Io.Clock) std.os.wasi.clockid_t {
1049910503}
1050010504
1050110505fn statFromLinux(stx: *const std.os.linux.Statx) File.Stat {
10506 assert(stx.mask.TYPE);
10507 assert(stx.mask.MODE);
10508 assert(stx.mask.ATIME);
10509 assert(stx.mask.MTIME);
10510 assert(stx.mask.CTIME);
10511 assert(stx.mask.INO);
10512 assert(stx.mask.SIZE);
10513 assert(stx.mask.NLINK);
1050210514 const atime = stx.atime;
1050310515 const mtime = stx.mtime;
1050410516 const ctime = stx.ctime;
1050510517 return .{
1050610518 .inode = stx.ino,
10519 .nlink = stx.nlink,
1050710520 .size = stx.size,
1050810521 .permissions = .fromMode(stx.mode),
1050910522 .kind = switch (stx.mode & std.os.linux.S.IFMT) {
......@@ -10528,6 +10541,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat {
1052810541 const ctime = st.ctime();
1052910542 return .{
1053010543 .inode = st.ino,
10544 .nlink = st.nlink,
1053110545 .size = @bitCast(st.size),
1053210546 .permissions = .fromMode(st.mode),
1053310547 .kind = k: {
lib/std/c.zig+1-1
......@@ -162,7 +162,7 @@ pub const nlink_t = switch (native_os) {
162162 .freebsd, .serenity => u64,
163163 .openbsd, .netbsd, .illumos => u32,
164164 .haiku => i32,
165 else => void,
165 else => u0,
166166};
167167
168168pub const uid_t = switch (native_os) {