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...@@ -989,6 +989,39 @@ pub fn renameAbsolute(io: Io, old_path: []const u8, new_path: []const u8) Rename
989 return io.vtable.dirRename(io.userdata, my_cwd, old_path, my_cwd, new_path);989 return io.vtable.dirRename(io.userdata, my_cwd, old_path, my_cwd, new_path);
990}990}
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
992/// Use with `symLink`, `symLinkAtomic`, and `symLinkAbsolute` to1025/// Use with `symLink`, `symLinkAtomic`, and `symLinkAbsolute` to
993/// specify whether the symlink will point to a file or a directory. This value1026/// specify whether the symlink will point to a file or a directory. This value
994/// is ignored on all hosts except Windows where creating symlinks to different1027/// 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");...@@ -17,6 +17,7 @@ pub const Atomic = @import("File/Atomic.zig");
1717
18pub const Handle = std.posix.fd_t;18pub const Handle = std.posix.fd_t;
19pub const INode = std.posix.ino_t;19pub const INode = std.posix.ino_t;
20pub const NLink = std.posix.nlink_t;
20pub const Uid = std.posix.uid_t;21pub const Uid = std.posix.uid_t;
21pub const Gid = std.posix.gid_t;22pub const Gid = std.posix.gid_t;
2223
...@@ -47,6 +48,7 @@ pub const Stat = struct {...@@ -47,6 +48,7 @@ pub const Stat = struct {
47 /// The FileIndex on Windows is similar. It is a number for a file that48 /// The FileIndex on Windows is similar. It is a number for a file that
48 /// is unique to each filesystem.49 /// is unique to each filesystem.
49 inode: INode,50 inode: INode,
51 nlink: NLink,
50 size: u64,52 size: u64,
51 permissions: Permissions,53 permissions: Permissions,
52 kind: Kind,54 kind: Kind,
...@@ -101,9 +103,11 @@ pub const OpenFlags = struct {...@@ -101,9 +103,11 @@ pub const OpenFlags = struct {
101 mode: OpenMode = .read_only,103 mode: OpenMode = .read_only,
102104
103 /// Determines the behavior when opening a path that refers to a directory.105 /// Determines the behavior when opening a path that refers to a directory.
106 ///
104 /// If set to true, directories may be opened, but `error.IsDir` is still107 /// If set to true, directories may be opened, but `error.IsDir` is still
105 /// possible in certain scenarios, e.g. attempting to open a directory with108 /// possible in certain scenarios, e.g. attempting to open a directory with
106 /// write permissions.109 /// write permissions.
110 ///
107 /// If set to false, `error.IsDir` will always be returned when opening a directory.111 /// If set to false, `error.IsDir` will always be returned when opening a directory.
108 ///112 ///
109 /// When set to false:113 /// When set to false:
...@@ -289,7 +293,7 @@ pub fn sync(file: File, io: Io) SyncError!void {...@@ -289,7 +293,7 @@ pub fn sync(file: File, io: Io) SyncError!void {
289 return io.vtable.fileSync(io.userdata, file);293 return io.vtable.fileSync(io.userdata, file);
290}294}
291295
292/// Test whether the file refers to a terminal.296/// Test whether the file refers to a terminal (similar to libc "isatty").
293///297///
294/// See also:298/// See also:
295/// * `enableAnsiEscapeCodes`299/// * `enableAnsiEscapeCodes`
lib/std/Io/Threaded.zig+30-16
...@@ -1829,19 +1829,21 @@ fn dirStatFileLinux(...@@ -1829,19 +1829,21 @@ fn dirStatFileLinux(
1829 dir.handle,1829 dir.handle,
1830 sub_path_posix,1830 sub_path_posix,
1831 flags,1831 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 },
1833 &statx,1842 &statx,
1834 );1843 );
1835 switch (sys.errno(rc)) {1844 switch (sys.errno(rc)) {
1836 .SUCCESS => {1845 .SUCCESS => {
1837 current_thread.endSyscall();1846 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);
1845 return statFromLinux(&statx);1847 return statFromLinux(&statx);
1846 },1848 },
1847 .INTR => {1849 .INTR => {
...@@ -2082,19 +2084,21 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {...@@ -2082,19 +2084,21 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
2082 file.handle,2084 file.handle,
2083 "",2085 "",
2084 linux.AT.EMPTY_PATH,2086 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 },
2086 &statx,2097 &statx,
2087 );2098 );
2088 switch (sys.errno(rc)) {2099 switch (sys.errno(rc)) {
2089 .SUCCESS => {2100 .SUCCESS => {
2090 current_thread.endSyscall();2101 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);
2098 return statFromLinux(&statx);2102 return statFromLinux(&statx);
2099 },2103 },
2100 .INTR => {2104 .INTR => {
...@@ -10499,11 +10503,20 @@ fn clockToWasi(clock: Io.Clock) std.os.wasi.clockid_t {...@@ -10499,11 +10503,20 @@ fn clockToWasi(clock: Io.Clock) std.os.wasi.clockid_t {
10499}10503}
1050010504
10501fn statFromLinux(stx: *const std.os.linux.Statx) File.Stat {10505fn 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);
10502 const atime = stx.atime;10514 const atime = stx.atime;
10503 const mtime = stx.mtime;10515 const mtime = stx.mtime;
10504 const ctime = stx.ctime;10516 const ctime = stx.ctime;
10505 return .{10517 return .{
10506 .inode = stx.ino,10518 .inode = stx.ino,
10519 .nlink = stx.nlink,
10507 .size = stx.size,10520 .size = stx.size,
10508 .permissions = .fromMode(stx.mode),10521 .permissions = .fromMode(stx.mode),
10509 .kind = switch (stx.mode & std.os.linux.S.IFMT) {10522 .kind = switch (stx.mode & std.os.linux.S.IFMT) {
...@@ -10528,6 +10541,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat {...@@ -10528,6 +10541,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat {
10528 const ctime = st.ctime();10541 const ctime = st.ctime();
10529 return .{10542 return .{
10530 .inode = st.ino,10543 .inode = st.ino,
10544 .nlink = st.nlink,
10531 .size = @bitCast(st.size),10545 .size = @bitCast(st.size),
10532 .permissions = .fromMode(st.mode),10546 .permissions = .fromMode(st.mode),
10533 .kind = k: {10547 .kind = k: {
lib/std/c.zig+1-1
...@@ -162,7 +162,7 @@ pub const nlink_t = switch (native_os) {...@@ -162,7 +162,7 @@ pub const nlink_t = switch (native_os) {
162 .freebsd, .serenity => u64,162 .freebsd, .serenity => u64,
163 .openbsd, .netbsd, .illumos => u32,163 .openbsd, .netbsd, .illumos => u32,
164 .haiku => i32,164 .haiku => i32,
165 else => void,165 else => u0,
166};166};
167167
168pub const uid_t = switch (native_os) {168pub const uid_t = switch (native_os) {