authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-19 11:26:06-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:11-08:00
log002d444964869a5fd4d588d45c46f3acfa16cf5d
treea92f8dcd1de9c85613a721d4f92f71f20bb520a4
parent018e34271fe58e540dc46e8bca529ce399625bef

std: fix Io.Dir.min_buffer_len on Linux


4 files changed, 25 insertions(+), 9 deletions(-)

lib/std/Io/Dir.zig+6-1
...@@ -102,7 +102,12 @@ pub const Reader = struct {...@@ -102,7 +102,12 @@ pub const Reader = struct {
102 end: usize,102 end: usize,
103103
104 /// A length for `buffer` that allows all implementations to function.104 /// A length for `buffer` that allows all implementations to function.
105 pub const min_buffer_len = std.mem.alignForward(usize, max_name_bytes, @alignOf(usize));105 pub const min_buffer_len = switch (native_os) {
106 .linux => @sizeOf(std.os.linux.dirent64) +
107 std.mem.alignForward(usize, max_name_bytes, @alignOf(std.os.linux.dirent64)),
108 .windows => std.mem.alignForward(usize, max_name_bytes, @alignOf(usize)),
109 else => if (builtin.link_libc) @sizeOf(std.c.dirent) else std.mem.alignForward(usize, max_name_bytes, @alignOf(usize)),
110 };
106111
107 pub const State = enum {112 pub const State = enum {
108 /// Indicates the next call to `read` should rewind and start over the113 /// Indicates the next call to `read` should rewind and start over the
lib/std/Io/Threaded.zig+14-3
...@@ -3399,7 +3399,10 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir...@@ -3399,7 +3399,10 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir
3399 dr.state = .finished;3399 dr.state = .finished;
3400 return 0;3400 return 0;
3401 },3401 },
3402 .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.3402 // This can occur when reading /proc/$PID/net, or
3403 // if the provided buffer is too small. Neither
3404 // scenario is intended to be handled by this API.
3405 .INVAL => return error.Unexpected,
3403 .ACCES => return error.AccessDenied, // Lacking permission to iterate this directory.3406 .ACCES => return error.AccessDenied, // Lacking permission to iterate this directory.
3404 else => |err| return posix.unexpectedErrno(err),3407 else => |err| return posix.unexpectedErrno(err),
3405 }3408 }
...@@ -3413,11 +3416,19 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir...@@ -3413,11 +3416,19 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir
3413 dr.index = 0;3416 dr.index = 0;
3414 dr.end = n;3417 dr.end = n;
3415 }3418 }
3416 const linux_entry: *align(1) linux.dirent64 = @ptrCast(&dr.buffer[dr.index]);3419 // Linux aligns the header by padding after the null byte of the name
3420 // to align the next entry. This means we can find the end of the name
3421 // by looking at only the 8 bytes before the next record. However since
3422 // file names are usually short it's better to keep the machine code
3423 // simpler.
3424 const linux_entry: *linux.dirent64 = @ptrCast(@alignCast(&dr.buffer[dr.index]));
3417 const next_index = dr.index + linux_entry.reclen;3425 const next_index = dr.index + linux_entry.reclen;
3418 dr.index = next_index;3426 dr.index = next_index;
3427 const name_ptr: [*]u8 = &linux_entry.name;
3428 const padded_name = name_ptr[0 .. linux_entry.reclen - @offsetOf(linux.dirent64, "name")];
3429 const name_len = std.mem.findScalar(u8, padded_name, 0).?;
3430 const name = name_ptr[0..name_len :0];
34193431
3420 const name = std.mem.sliceTo(@as([*:0]u8, @ptrCast(&linux_entry.name)), 0);
3421 if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;3432 if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;
34223433
3423 const entry_kind: File.Kind = switch (linux_entry.type) {3434 const entry_kind: File.Kind = switch (linux_entry.type) {
lib/std/fs/test.zig+4-4
...@@ -1418,12 +1418,12 @@ test "max file name component lengths" {...@@ -1418,12 +1418,12 @@ test "max file name component lengths" {
1418 // On WASI, the maxed filename depends on the host OS, so in order for this test to1418 // On WASI, the maxed filename depends on the host OS, so in order for this test to
1419 // work on any host, we need to use a length that will work for all platforms1419 // work on any host, we need to use a length that will work for all platforms
1420 // (i.e. the minimum max_name_bytes of all supported platforms).1420 // (i.e. the minimum max_name_bytes of all supported platforms).
1421 const maxed_wasi_filename1 = [_]u8{'1'} ** 255;1421 const maxed_wasi_filename1: [255]u8 = @splat('1');
1422 const maxed_wasi_filename2 = [_]u8{'2'} ** 255;1422 const maxed_wasi_filename2: [255]u8 = @splat('2');
1423 try testFilenameLimits(io, tmp.dir, &maxed_wasi_filename1, &maxed_wasi_filename2);1423 try testFilenameLimits(io, tmp.dir, &maxed_wasi_filename1, &maxed_wasi_filename2);
1424 } else {1424 } else {
1425 const maxed_ascii_filename1 = [_]u8{'1'} ** std.fs.max_name_bytes;1425 const maxed_ascii_filename1: [Dir.max_name_bytes]u8 = @splat('1');
1426 const maxed_ascii_filename2 = [_]u8{'2'} ** std.fs.max_name_bytes;1426 const maxed_ascii_filename2: [Dir.max_name_bytes]u8 = @splat('2');
1427 try testFilenameLimits(io, tmp.dir, &maxed_ascii_filename1, &maxed_ascii_filename2);1427 try testFilenameLimits(io, tmp.dir, &maxed_ascii_filename1, &maxed_ascii_filename2);
1428 }1428 }
1429}1429}
lib/std/os/linux.zig+1-1
...@@ -6040,7 +6040,7 @@ pub const dirent64 = extern struct {...@@ -6040,7 +6040,7 @@ pub const dirent64 = extern struct {
6040 off: u64,6040 off: u64,
6041 reclen: u16,6041 reclen: u16,
6042 type: u8,6042 type: u8,
6043 name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/1736043 name: [0]u8,
6044};6044};
60456045
6046pub const dl_phdr_info = extern struct {6046pub const dl_phdr_info = extern struct {