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 {
21622162}
21632163
21642164pub fn getSourceMTimeUncached(comp: *const Compilation, source_id: Source.Id) ?u64 {
2165 const io = comp.io;
21652166 const source = comp.getSource(source_id);
2166 if (comp.cwd.statFile(source.path)) |stat| {
2167 if (comp.cwd.statFile(io, source.path, .{})) |stat| {
21672168 return std.math.cast(u64, stat.mtime.toSeconds());
21682169 } else |_| {
21692170 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
9494 return p.root_dir.handle.makeOpenPath(joined_path, opts);
9595}
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 {
9898 var buf: [fs.max_path_bytes]u8 = undefined;
9999 const joined_path = if (p.sub_path.len == 0) sub_path else p: {
100100 break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
101101 p.sub_path, sub_path,
102102 }) catch return error.NameTooLong;
103103 };
104 return p.root_dir.handle.statFile(joined_path);
104 return p.root_dir.handle.statFile(io, joined_path, .{});
105105}
106106
107107pub fn atomicFile(
lib/std/Io.zig+1-1
......@@ -671,7 +671,7 @@ pub const VTable = struct {
671671 dirMakeOpenPath: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions, Dir.OpenOptions) Dir.MakeOpenPathError!Dir,
672672 dirOpenDir: *const fn (?*anyopaque, Dir, []const u8, Dir.OpenOptions) Dir.OpenError!Dir,
673673 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,
675675 dirAccess: *const fn (?*anyopaque, Dir, []const u8, Dir.AccessOptions) Dir.AccessError!void,
676676 dirCreateFile: *const fn (?*anyopaque, Dir, []const u8, File.CreateFlags) File.OpenError!File,
677677 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
669669
670670test makeDirAbsolute {}
671671
672pub const MakePathError = MakeError || StatPathError;
672pub const MakePathError = MakeError || StatFileError;
673673
674674/// Creates parent directories with default permissions as necessary to ensure
675675/// `sub_path` exists as a directory.
......@@ -708,7 +708,7 @@ pub fn makePathStatus(dir: Dir, io: Io, sub_path: []const u8, permissions: Permi
708708 return io.vtable.dirMakePath(io.userdata, dir, sub_path, permissions);
709709}
710710
711pub const MakeOpenPathError = MakeError || OpenError || StatPathError;
711pub const MakeOpenPathError = MakeError || OpenError || StatFileError;
712712
713713pub const MakeOpenPathOptions = struct {
714714 open_options: OpenOptions = .{},
......@@ -734,9 +734,9 @@ pub fn stat(dir: Dir, io: Io) StatError!Stat {
734734 return io.vtable.dirStat(io.userdata, dir);
735735}
736736
737pub const StatPathError = File.OpenError || File.StatError;
737pub const StatFileError = File.OpenError || File.StatError;
738738
739pub const StatPathOptions = struct {
739pub const StatFileOptions = struct {
740740 follow_symlinks: bool = true,
741741};
742742
......@@ -752,8 +752,8 @@ pub const StatPathOptions = struct {
752752/// * On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
753753/// * On WASI, `sub_path` should be encoded as valid UTF-8.
754754/// * 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 {
756 return io.vtable.dirStatPath(io.userdata, dir, sub_path, options);
755pub fn statFile(dir: Dir, io: Io, sub_path: []const u8, options: StatFileOptions) StatFileError!Stat {
756 return io.vtable.dirStatFile(io.userdata, dir, sub_path, options);
757757}
758758
759759pub const RealPathError = error{
lib/std/Io/File.zig+3
......@@ -372,6 +372,7 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(
372372 _,
373373
374374 pub const default_dir: @This() = .default_file;
375 pub const executable_file: @This() = .default_file;
375376 pub const has_executable_bit = false;
376377
377378 const windows = std.os.windows;
......@@ -401,6 +402,7 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(
401402 /// process-scoped "umask" setting to adjust this number for file creation.
402403 default_file = 0o666,
403404 default_dir = 0o755,
405 executable_file = 0o777,
404406 _,
405407
406408 pub const has_executable_bit = true;
......@@ -428,6 +430,7 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(
428430} else enum(u0) {
429431 default_file = 0,
430432 pub const default_dir: @This() = .default_file;
433 pub const executable_file: @This() = .default_file;
431434 pub const has_executable_bit = false;
432435};
433436
lib/std/Io/Threaded.zig+25-25
......@@ -708,7 +708,7 @@ pub fn io(t: *Threaded) Io {
708708 .dirMakePath = dirMakePath,
709709 .dirMakeOpenPath = dirMakeOpenPath,
710710 .dirStat = dirStat,
711 .dirStatPath = dirStatPath,
711 .dirStatFile = dirStatFile,
712712 .dirAccess = dirAccess,
713713 .dirCreateFile = dirCreateFile,
714714 .dirOpenFile = dirOpenFile,
......@@ -840,7 +840,7 @@ pub fn ioBasic(t: *Threaded) Io {
840840 .dirMakePath = dirMakePath,
841841 .dirMakeOpenPath = dirMakeOpenPath,
842842 .dirStat = dirStat,
843 .dirStatPath = dirStatPath,
843 .dirStatFile = dirStatFile,
844844 .dirAccess = dirAccess,
845845 .dirCreateFile = dirCreateFile,
846846 .dirOpenFile = dirOpenFile,
......@@ -1623,7 +1623,7 @@ fn dirMakePath(
16231623 // could cause an infinite loop
16241624 check_dir: {
16251625 // 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) {
16271627 error.IsDir => break :check_dir,
16281628 else => |e| return e,
16291629 };
......@@ -1752,7 +1752,7 @@ fn dirMakeOpenPathWindows(
17521752 // could cause an infinite loop
17531753 check_dir: {
17541754 // 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, .{
17561756 .follow_symlinks = options.follow_symlinks,
17571757 }) catch |stat_err| switch (stat_err) {
17581758 error.IsDir => break :check_dir,
......@@ -1806,19 +1806,19 @@ fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {
18061806 return fileStat(t, file);
18071807}
18081808
1809const dirStatPath = switch (native_os) {
1810 .linux => dirStatPathLinux,
1811 .windows => dirStatPathWindows,
1812 .wasi => dirStatPathWasi,
1813 else => dirStatPathPosix,
1809const dirStatFile = switch (native_os) {
1810 .linux => dirStatFileLinux,
1811 .windows => dirStatFileWindows,
1812 .wasi => dirStatFileWasi,
1813 else => dirStatFilePosix,
18141814};
18151815
1816fn dirStatPathLinux(
1816fn dirStatFileLinux(
18171817 userdata: ?*anyopaque,
18181818 dir: Dir,
18191819 sub_path: []const u8,
1820 options: Dir.StatPathOptions,
1821) Dir.StatPathError!File.Stat {
1820 options: Dir.StatFileOptions,
1821) Dir.StatFileError!File.Stat {
18221822 const t: *Threaded = @ptrCast(@alignCast(userdata));
18231823 const current_thread = Thread.getCurrent(t);
18241824 const linux = std.os.linux;
......@@ -1875,12 +1875,12 @@ fn dirStatPathLinux(
18751875 }
18761876}
18771877
1878fn dirStatPathPosix(
1878fn dirStatFilePosix(
18791879 userdata: ?*anyopaque,
18801880 dir: Dir,
18811881 sub_path: []const u8,
1882 options: Dir.StatPathOptions,
1883) Dir.StatPathError!File.Stat {
1882 options: Dir.StatFileOptions,
1883) Dir.StatFileError!File.Stat {
18841884 const t: *Threaded = @ptrCast(@alignCast(userdata));
18851885 const current_thread = Thread.getCurrent(t);
18861886
......@@ -1889,10 +1889,10 @@ fn dirStatPathPosix(
18891889
18901890 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);
18931893}
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 {
18961896 try current_thread.beginSyscall();
18971897 while (true) {
18981898 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
19271927 }
19281928}
19291929
1930fn dirStatPathWindows(
1930fn dirStatFileWindows(
19311931 userdata: ?*anyopaque,
19321932 dir: Dir,
19331933 sub_path: []const u8,
1934 options: Dir.StatPathOptions,
1935) Dir.StatPathError!File.Stat {
1934 options: Dir.StatFileOptions,
1935) Dir.StatFileError!File.Stat {
19361936 const t: *Threaded = @ptrCast(@alignCast(userdata));
19371937 const file = try dirOpenFileWindows(t, dir, sub_path, .{
19381938 .follow_symlinks = options.follow_symlinks,
......@@ -1941,13 +1941,13 @@ fn dirStatPathWindows(
19411941 return fileStatWindows(t, file);
19421942}
19431943
1944fn dirStatPathWasi(
1944fn dirStatFileWasi(
19451945 userdata: ?*anyopaque,
19461946 dir: Dir,
19471947 sub_path: []const u8,
1948 options: Dir.StatPathOptions,
1949) Dir.StatPathError!File.Stat {
1950 if (builtin.link_libc) return dirStatPathPosix(userdata, dir, sub_path, options);
1948 options: Dir.StatFileOptions,
1949) Dir.StatFileError!File.Stat {
1950 if (builtin.link_libc) return dirStatFilePosix(userdata, dir, sub_path, options);
19511951 const t: *Threaded = @ptrCast(@alignCast(userdata));
19521952 const current_thread = Thread.getCurrent(t);
19531953 const wasi = std.os.wasi;
......@@ -3629,7 +3629,7 @@ fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D
36293629 if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;
36303630
36313631 // 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
36343634 buffer[buffer_index] = .{
36353635 .name = name,
lib/std/fs/test.zig+30-27
......@@ -160,8 +160,8 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep:
160160
161161// For use in test setup. If the symlink creation fails on Windows with
162162// 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 {
164 return dir.symLink(target, link, flags) catch |err| switch (err) {
163fn setupSymlink(io: Io, dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
164 return dir.symLink(io, target, link, flags) catch |err| switch (err) {
165165 // Symlink requires admin privileges on windows, so this test can legitimately fail.
166166 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,
167167 else => return err,
......@@ -193,15 +193,15 @@ test "Dir.readLink" {
193193 const canonical_dir_target_path = try ctx.toCanonicalPathSep(dir_target_path);
194194
195195 // test 1: symlink to a file
196 try setupSymlink(ctx.dir, file_target_path, "symlink1", .{});
197 try testReadLink(ctx.dir, canonical_file_target_path, "symlink1");
196 try setupSymlink(io, ctx.dir, file_target_path, "symlink1", .{});
197 try testReadLink(io, ctx.dir, canonical_file_target_path, "symlink1");
198198 if (builtin.os.tag == .windows) {
199199 try testReadLinkW(testing.allocator, ctx.dir, canonical_file_target_path, "symlink1");
200200 }
201201
202202 // test 2: symlink to a directory (can be different on Windows)
203 try setupSymlink(ctx.dir, dir_target_path, "symlink2", .{ .is_directory = true });
204 try testReadLink(ctx.dir, canonical_dir_target_path, "symlink2");
203 try setupSymlink(io, ctx.dir, dir_target_path, "symlink2", .{ .is_directory = true });
204 try testReadLink(io, ctx.dir, canonical_dir_target_path, "symlink2");
205205 if (builtin.os.tag == .windows) {
206206 try testReadLinkW(testing.allocator, ctx.dir, canonical_dir_target_path, "symlink2");
207207 }
......@@ -211,8 +211,8 @@ test "Dir.readLink" {
211211 const canonical_parent_file = try ctx.toCanonicalPathSep(parent_file);
212212 var subdir = try ctx.dir.makeOpenPath("subdir", .{});
213213 defer subdir.close(io);
214 try setupSymlink(subdir, canonical_parent_file, "relative-link.txt", .{});
215 try testReadLink(subdir, canonical_parent_file, "relative-link.txt");
214 try setupSymlink(io, subdir, canonical_parent_file, "relative-link.txt", .{});
215 try testReadLink(io, subdir, canonical_parent_file, "relative-link.txt");
216216 if (builtin.os.tag == .windows) {
217217 try testReadLinkW(testing.allocator, subdir, canonical_parent_file, "relative-link.txt");
218218 }
......@@ -246,9 +246,9 @@ test "Dir.readLink on non-symlinks" {
246246 }.impl);
247247}
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 {
250250 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);
252252 try testing.expectEqualStrings(target_path, actual);
253253}
254254
......@@ -284,7 +284,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {
284284 const dir_target_path = try ctx.transformPath("subdir");
285285 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
289289 var symlink: Dir = switch (builtin.target.os.tag) {
290290 .windows => windows_symlink: {
......@@ -809,11 +809,11 @@ test "Dir.statFile" {
809809 const io = ctx.io;
810810 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
814814 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, .{});
817817 try testing.expectEqual(File.Kind.file, stat.kind);
818818 }
819819 }.impl);
......@@ -822,12 +822,13 @@ test "Dir.statFile" {
822822test "statFile on dangling symlink" {
823823 try testWithAllSupportedPathTypes(struct {
824824 fn impl(ctx: *TestContext) !void {
825 const io = ctx.io;
825826 const symlink_name = try ctx.transformPath("dangling-symlink");
826827 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, .{}));
831832 }
832833 }.impl);
833834}
......@@ -1206,7 +1207,7 @@ test "deleteTree does not follow symlinks" {
12061207 var a = try tmp.dir.makeOpenPath("a", .{});
12071208 defer a.close(io);
12081209
1209 try setupSymlink(a, "../b", "b", .{ .is_directory = true });
1210 try setupSymlink(io, a, "../b", "b", .{ .is_directory = true });
12101211 }
12111212
12121213 try tmp.dir.deleteTree(io, "a");
......@@ -1223,7 +1224,7 @@ test "deleteTree on a symlink" {
12231224
12241225 // Symlink to a file
12251226 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
12281229 try tmp.dir.deleteTree(io, "filelink");
12291230 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "filelink", .{}));
......@@ -1231,7 +1232,7 @@ test "deleteTree on a symlink" {
12311232
12321233 // Symlink to a directory
12331234 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
12361237 try tmp.dir.deleteTree(io, "dirlink");
12371238 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "dirlink", .{}));
......@@ -1337,7 +1338,7 @@ test "makepath through existing valid symlink" {
13371338 defer tmp.cleanup();
13381339
13391340 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
13421343 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" {
21782179
21792180 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, .{}));
21822183 if (native_os == .wasi) {
21832184 try testing.expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{}));
21842185 }
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{}));
21872188 if (native_os == .wasi) {
21882189 try testing.expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{}));
21892190 }
......@@ -2386,14 +2387,16 @@ test "File.Writer sendfile with buffered contents" {
23862387test "readlink on Windows" {
23872388 if (native_os != .windows) return error.SkipZigTest;
23882389
2389 try testReadlink("C:\\ProgramData", "C:\\Users\\All Users");
2390 try testReadlink("C:\\Users\\Default", "C:\\Users\\Default User");
2391 try testReadlink("C:\\Users", "C:\\Documents and Settings");
2390 const io = testing.io;
2391
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");
23922395}
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 {
23952398 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);
23972400 try expect(mem.eql(u8, target_path, given));
23982401}
23992402
......@@ -2424,6 +2427,6 @@ test "readlinkat" {
24242427
24252428 // read the link
24262429 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);
24282431 try expect(mem.eql(u8, "file.txt", read_link));
24292432}
lib/std/tar.zig+23-31
......@@ -653,11 +653,11 @@ fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, permissions: Io.
653653
654654// Creates a symbolic link at path `file_name` which points to `link_name`.
655655fn 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| {
657657 if (err == error.FileNotFound) {
658658 if (std.fs.path.dirname(file_name)) |dir_name| {
659659 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, .{});
661661 }
662662 }
663663 return err;
......@@ -996,15 +996,15 @@ test pipeToFileSystem {
996996 return err;
997997 };
998998
999 try testing.expectError(error.FileNotFound, dir.statFile("empty"));
1000 try testing.expect((try dir.statFile("a/file")).kind == .file);
1001 try testing.expect((try dir.statFile("b/symlink")).kind == .file); // statFile follows symlink
999 try testing.expectError(error.FileNotFound, dir.statFile(io, "empty", .{}));
1000 try testing.expect((try dir.statFile(io, "a/file", .{})).kind == .file);
1001 try testing.expect((try dir.statFile(io, "b/symlink", .{})).kind == .file); // statFile follows symlink
10021002
10031003 var buf: [32]u8 = undefined;
10041004 try testing.expectEqualSlices(
10051005 u8,
10061006 "../a/file",
1007 normalizePath(try dir.readLink("b/symlink", &buf)),
1007 normalizePath(buf[0..try dir.readLink(io, "b/symlink", &buf)]),
10081008 );
10091009}
10101010
......@@ -1120,28 +1120,18 @@ fn normalizePath(bytes: []u8) []u8 {
11201120
11211121// File system mode based on tar header mode and mode_mode options.
11221122fn filePermissions(mode: u32, options: PipeOptions) Io.File.Permissions {
1123 const default_mode = 0o666;
1124
1125 if (!Io.File.Permissions.has_executable_bit or options.mode_mode == .ignore)
1126 return .fromMode(default_mode);
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);
1123 return if (!Io.File.Permissions.has_executable_bit or options.mode_mode == .ignore or (mode & 0o100) == 0)
1124 .default_file
1125 else
1126 .executable_file;
11371127}
11381128
11391129test filePermissions {
11401130 if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest;
1141 try testing.expectEqual(0o666, filePermissions(0o744, PipeOptions{ .mode_mode = .ignore }));
1142 try testing.expectEqual(0o777, filePermissions(0o744, PipeOptions{}));
1143 try testing.expectEqual(0o666, filePermissions(0o644, PipeOptions{}));
1144 try testing.expectEqual(0o666, filePermissions(0o655, PipeOptions{}));
1131 try testing.expectEqual(.default_file, filePermissions(0o744, .{ .mode_mode = .ignore }));
1132 try testing.expectEqual(.executable_file, filePermissions(0o744, .{}));
1133 try testing.expectEqual(.default_file, filePermissions(0o644, .{}));
1134 try testing.expectEqual(.default_file, filePermissions(0o655, .{}));
11451135}
11461136
11471137test "executable bit" {
......@@ -1167,19 +1157,21 @@ test "executable bit" {
11671157 return err;
11681158 };
11691159
1170 const fs = try tmp.dir.statFile("a/file");
1160 const fs = try tmp.dir.statFile(io, "a/file", .{});
11711161 try testing.expect(fs.kind == .file);
11721162
1163 const mode = fs.permissions.toMode();
1164
11731165 if (opt == .executable_bit_only) {
11741166 // Executable bit is set for user, group and others
1175 try testing.expect(fs.mode & S.IXUSR > 0);
1176 try testing.expect(fs.mode & S.IXGRP > 0);
1177 try testing.expect(fs.mode & S.IXOTH > 0);
1167 try testing.expect(mode & S.IXUSR > 0);
1168 try testing.expect(mode & S.IXGRP > 0);
1169 try testing.expect(mode & S.IXOTH > 0);
11781170 }
11791171 if (opt == .ignore) {
1180 try testing.expect(fs.mode & S.IXUSR == 0);
1181 try testing.expect(fs.mode & S.IXGRP == 0);
1182 try testing.expect(fs.mode & S.IXOTH == 0);
1172 try testing.expect(mode & S.IXUSR == 0);
1173 try testing.expect(mode & S.IXGRP == 0);
1174 try testing.expect(mode & S.IXOTH == 0);
11831175 }
11841176 }
11851177}
lib/std/tar/test.zig+2-2
......@@ -504,6 +504,6 @@ test "case sensitivity" {
504504 };
505505
506506 // on case sensitive os both files are created
507 try testing.expect((try root.dir.statFile("alacritty/darkermatrix.yml")).kind == .file);
508 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(io, "alacritty/Darkermatrix.yml", .{})).kind == .file);
509509}
lib/std/zig/WindowsSdk.zig+1-1
......@@ -1012,7 +1012,7 @@ const MsvcLibDir = struct {
10121012 var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false;
10131013 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;
10161016 if (stat.kind != .file)
10171017 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" {
45394539test "zig fmt: Don't add extra newline after if" {
45404540 try testCanonical(
45414541 \\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, .{})) {
45434543 \\ return;
45444544 \\ }
45454545 \\}
src/Builtin.zig+2-1
......@@ -313,8 +313,9 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {
313313 assert(file.source != null);
314314
315315 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| {
318319 if (stat.size != file.source.?.len) {
319320 std.log.warn(
320321 "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
14401440 },
14411441 .sym_link => {
14421442 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);
14441444 // TODO: if this would create a symlink to outside
14451445 // 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) {
14471447 error.FileNotFound => {
14481448 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, .{});
14501450 },
14511451 else => |e| return e,
14521452 };
......@@ -1698,7 +1698,7 @@ fn hashFileFallible(io: Io, dir: Io.Dir, hashed_file: *HashedFile) HashedFile.Er
16981698 }
16991699 },
17001700 .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);
17021702 if (fs.path.sep != canonical_sep) {
17031703 // Package hashes are intended to be consistent across
17041704 // platforms which means we must normalize path separators
......@@ -2218,13 +2218,13 @@ test "set executable bit based on file content" {
22182218 defer out.close(io);
22192219 const S = std.posix.S;
22202220 // expect executable bit not set
2221 try std.testing.expect((try out.statFile("file1")).mode & S.IXUSR == 0);
2222 try std.testing.expect((try out.statFile("script_without_shebang")).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(io, "script_without_shebang", .{})).mode & S.IXUSR == 0);
22232223 // expect executable bit set
2224 try std.testing.expect((try out.statFile("hello")).mode & S.IXUSR != 0);
2225 try std.testing.expect((try out.statFile("script")).mode & S.IXUSR != 0);
2226 try std.testing.expect((try out.statFile("script_with_shebang_without_exec_bit")).mode & S.IXUSR != 0);
2227 try std.testing.expect((try out.statFile("hello_ln")).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(io, "script", .{})).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(io, "hello_ln", .{})).mode & S.IXUSR != 0);
22282228
22292229 //
22302230 // $ 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 {
281281 const symlink_object = try repository.odb.readObject();
282282 if (symlink_object.type != .blob) return error.InvalidFile;
283283 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| {
285285 const file_name = try std.fs.path.join(diagnostics.allocator, &.{ current_path, entry.name });
286286 errdefer diagnostics.allocator.free(file_name);
287287 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) !
182182 // Mark any excluded files/directories as already seen,
183183 // so that they are skipped later during actual processing
184184 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) {
186186 error.FileNotFound => continue,
187187 // On Windows, statFile does not work for directories
188188 error.IsDir => dir: {