authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-11 22:11:16-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:09-08:00
log68621afd2e203d82b6f53bf4ede951827fa98db8
tree60899decd0062ce7786592e49ca77ab6f80d22fd
parent0e230993d51d0ecded40d5235ada4f2f64036b26

std.tar: update fs API calls to take io argument


15 files changed, 110 insertions(+), 110 deletions(-)

lib/compiler/aro/aro/Compilation.zig+2-1
...@@ -2162,8 +2162,9 @@ pub fn locSlice(comp: *const Compilation, loc: Source.Location) []const u8 {...@@ -2162,8 +2162,9 @@ pub fn locSlice(comp: *const Compilation, loc: Source.Location) []const u8 {
2162}2162}
21632163
2164pub fn getSourceMTimeUncached(comp: *const Compilation, source_id: Source.Id) ?u64 {2164pub fn getSourceMTimeUncached(comp: *const Compilation, source_id: Source.Id) ?u64 {
2165 const io = comp.io;
2165 const source = comp.getSource(source_id);2166 const source = comp.getSource(source_id);
2166 if (comp.cwd.statFile(source.path)) |stat| {2167 if (comp.cwd.statFile(io, source.path, .{})) |stat| {
2167 return std.math.cast(u64, stat.mtime.toSeconds());2168 return std.math.cast(u64, stat.mtime.toSeconds());
2168 } else |_| {2169 } else |_| {
2169 return null;2170 return null;
lib/std/Build/Cache/Path.zig+2-2
...@@ -94,14 +94,14 @@ pub fn makeOpenPath(p: Path, sub_path: []const u8, opts: Io.Dir.OpenOptions) !Io...@@ -94,14 +94,14 @@ pub fn makeOpenPath(p: Path, sub_path: []const u8, opts: Io.Dir.OpenOptions) !Io
94 return p.root_dir.handle.makeOpenPath(joined_path, opts);94 return p.root_dir.handle.makeOpenPath(joined_path, opts);
95}95}
9696
97pub fn statFile(p: Path, sub_path: []const u8) !Io.Dir.Stat {97pub fn statFile(p: Path, io: Io, sub_path: []const u8) !Io.Dir.Stat {
98 var buf: [fs.max_path_bytes]u8 = undefined;98 var buf: [fs.max_path_bytes]u8 = undefined;
99 const joined_path = if (p.sub_path.len == 0) sub_path else p: {99 const joined_path = if (p.sub_path.len == 0) sub_path else p: {
100 break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{100 break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
101 p.sub_path, sub_path,101 p.sub_path, sub_path,
102 }) catch return error.NameTooLong;102 }) catch return error.NameTooLong;
103 };103 };
104 return p.root_dir.handle.statFile(joined_path);104 return p.root_dir.handle.statFile(io, joined_path, .{});
105}105}
106106
107pub fn atomicFile(107pub fn atomicFile(
lib/std/Io.zig+1-1
...@@ -671,7 +671,7 @@ pub const VTable = struct {...@@ -671,7 +671,7 @@ pub const VTable = struct {
671 dirMakeOpenPath: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions, Dir.OpenOptions) Dir.MakeOpenPathError!Dir,671 dirMakeOpenPath: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions, Dir.OpenOptions) Dir.MakeOpenPathError!Dir,
672 dirOpenDir: *const fn (?*anyopaque, Dir, []const u8, Dir.OpenOptions) Dir.OpenError!Dir,672 dirOpenDir: *const fn (?*anyopaque, Dir, []const u8, Dir.OpenOptions) Dir.OpenError!Dir,
673 dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,673 dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,
674 dirStatPath: *const fn (?*anyopaque, Dir, []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat,674 dirStatFile: *const fn (?*anyopaque, Dir, []const u8, Dir.StatFileOptions) Dir.StatFileError!File.Stat,
675 dirAccess: *const fn (?*anyopaque, Dir, []const u8, Dir.AccessOptions) Dir.AccessError!void,675 dirAccess: *const fn (?*anyopaque, Dir, []const u8, Dir.AccessOptions) Dir.AccessError!void,
676 dirCreateFile: *const fn (?*anyopaque, Dir, []const u8, File.CreateFlags) File.OpenError!File,676 dirCreateFile: *const fn (?*anyopaque, Dir, []const u8, File.CreateFlags) File.OpenError!File,
677 dirOpenFile: *const fn (?*anyopaque, Dir, []const u8, File.OpenFlags) File.OpenError!File,677 dirOpenFile: *const fn (?*anyopaque, Dir, []const u8, File.OpenFlags) File.OpenError!File,
lib/std/Io/Dir.zig+6-6
...@@ -669,7 +669,7 @@ pub fn makeDirAbsolute(io: Io, absolute_path: []const u8, permissions: Permissio...@@ -669,7 +669,7 @@ pub fn makeDirAbsolute(io: Io, absolute_path: []const u8, permissions: Permissio
669669
670test makeDirAbsolute {}670test makeDirAbsolute {}
671671
672pub const MakePathError = MakeError || StatPathError;672pub const MakePathError = MakeError || StatFileError;
673673
674/// Creates parent directories with default permissions as necessary to ensure674/// Creates parent directories with default permissions as necessary to ensure
675/// `sub_path` exists as a directory.675/// `sub_path` exists as a directory.
...@@ -708,7 +708,7 @@ pub fn makePathStatus(dir: Dir, io: Io, sub_path: []const u8, permissions: Permi...@@ -708,7 +708,7 @@ pub fn makePathStatus(dir: Dir, io: Io, sub_path: []const u8, permissions: Permi
708 return io.vtable.dirMakePath(io.userdata, dir, sub_path, permissions);708 return io.vtable.dirMakePath(io.userdata, dir, sub_path, permissions);
709}709}
710710
711pub const MakeOpenPathError = MakeError || OpenError || StatPathError;711pub const MakeOpenPathError = MakeError || OpenError || StatFileError;
712712
713pub const MakeOpenPathOptions = struct {713pub const MakeOpenPathOptions = struct {
714 open_options: OpenOptions = .{},714 open_options: OpenOptions = .{},
...@@ -734,9 +734,9 @@ pub fn stat(dir: Dir, io: Io) StatError!Stat {...@@ -734,9 +734,9 @@ pub fn stat(dir: Dir, io: Io) StatError!Stat {
734 return io.vtable.dirStat(io.userdata, dir);734 return io.vtable.dirStat(io.userdata, dir);
735}735}
736736
737pub const StatPathError = File.OpenError || File.StatError;737pub const StatFileError = File.OpenError || File.StatError;
738738
739pub const StatPathOptions = struct {739pub const StatFileOptions = struct {
740 follow_symlinks: bool = true,740 follow_symlinks: bool = true,
741};741};
742742
...@@ -752,8 +752,8 @@ pub const StatPathOptions = struct {...@@ -752,8 +752,8 @@ pub const StatPathOptions = struct {
752/// * On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).752/// * On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
753/// * On WASI, `sub_path` should be encoded as valid UTF-8.753/// * On WASI, `sub_path` should be encoded as valid UTF-8.
754/// * On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.754/// * On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
755pub fn statPath(dir: Dir, io: Io, sub_path: []const u8, options: StatPathOptions) StatPathError!Stat {755pub fn statFile(dir: Dir, io: Io, sub_path: []const u8, options: StatFileOptions) StatFileError!Stat {
756 return io.vtable.dirStatPath(io.userdata, dir, sub_path, options);756 return io.vtable.dirStatFile(io.userdata, dir, sub_path, options);
757}757}
758758
759pub const RealPathError = error{759pub const RealPathError = error{
lib/std/Io/File.zig+3
...@@ -372,6 +372,7 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(...@@ -372,6 +372,7 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(
372 _,372 _,
373373
374 pub const default_dir: @This() = .default_file;374 pub const default_dir: @This() = .default_file;
375 pub const executable_file: @This() = .default_file;
375 pub const has_executable_bit = false;376 pub const has_executable_bit = false;
376377
377 const windows = std.os.windows;378 const windows = std.os.windows;
...@@ -401,6 +402,7 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(...@@ -401,6 +402,7 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(
401 /// process-scoped "umask" setting to adjust this number for file creation.402 /// process-scoped "umask" setting to adjust this number for file creation.
402 default_file = 0o666,403 default_file = 0o666,
403 default_dir = 0o755,404 default_dir = 0o755,
405 executable_file = 0o777,
404 _,406 _,
405407
406 pub const has_executable_bit = true;408 pub const has_executable_bit = true;
...@@ -428,6 +430,7 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(...@@ -428,6 +430,7 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(
428} else enum(u0) {430} else enum(u0) {
429 default_file = 0,431 default_file = 0,
430 pub const default_dir: @This() = .default_file;432 pub const default_dir: @This() = .default_file;
433 pub const executable_file: @This() = .default_file;
431 pub const has_executable_bit = false;434 pub const has_executable_bit = false;
432};435};
433436
lib/std/Io/Threaded.zig+25-25
...@@ -708,7 +708,7 @@ pub fn io(t: *Threaded) Io {...@@ -708,7 +708,7 @@ pub fn io(t: *Threaded) Io {
708 .dirMakePath = dirMakePath,708 .dirMakePath = dirMakePath,
709 .dirMakeOpenPath = dirMakeOpenPath,709 .dirMakeOpenPath = dirMakeOpenPath,
710 .dirStat = dirStat,710 .dirStat = dirStat,
711 .dirStatPath = dirStatPath,711 .dirStatFile = dirStatFile,
712 .dirAccess = dirAccess,712 .dirAccess = dirAccess,
713 .dirCreateFile = dirCreateFile,713 .dirCreateFile = dirCreateFile,
714 .dirOpenFile = dirOpenFile,714 .dirOpenFile = dirOpenFile,
...@@ -840,7 +840,7 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -840,7 +840,7 @@ pub fn ioBasic(t: *Threaded) Io {
840 .dirMakePath = dirMakePath,840 .dirMakePath = dirMakePath,
841 .dirMakeOpenPath = dirMakeOpenPath,841 .dirMakeOpenPath = dirMakeOpenPath,
842 .dirStat = dirStat,842 .dirStat = dirStat,
843 .dirStatPath = dirStatPath,843 .dirStatFile = dirStatFile,
844 .dirAccess = dirAccess,844 .dirAccess = dirAccess,
845 .dirCreateFile = dirCreateFile,845 .dirCreateFile = dirCreateFile,
846 .dirOpenFile = dirOpenFile,846 .dirOpenFile = dirOpenFile,
...@@ -1623,7 +1623,7 @@ fn dirMakePath(...@@ -1623,7 +1623,7 @@ fn dirMakePath(
1623 // could cause an infinite loop1623 // could cause an infinite loop
1624 check_dir: {1624 check_dir: {
1625 // workaround for windows, see https://github.com/ziglang/zig/issues/167381625 // workaround for windows, see https://github.com/ziglang/zig/issues/16738
1626 const fstat = dirStatPath(t, dir, component.path, .{}) catch |stat_err| switch (stat_err) {1626 const fstat = dirStatFile(t, dir, component.path, .{}) catch |stat_err| switch (stat_err) {
1627 error.IsDir => break :check_dir,1627 error.IsDir => break :check_dir,
1628 else => |e| return e,1628 else => |e| return e,
1629 };1629 };
...@@ -1752,7 +1752,7 @@ fn dirMakeOpenPathWindows(...@@ -1752,7 +1752,7 @@ fn dirMakeOpenPathWindows(
1752 // could cause an infinite loop1752 // could cause an infinite loop
1753 check_dir: {1753 check_dir: {
1754 // workaround for windows, see https://github.com/ziglang/zig/issues/167381754 // workaround for windows, see https://github.com/ziglang/zig/issues/16738
1755 const fstat = dirStatPathWindows(t, dir, component.path, .{1755 const fstat = dirStatFileWindows(t, dir, component.path, .{
1756 .follow_symlinks = options.follow_symlinks,1756 .follow_symlinks = options.follow_symlinks,
1757 }) catch |stat_err| switch (stat_err) {1757 }) catch |stat_err| switch (stat_err) {
1758 error.IsDir => break :check_dir,1758 error.IsDir => break :check_dir,
...@@ -1806,19 +1806,19 @@ fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {...@@ -1806,19 +1806,19 @@ fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {
1806 return fileStat(t, file);1806 return fileStat(t, file);
1807}1807}
18081808
1809const dirStatPath = switch (native_os) {1809const dirStatFile = switch (native_os) {
1810 .linux => dirStatPathLinux,1810 .linux => dirStatFileLinux,
1811 .windows => dirStatPathWindows,1811 .windows => dirStatFileWindows,
1812 .wasi => dirStatPathWasi,1812 .wasi => dirStatFileWasi,
1813 else => dirStatPathPosix,1813 else => dirStatFilePosix,
1814};1814};
18151815
1816fn dirStatPathLinux(1816fn dirStatFileLinux(
1817 userdata: ?*anyopaque,1817 userdata: ?*anyopaque,
1818 dir: Dir,1818 dir: Dir,
1819 sub_path: []const u8,1819 sub_path: []const u8,
1820 options: Dir.StatPathOptions,1820 options: Dir.StatFileOptions,
1821) Dir.StatPathError!File.Stat {1821) Dir.StatFileError!File.Stat {
1822 const t: *Threaded = @ptrCast(@alignCast(userdata));1822 const t: *Threaded = @ptrCast(@alignCast(userdata));
1823 const current_thread = Thread.getCurrent(t);1823 const current_thread = Thread.getCurrent(t);
1824 const linux = std.os.linux;1824 const linux = std.os.linux;
...@@ -1875,12 +1875,12 @@ fn dirStatPathLinux(...@@ -1875,12 +1875,12 @@ fn dirStatPathLinux(
1875 }1875 }
1876}1876}
18771877
1878fn dirStatPathPosix(1878fn dirStatFilePosix(
1879 userdata: ?*anyopaque,1879 userdata: ?*anyopaque,
1880 dir: Dir,1880 dir: Dir,
1881 sub_path: []const u8,1881 sub_path: []const u8,
1882 options: Dir.StatPathOptions,1882 options: Dir.StatFileOptions,
1883) Dir.StatPathError!File.Stat {1883) Dir.StatFileError!File.Stat {
1884 const t: *Threaded = @ptrCast(@alignCast(userdata));1884 const t: *Threaded = @ptrCast(@alignCast(userdata));
1885 const current_thread = Thread.getCurrent(t);1885 const current_thread = Thread.getCurrent(t);
18861886
...@@ -1889,10 +1889,10 @@ fn dirStatPathPosix(...@@ -1889,10 +1889,10 @@ fn dirStatPathPosix(
18891889
1890 const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;1890 const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
18911891
1892 return posixStatPath(current_thread, dir.handle, sub_path_posix, flags);1892 return posixStatFile(current_thread, dir.handle, sub_path_posix, flags);
1893}1893}
18941894
1895fn posixStatPath(current_thread: *Thread, dir_fd: posix.fd_t, sub_path: [:0]const u8, flags: u32) Dir.StatPathError!File.Stat {1895fn posixStatFile(current_thread: *Thread, dir_fd: posix.fd_t, sub_path: [:0]const u8, flags: u32) Dir.StatFileError!File.Stat {
1896 try current_thread.beginSyscall();1896 try current_thread.beginSyscall();
1897 while (true) {1897 while (true) {
1898 var stat = std.mem.zeroes(posix.Stat);1898 var stat = std.mem.zeroes(posix.Stat);
...@@ -1927,12 +1927,12 @@ fn posixStatPath(current_thread: *Thread, dir_fd: posix.fd_t, sub_path: [:0]cons...@@ -1927,12 +1927,12 @@ fn posixStatPath(current_thread: *Thread, dir_fd: posix.fd_t, sub_path: [:0]cons
1927 }1927 }
1928}1928}
19291929
1930fn dirStatPathWindows(1930fn dirStatFileWindows(
1931 userdata: ?*anyopaque,1931 userdata: ?*anyopaque,
1932 dir: Dir,1932 dir: Dir,
1933 sub_path: []const u8,1933 sub_path: []const u8,
1934 options: Dir.StatPathOptions,1934 options: Dir.StatFileOptions,
1935) Dir.StatPathError!File.Stat {1935) Dir.StatFileError!File.Stat {
1936 const t: *Threaded = @ptrCast(@alignCast(userdata));1936 const t: *Threaded = @ptrCast(@alignCast(userdata));
1937 const file = try dirOpenFileWindows(t, dir, sub_path, .{1937 const file = try dirOpenFileWindows(t, dir, sub_path, .{
1938 .follow_symlinks = options.follow_symlinks,1938 .follow_symlinks = options.follow_symlinks,
...@@ -1941,13 +1941,13 @@ fn dirStatPathWindows(...@@ -1941,13 +1941,13 @@ fn dirStatPathWindows(
1941 return fileStatWindows(t, file);1941 return fileStatWindows(t, file);
1942}1942}
19431943
1944fn dirStatPathWasi(1944fn dirStatFileWasi(
1945 userdata: ?*anyopaque,1945 userdata: ?*anyopaque,
1946 dir: Dir,1946 dir: Dir,
1947 sub_path: []const u8,1947 sub_path: []const u8,
1948 options: Dir.StatPathOptions,1948 options: Dir.StatFileOptions,
1949) Dir.StatPathError!File.Stat {1949) Dir.StatFileError!File.Stat {
1950 if (builtin.link_libc) return dirStatPathPosix(userdata, dir, sub_path, options);1950 if (builtin.link_libc) return dirStatFilePosix(userdata, dir, sub_path, options);
1951 const t: *Threaded = @ptrCast(@alignCast(userdata));1951 const t: *Threaded = @ptrCast(@alignCast(userdata));
1952 const current_thread = Thread.getCurrent(t);1952 const current_thread = Thread.getCurrent(t);
1953 const wasi = std.os.wasi;1953 const wasi = std.os.wasi;
...@@ -3629,7 +3629,7 @@ fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D...@@ -3629,7 +3629,7 @@ fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D
3629 if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;3629 if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;
36303630
3631 // illumos dirent doesn't expose type, so we have to call stat to get it.3631 // illumos dirent doesn't expose type, so we have to call stat to get it.
3632 const stat = try posixStatPath(current_thread, dr.dir.handle, name, posix.AT.SYMLINK_NOFOLLOW);3632 const stat = try posixStatFile(current_thread, dr.dir.handle, name, posix.AT.SYMLINK_NOFOLLOW);
36333633
3634 buffer[buffer_index] = .{3634 buffer[buffer_index] = .{
3635 .name = name,3635 .name = name,
lib/std/fs/test.zig+30-27
...@@ -160,8 +160,8 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep:...@@ -160,8 +160,8 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep:
160160
161// For use in test setup. If the symlink creation fails on Windows with161// For use in test setup. If the symlink creation fails on Windows with
162// AccessDenied, then make the test failure silent (it is not a Zig failure).162// AccessDenied, then make the test failure silent (it is not a Zig failure).
163fn setupSymlink(dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {163fn setupSymlink(io: Io, dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
164 return dir.symLink(target, link, flags) catch |err| switch (err) {164 return dir.symLink(io, target, link, flags) catch |err| switch (err) {
165 // Symlink requires admin privileges on windows, so this test can legitimately fail.165 // Symlink requires admin privileges on windows, so this test can legitimately fail.
166 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,166 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,
167 else => return err,167 else => return err,
...@@ -193,15 +193,15 @@ test "Dir.readLink" {...@@ -193,15 +193,15 @@ test "Dir.readLink" {
193 const canonical_dir_target_path = try ctx.toCanonicalPathSep(dir_target_path);193 const canonical_dir_target_path = try ctx.toCanonicalPathSep(dir_target_path);
194194
195 // test 1: symlink to a file195 // test 1: symlink to a file
196 try setupSymlink(ctx.dir, file_target_path, "symlink1", .{});196 try setupSymlink(io, ctx.dir, file_target_path, "symlink1", .{});
197 try testReadLink(ctx.dir, canonical_file_target_path, "symlink1");197 try testReadLink(io, ctx.dir, canonical_file_target_path, "symlink1");
198 if (builtin.os.tag == .windows) {198 if (builtin.os.tag == .windows) {
199 try testReadLinkW(testing.allocator, ctx.dir, canonical_file_target_path, "symlink1");199 try testReadLinkW(testing.allocator, ctx.dir, canonical_file_target_path, "symlink1");
200 }200 }
201201
202 // test 2: symlink to a directory (can be different on Windows)202 // test 2: symlink to a directory (can be different on Windows)
203 try setupSymlink(ctx.dir, dir_target_path, "symlink2", .{ .is_directory = true });203 try setupSymlink(io, ctx.dir, dir_target_path, "symlink2", .{ .is_directory = true });
204 try testReadLink(ctx.dir, canonical_dir_target_path, "symlink2");204 try testReadLink(io, ctx.dir, canonical_dir_target_path, "symlink2");
205 if (builtin.os.tag == .windows) {205 if (builtin.os.tag == .windows) {
206 try testReadLinkW(testing.allocator, ctx.dir, canonical_dir_target_path, "symlink2");206 try testReadLinkW(testing.allocator, ctx.dir, canonical_dir_target_path, "symlink2");
207 }207 }
...@@ -211,8 +211,8 @@ test "Dir.readLink" {...@@ -211,8 +211,8 @@ test "Dir.readLink" {
211 const canonical_parent_file = try ctx.toCanonicalPathSep(parent_file);211 const canonical_parent_file = try ctx.toCanonicalPathSep(parent_file);
212 var subdir = try ctx.dir.makeOpenPath("subdir", .{});212 var subdir = try ctx.dir.makeOpenPath("subdir", .{});
213 defer subdir.close(io);213 defer subdir.close(io);
214 try setupSymlink(subdir, canonical_parent_file, "relative-link.txt", .{});214 try setupSymlink(io, subdir, canonical_parent_file, "relative-link.txt", .{});
215 try testReadLink(subdir, canonical_parent_file, "relative-link.txt");215 try testReadLink(io, subdir, canonical_parent_file, "relative-link.txt");
216 if (builtin.os.tag == .windows) {216 if (builtin.os.tag == .windows) {
217 try testReadLinkW(testing.allocator, subdir, canonical_parent_file, "relative-link.txt");217 try testReadLinkW(testing.allocator, subdir, canonical_parent_file, "relative-link.txt");
218 }218 }
...@@ -246,9 +246,9 @@ test "Dir.readLink on non-symlinks" {...@@ -246,9 +246,9 @@ test "Dir.readLink on non-symlinks" {
246 }.impl);246 }.impl);
247}247}
248248
249fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {249fn testReadLink(io: Io, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {
250 var buffer: [fs.max_path_bytes]u8 = undefined;250 var buffer: [fs.max_path_bytes]u8 = undefined;
251 const actual = try dir.readLink(symlink_path, buffer[0..]);251 const actual = try dir.readLink(io, symlink_path, &buffer);
252 try testing.expectEqualStrings(target_path, actual);252 try testing.expectEqualStrings(target_path, actual);
253}253}
254254
...@@ -284,7 +284,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {...@@ -284,7 +284,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {
284 const dir_target_path = try ctx.transformPath("subdir");284 const dir_target_path = try ctx.transformPath("subdir");
285 try ctx.dir.makeDir(io, dir_target_path, .default_dir);285 try ctx.dir.makeDir(io, dir_target_path, .default_dir);
286286
287 try setupSymlink(ctx.dir, dir_target_path, "symlink", .{ .is_directory = true });287 try setupSymlink(io, ctx.dir, dir_target_path, "symlink", .{ .is_directory = true });
288288
289 var symlink: Dir = switch (builtin.target.os.tag) {289 var symlink: Dir = switch (builtin.target.os.tag) {
290 .windows => windows_symlink: {290 .windows => windows_symlink: {
...@@ -809,11 +809,11 @@ test "Dir.statFile" {...@@ -809,11 +809,11 @@ test "Dir.statFile" {
809 const io = ctx.io;809 const io = ctx.io;
810 const test_file_name = try ctx.transformPath("test_file");810 const test_file_name = try ctx.transformPath("test_file");
811811
812 try testing.expectError(error.FileNotFound, ctx.dir.statFile(test_file_name));812 try testing.expectError(error.FileNotFound, ctx.dir.statFile(io, test_file_name, .{}));
813813
814 try ctx.dir.writeFile(io, .{ .sub_path = test_file_name, .data = "" });814 try ctx.dir.writeFile(io, .{ .sub_path = test_file_name, .data = "" });
815815
816 const stat = try ctx.dir.statFile(test_file_name);816 const stat = try ctx.dir.statFile(io, test_file_name, .{});
817 try testing.expectEqual(File.Kind.file, stat.kind);817 try testing.expectEqual(File.Kind.file, stat.kind);
818 }818 }
819 }.impl);819 }.impl);
...@@ -822,12 +822,13 @@ test "Dir.statFile" {...@@ -822,12 +822,13 @@ test "Dir.statFile" {
822test "statFile on dangling symlink" {822test "statFile on dangling symlink" {
823 try testWithAllSupportedPathTypes(struct {823 try testWithAllSupportedPathTypes(struct {
824 fn impl(ctx: *TestContext) !void {824 fn impl(ctx: *TestContext) !void {
825 const io = ctx.io;
825 const symlink_name = try ctx.transformPath("dangling-symlink");826 const symlink_name = try ctx.transformPath("dangling-symlink");
826 const symlink_target = "." ++ fs.path.sep_str ++ "doesnotexist";827 const symlink_target = "." ++ fs.path.sep_str ++ "doesnotexist";
827828
828 try setupSymlink(ctx.dir, symlink_target, symlink_name, .{});829 try setupSymlink(io, ctx.dir, symlink_target, symlink_name, .{});
829830
830 try std.testing.expectError(error.FileNotFound, ctx.dir.statFile(symlink_name));831 try std.testing.expectError(error.FileNotFound, ctx.dir.statFile(io, symlink_name, .{}));
831 }832 }
832 }.impl);833 }.impl);
833}834}
...@@ -1206,7 +1207,7 @@ test "deleteTree does not follow symlinks" {...@@ -1206,7 +1207,7 @@ test "deleteTree does not follow symlinks" {
1206 var a = try tmp.dir.makeOpenPath("a", .{});1207 var a = try tmp.dir.makeOpenPath("a", .{});
1207 defer a.close(io);1208 defer a.close(io);
12081209
1209 try setupSymlink(a, "../b", "b", .{ .is_directory = true });1210 try setupSymlink(io, a, "../b", "b", .{ .is_directory = true });
1210 }1211 }
12111212
1212 try tmp.dir.deleteTree(io, "a");1213 try tmp.dir.deleteTree(io, "a");
...@@ -1223,7 +1224,7 @@ test "deleteTree on a symlink" {...@@ -1223,7 +1224,7 @@ test "deleteTree on a symlink" {
12231224
1224 // Symlink to a file1225 // Symlink to a file
1225 try tmp.dir.writeFile(io, .{ .sub_path = "file", .data = "" });1226 try tmp.dir.writeFile(io, .{ .sub_path = "file", .data = "" });
1226 try setupSymlink(tmp.dir, "file", "filelink", .{});1227 try setupSymlink(io, tmp.dir, "file", "filelink", .{});
12271228
1228 try tmp.dir.deleteTree(io, "filelink");1229 try tmp.dir.deleteTree(io, "filelink");
1229 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "filelink", .{}));1230 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "filelink", .{}));
...@@ -1231,7 +1232,7 @@ test "deleteTree on a symlink" {...@@ -1231,7 +1232,7 @@ test "deleteTree on a symlink" {
12311232
1232 // Symlink to a directory1233 // Symlink to a directory
1233 try tmp.dir.makePath(io, "dir");1234 try tmp.dir.makePath(io, "dir");
1234 try setupSymlink(tmp.dir, "dir", "dirlink", .{ .is_directory = true });1235 try setupSymlink(io, tmp.dir, "dir", "dirlink", .{ .is_directory = true });
12351236
1236 try tmp.dir.deleteTree(io, "dirlink");1237 try tmp.dir.deleteTree(io, "dirlink");
1237 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "dirlink", .{}));1238 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "dirlink", .{}));
...@@ -1337,7 +1338,7 @@ test "makepath through existing valid symlink" {...@@ -1337,7 +1338,7 @@ test "makepath through existing valid symlink" {
1337 defer tmp.cleanup();1338 defer tmp.cleanup();
13381339
1339 try tmp.dir.makeDir(io, "realfolder", .default_dir);1340 try tmp.dir.makeDir(io, "realfolder", .default_dir);
1340 try setupSymlink(tmp.dir, "." ++ fs.path.sep_str ++ "realfolder", "working-symlink", .{});1341 try setupSymlink(io, tmp.dir, "." ++ fs.path.sep_str ++ "realfolder", "working-symlink", .{});
13411342
1342 try tmp.dir.makePath(io, "working-symlink" ++ fs.path.sep_str ++ "in-realfolder");1343 try tmp.dir.makePath(io, "working-symlink" ++ fs.path.sep_str ++ "in-realfolder");
13431344
...@@ -2178,12 +2179,12 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2178,12 +2179,12 @@ test "invalid UTF-8/WTF-8 paths" {
21782179
2179 try testing.expectError(expected_err, ctx.dir.rename(invalid_path, ctx.dir, invalid_path, io));2180 try testing.expectError(expected_err, ctx.dir.rename(invalid_path, ctx.dir, invalid_path, io));
21802181
2181 try testing.expectError(expected_err, ctx.dir.symLink(invalid_path, invalid_path, .{}));2182 try testing.expectError(expected_err, ctx.dir.symLink(io, invalid_path, invalid_path, .{}));
2182 if (native_os == .wasi) {2183 if (native_os == .wasi) {
2183 try testing.expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{}));2184 try testing.expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{}));
2184 }2185 }
21852186
2186 try testing.expectError(expected_err, ctx.dir.readLink(invalid_path, &[_]u8{}));2187 try testing.expectError(expected_err, ctx.dir.readLink(io, invalid_path, &[_]u8{}));
2187 if (native_os == .wasi) {2188 if (native_os == .wasi) {
2188 try testing.expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{}));2189 try testing.expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{}));
2189 }2190 }
...@@ -2386,14 +2387,16 @@ test "File.Writer sendfile with buffered contents" {...@@ -2386,14 +2387,16 @@ test "File.Writer sendfile with buffered contents" {
2386test "readlink on Windows" {2387test "readlink on Windows" {
2387 if (native_os != .windows) return error.SkipZigTest;2388 if (native_os != .windows) return error.SkipZigTest;
23882389
2389 try testReadlink("C:\\ProgramData", "C:\\Users\\All Users");2390 const io = testing.io;
2390 try testReadlink("C:\\Users\\Default", "C:\\Users\\Default User");2391
2391 try testReadlink("C:\\Users", "C:\\Documents and Settings");2392 try testReadLinkWindows(io, "C:\\ProgramData", "C:\\Users\\All Users");
2393 try testReadLinkWindows(io, "C:\\Users\\Default", "C:\\Users\\Default User");
2394 try testReadLinkWindows(io, "C:\\Users", "C:\\Documents and Settings");
2392}2395}
23932396
2394fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {2397fn testReadLinkWindows(io: Io, target_path: []const u8, symlink_path: []const u8) !void {
2395 var buffer: [fs.max_path_bytes]u8 = undefined;2398 var buffer: [fs.max_path_bytes]u8 = undefined;
2396 const given = try Dir.readLinkAbsolute(symlink_path, buffer[0..]);2399 const given = try Dir.readLinkAbsolute(io, symlink_path, &buffer);
2397 try expect(mem.eql(u8, target_path, given));2400 try expect(mem.eql(u8, target_path, given));
2398}2401}
23992402
...@@ -2424,6 +2427,6 @@ test "readlinkat" {...@@ -2424,6 +2427,6 @@ test "readlinkat" {
24242427
2425 // read the link2428 // read the link
2426 var buffer: [fs.max_path_bytes]u8 = undefined;2429 var buffer: [fs.max_path_bytes]u8 = undefined;
2427 const read_link = try tmp.dir.readLink("link", &buffer);2430 const read_link = try tmp.dir.readLink(io, "link", &buffer);
2428 try expect(mem.eql(u8, "file.txt", read_link));2431 try expect(mem.eql(u8, "file.txt", read_link));
2429}2432}
lib/std/tar.zig+23-31
...@@ -653,11 +653,11 @@ fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, permissions: Io....@@ -653,11 +653,11 @@ fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, permissions: Io.
653653
654// Creates a symbolic link at path `file_name` which points to `link_name`.654// Creates a symbolic link at path `file_name` which points to `link_name`.
655fn createDirAndSymlink(io: Io, dir: Io.Dir, link_name: []const u8, file_name: []const u8) !void {655fn createDirAndSymlink(io: Io, dir: Io.Dir, link_name: []const u8, file_name: []const u8) !void {
656 dir.symLink(link_name, file_name, .{}) catch |err| {656 dir.symLink(io, link_name, file_name, .{}) catch |err| {
657 if (err == error.FileNotFound) {657 if (err == error.FileNotFound) {
658 if (std.fs.path.dirname(file_name)) |dir_name| {658 if (std.fs.path.dirname(file_name)) |dir_name| {
659 try dir.makePath(io, dir_name);659 try dir.makePath(io, dir_name);
660 return try dir.symLink(link_name, file_name, .{});660 return try dir.symLink(io, link_name, file_name, .{});
661 }661 }
662 }662 }
663 return err;663 return err;
...@@ -996,15 +996,15 @@ test pipeToFileSystem {...@@ -996,15 +996,15 @@ test pipeToFileSystem {
996 return err;996 return err;
997 };997 };
998998
999 try testing.expectError(error.FileNotFound, dir.statFile("empty"));999 try testing.expectError(error.FileNotFound, dir.statFile(io, "empty", .{}));
1000 try testing.expect((try dir.statFile("a/file")).kind == .file);1000 try testing.expect((try dir.statFile(io, "a/file", .{})).kind == .file);
1001 try testing.expect((try dir.statFile("b/symlink")).kind == .file); // statFile follows symlink1001 try testing.expect((try dir.statFile(io, "b/symlink", .{})).kind == .file); // statFile follows symlink
10021002
1003 var buf: [32]u8 = undefined;1003 var buf: [32]u8 = undefined;
1004 try testing.expectEqualSlices(1004 try testing.expectEqualSlices(
1005 u8,1005 u8,
1006 "../a/file",1006 "../a/file",
1007 normalizePath(try dir.readLink("b/symlink", &buf)),1007 normalizePath(buf[0..try dir.readLink(io, "b/symlink", &buf)]),
1008 );1008 );
1009}1009}
10101010
...@@ -1120,28 +1120,18 @@ fn normalizePath(bytes: []u8) []u8 {...@@ -1120,28 +1120,18 @@ fn normalizePath(bytes: []u8) []u8 {
11201120
1121// File system mode based on tar header mode and mode_mode options.1121// File system mode based on tar header mode and mode_mode options.
1122fn filePermissions(mode: u32, options: PipeOptions) Io.File.Permissions {1122fn filePermissions(mode: u32, options: PipeOptions) Io.File.Permissions {
1123 const default_mode = 0o666;1123 return if (!Io.File.Permissions.has_executable_bit or options.mode_mode == .ignore or (mode & 0o100) == 0)
11241124 .default_file
1125 if (!Io.File.Permissions.has_executable_bit or options.mode_mode == .ignore)1125 else
1126 return .fromMode(default_mode);1126 .executable_file;
1127
1128 const S = std.posix.S;
1129
1130 // The mode from the tar file is inspected for the owner executable bit.
1131 if (mode & S.IXUSR == 0)
1132 return .fromMode(default_mode);
1133
1134 // This bit is copied to the group and other executable bits.
1135 // Other bits of the mode are left as the default when creating files.
1136 return .fromMode(default_mode | S.IXUSR | S.IXGRP | S.IXOTH);
1137}1127}
11381128
1139test filePermissions {1129test filePermissions {
1140 if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest;1130 if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest;
1141 try testing.expectEqual(0o666, filePermissions(0o744, PipeOptions{ .mode_mode = .ignore }));1131 try testing.expectEqual(.default_file, filePermissions(0o744, .{ .mode_mode = .ignore }));
1142 try testing.expectEqual(0o777, filePermissions(0o744, PipeOptions{}));1132 try testing.expectEqual(.executable_file, filePermissions(0o744, .{}));
1143 try testing.expectEqual(0o666, filePermissions(0o644, PipeOptions{}));1133 try testing.expectEqual(.default_file, filePermissions(0o644, .{}));
1144 try testing.expectEqual(0o666, filePermissions(0o655, PipeOptions{}));1134 try testing.expectEqual(.default_file, filePermissions(0o655, .{}));
1145}1135}
11461136
1147test "executable bit" {1137test "executable bit" {
...@@ -1167,19 +1157,21 @@ test "executable bit" {...@@ -1167,19 +1157,21 @@ test "executable bit" {
1167 return err;1157 return err;
1168 };1158 };
11691159
1170 const fs = try tmp.dir.statFile("a/file");1160 const fs = try tmp.dir.statFile(io, "a/file", .{});
1171 try testing.expect(fs.kind == .file);1161 try testing.expect(fs.kind == .file);
11721162
1163 const mode = fs.permissions.toMode();
1164
1173 if (opt == .executable_bit_only) {1165 if (opt == .executable_bit_only) {
1174 // Executable bit is set for user, group and others1166 // Executable bit is set for user, group and others
1175 try testing.expect(fs.mode & S.IXUSR > 0);1167 try testing.expect(mode & S.IXUSR > 0);
1176 try testing.expect(fs.mode & S.IXGRP > 0);1168 try testing.expect(mode & S.IXGRP > 0);
1177 try testing.expect(fs.mode & S.IXOTH > 0);1169 try testing.expect(mode & S.IXOTH > 0);
1178 }1170 }
1179 if (opt == .ignore) {1171 if (opt == .ignore) {
1180 try testing.expect(fs.mode & S.IXUSR == 0);1172 try testing.expect(mode & S.IXUSR == 0);
1181 try testing.expect(fs.mode & S.IXGRP == 0);1173 try testing.expect(mode & S.IXGRP == 0);
1182 try testing.expect(fs.mode & S.IXOTH == 0);1174 try testing.expect(mode & S.IXOTH == 0);
1183 }1175 }
1184 }1176 }
1185}1177}
lib/std/tar/test.zig+2-2
...@@ -504,6 +504,6 @@ test "case sensitivity" {...@@ -504,6 +504,6 @@ test "case sensitivity" {
504 };504 };
505505
506 // on case sensitive os both files are created506 // on case sensitive os both files are created
507 try testing.expect((try root.dir.statFile("alacritty/darkermatrix.yml")).kind == .file);507 try testing.expect((try root.dir.statFile(io, "alacritty/darkermatrix.yml", .{})).kind == .file);
508 try testing.expect((try root.dir.statFile("alacritty/Darkermatrix.yml")).kind == .file);508 try testing.expect((try root.dir.statFile(io, "alacritty/Darkermatrix.yml", .{})).kind == .file);
509}509}
lib/std/zig/WindowsSdk.zig+1-1
...@@ -1012,7 +1012,7 @@ const MsvcLibDir = struct {...@@ -1012,7 +1012,7 @@ const MsvcLibDir = struct {
1012 var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false;1012 var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false;
1013 defer dir.close(io);1013 defer dir.close(io);
10141014
1015 const stat = dir.statFile("vcruntime.lib") catch return false;1015 const stat = dir.statFile(io, "vcruntime.lib", .{}) catch return false;
1016 if (stat.kind != .file)1016 if (stat.kind != .file)
1017 return false;1017 return false;
10181018
lib/std/zig/parser_test.zig+1-1
...@@ -4539,7 +4539,7 @@ test "zig fmt: Only indent multiline string literals in function calls" {...@@ -4539,7 +4539,7 @@ test "zig fmt: Only indent multiline string literals in function calls" {
4539test "zig fmt: Don't add extra newline after if" {4539test "zig fmt: Don't add extra newline after if" {
4540 try testCanonical(4540 try testCanonical(
4541 \\pub fn atomicSymLink(allocator: Allocator, existing_path: []const u8, new_path: []const u8) !void {4541 \\pub fn atomicSymLink(allocator: Allocator, existing_path: []const u8, new_path: []const u8) !void {
4542 \\ if (cwd().symLink(existing_path, new_path, .{})) {4542 \\ if (foo().bar(existing_path, new_path, .{})) {
4543 \\ return;4543 \\ return;
4544 \\ }4544 \\ }
4545 \\}4545 \\}
src/Builtin.zig+2-1
...@@ -313,8 +313,9 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {...@@ -313,8 +313,9 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {
313 assert(file.source != null);313 assert(file.source != null);
314314
315 const root_dir, const sub_path = file.path.openInfo(comp.dirs);315 const root_dir, const sub_path = file.path.openInfo(comp.dirs);
316 const io = comp.io;
316317
317 if (root_dir.statFile(sub_path)) |stat| {318 if (root_dir.statFile(io, sub_path, .{})) |stat| {
318 if (stat.size != file.source.?.len) {319 if (stat.size != file.source.?.len) {
319 std.log.warn(320 std.log.warn(
320 "the cached file '{f}' had the wrong size. Expected {d}, found {d}. " ++321 "the cached file '{f}' had the wrong size. Expected {d}, found {d}. " ++
src/Package/Fetch.zig+10-10
...@@ -1440,13 +1440,13 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void...@@ -1440,13 +1440,13 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void
1440 },1440 },
1441 .sym_link => {1441 .sym_link => {
1442 var buf: [fs.max_path_bytes]u8 = undefined;1442 var buf: [fs.max_path_bytes]u8 = undefined;
1443 const link_name = try dir.readLink(entry.path, &buf);1443 const link_name = try dir.readLink(io, entry.path, &buf);
1444 // TODO: if this would create a symlink to outside1444 // TODO: if this would create a symlink to outside
1445 // the destination directory, fail with an error instead.1445 // the destination directory, fail with an error instead.
1446 tmp_dir.symLink(link_name, entry.path, .{}) catch |err| switch (err) {1446 tmp_dir.symLink(io, link_name, entry.path, .{}) catch |err| switch (err) {
1447 error.FileNotFound => {1447 error.FileNotFound => {
1448 if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(io, dirname);1448 if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(io, dirname);
1449 try tmp_dir.symLink(link_name, entry.path, .{});1449 try tmp_dir.symLink(io, link_name, entry.path, .{});
1450 },1450 },
1451 else => |e| return e,1451 else => |e| return e,
1452 };1452 };
...@@ -1698,7 +1698,7 @@ fn hashFileFallible(io: Io, dir: Io.Dir, hashed_file: *HashedFile) HashedFile.Er...@@ -1698,7 +1698,7 @@ fn hashFileFallible(io: Io, dir: Io.Dir, hashed_file: *HashedFile) HashedFile.Er
1698 }1698 }
1699 },1699 },
1700 .link => {1700 .link => {
1701 const link_name = try dir.readLink(hashed_file.fs_path, &buf);1701 const link_name = try dir.readLink(io, hashed_file.fs_path, &buf);
1702 if (fs.path.sep != canonical_sep) {1702 if (fs.path.sep != canonical_sep) {
1703 // Package hashes are intended to be consistent across1703 // Package hashes are intended to be consistent across
1704 // platforms which means we must normalize path separators1704 // platforms which means we must normalize path separators
...@@ -2218,13 +2218,13 @@ test "set executable bit based on file content" {...@@ -2218,13 +2218,13 @@ test "set executable bit based on file content" {
2218 defer out.close(io);2218 defer out.close(io);
2219 const S = std.posix.S;2219 const S = std.posix.S;
2220 // expect executable bit not set2220 // expect executable bit not set
2221 try std.testing.expect((try out.statFile("file1")).mode & S.IXUSR == 0);2221 try std.testing.expect((try out.statFile(io, "file1", .{})).mode & S.IXUSR == 0);
2222 try std.testing.expect((try out.statFile("script_without_shebang")).mode & S.IXUSR == 0);2222 try std.testing.expect((try out.statFile(io, "script_without_shebang", .{})).mode & S.IXUSR == 0);
2223 // expect executable bit set2223 // expect executable bit set
2224 try std.testing.expect((try out.statFile("hello")).mode & S.IXUSR != 0);2224 try std.testing.expect((try out.statFile(io, "hello", .{})).mode & S.IXUSR != 0);
2225 try std.testing.expect((try out.statFile("script")).mode & S.IXUSR != 0);2225 try std.testing.expect((try out.statFile(io, "script", .{})).mode & S.IXUSR != 0);
2226 try std.testing.expect((try out.statFile("script_with_shebang_without_exec_bit")).mode & S.IXUSR != 0);2226 try std.testing.expect((try out.statFile(io, "script_with_shebang_without_exec_bit", .{})).mode & S.IXUSR != 0);
2227 try std.testing.expect((try out.statFile("hello_ln")).mode & S.IXUSR != 0);2227 try std.testing.expect((try out.statFile(io, "hello_ln", .{})).mode & S.IXUSR != 0);
22282228
2229 //2229 //
2230 // $ ls -al zig-cache/tmp/OCz9ovUcstDjTC_U/zig-global-cache/p/1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef32230 // $ ls -al zig-cache/tmp/OCz9ovUcstDjTC_U/zig-global-cache/p/1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef3
src/Package/Fetch/git.zig+1-1
...@@ -281,7 +281,7 @@ pub const Repository = struct {...@@ -281,7 +281,7 @@ pub const Repository = struct {
281 const symlink_object = try repository.odb.readObject();281 const symlink_object = try repository.odb.readObject();
282 if (symlink_object.type != .blob) return error.InvalidFile;282 if (symlink_object.type != .blob) return error.InvalidFile;
283 const link_name = symlink_object.data;283 const link_name = symlink_object.data;
284 dir.symLink(link_name, entry.name, .{}) catch |e| {284 dir.symLink(io, link_name, entry.name, .{}) catch |e| {
285 const file_name = try std.fs.path.join(diagnostics.allocator, &.{ current_path, entry.name });285 const file_name = try std.fs.path.join(diagnostics.allocator, &.{ current_path, entry.name });
286 errdefer diagnostics.allocator.free(file_name);286 errdefer diagnostics.allocator.free(file_name);
287 const link_name_dup = try diagnostics.allocator.dupe(u8, link_name);287 const link_name_dup = try diagnostics.allocator.dupe(u8, link_name);
src/fmt.zig+1-1
...@@ -182,7 +182,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !...@@ -182,7 +182,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !
182 // Mark any excluded files/directories as already seen,182 // Mark any excluded files/directories as already seen,
183 // so that they are skipped later during actual processing183 // so that they are skipped later during actual processing
184 for (excluded_files.items) |file_path| {184 for (excluded_files.items) |file_path| {
185 const stat = Io.Dir.cwd().statFile(file_path) catch |err| switch (err) {185 const stat = Io.Dir.cwd().statFile(io, file_path, .{}) catch |err| switch (err) {
186 error.FileNotFound => continue,186 error.FileNotFound => continue,
187 // On Windows, statFile does not work for directories187 // On Windows, statFile does not work for directories
188 error.IsDir => dir: {188 error.IsDir => dir: {