diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-19 11:26:06 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-23 22:15:11 -0800 |
| commit | 002d444964869a5fd4d588d45c46f3acfa16cf5d (patch) | |
| tree | a92f8dcd1de9c85613a721d4f92f71f20bb520a4 /lib | |
| parent | 018e34271fe58e540dc46e8bca529ce399625bef (diff) | |
| download | zig-002d444964869a5fd4d588d45c46f3acfa16cf5d.tar.gz zig-002d444964869a5fd4d588d45c46f3acfa16cf5d.zip | |
std: fix Io.Dir.min_buffer_len on Linux
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/std/Io/Dir.zig | 7 | ||||
| -rw-r--r-- | lib/std/Io/Threaded.zig | 17 | ||||
| -rw-r--r-- | lib/std/fs/test.zig | 8 | ||||
| -rw-r--r-- | lib/std/os/linux.zig | 2 |
4 files changed, 25 insertions, 9 deletions
diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index 2de0fbe564..330ac5bf62 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -102,7 +102,12 @@ pub const Reader = struct { end: usize, /// A length for `buffer` that allows all implementations to function. - pub const min_buffer_len = std.mem.alignForward(usize, max_name_bytes, @alignOf(usize)); + pub const min_buffer_len = switch (native_os) { + .linux => @sizeOf(std.os.linux.dirent64) + + std.mem.alignForward(usize, max_name_bytes, @alignOf(std.os.linux.dirent64)), + .windows => std.mem.alignForward(usize, max_name_bytes, @alignOf(usize)), + else => if (builtin.link_libc) @sizeOf(std.c.dirent) else std.mem.alignForward(usize, max_name_bytes, @alignOf(usize)), + }; pub const State = enum { /// Indicates the next call to `read` should rewind and start over the diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 81c18a8c74..3472b8fbe2 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -3399,7 +3399,10 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir dr.state = .finished; return 0; }, - .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net. + // This can occur when reading /proc/$PID/net, or + // if the provided buffer is too small. Neither + // scenario is intended to be handled by this API. + .INVAL => return error.Unexpected, .ACCES => return error.AccessDenied, // Lacking permission to iterate this directory. else => |err| return posix.unexpectedErrno(err), } @@ -3413,11 +3416,19 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir dr.index = 0; dr.end = n; } - const linux_entry: *align(1) linux.dirent64 = @ptrCast(&dr.buffer[dr.index]); + // Linux aligns the header by padding after the null byte of the name + // to align the next entry. This means we can find the end of the name + // by looking at only the 8 bytes before the next record. However since + // file names are usually short it's better to keep the machine code + // simpler. + const linux_entry: *linux.dirent64 = @ptrCast(@alignCast(&dr.buffer[dr.index])); const next_index = dr.index + linux_entry.reclen; dr.index = next_index; + const name_ptr: [*]u8 = &linux_entry.name; + const padded_name = name_ptr[0 .. linux_entry.reclen - @offsetOf(linux.dirent64, "name")]; + const name_len = std.mem.findScalar(u8, padded_name, 0).?; + const name = name_ptr[0..name_len :0]; - const name = std.mem.sliceTo(@as([*:0]u8, @ptrCast(&linux_entry.name)), 0); if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue; const entry_kind: File.Kind = switch (linux_entry.type) { diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 19df1189fb..d66868379d 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1418,12 +1418,12 @@ test "max file name component lengths" { // On WASI, the maxed filename depends on the host OS, so in order for this test to // work on any host, we need to use a length that will work for all platforms // (i.e. the minimum max_name_bytes of all supported platforms). - const maxed_wasi_filename1 = [_]u8{'1'} ** 255; - const maxed_wasi_filename2 = [_]u8{'2'} ** 255; + const maxed_wasi_filename1: [255]u8 = @splat('1'); + const maxed_wasi_filename2: [255]u8 = @splat('2'); try testFilenameLimits(io, tmp.dir, &maxed_wasi_filename1, &maxed_wasi_filename2); } else { - const maxed_ascii_filename1 = [_]u8{'1'} ** std.fs.max_name_bytes; - const maxed_ascii_filename2 = [_]u8{'2'} ** std.fs.max_name_bytes; + const maxed_ascii_filename1: [Dir.max_name_bytes]u8 = @splat('1'); + const maxed_ascii_filename2: [Dir.max_name_bytes]u8 = @splat('2'); try testFilenameLimits(io, tmp.dir, &maxed_ascii_filename1, &maxed_ascii_filename2); } } diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 1db462ad3e..96b4c8ee6f 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -6040,7 +6040,7 @@ pub const dirent64 = extern struct { off: u64, reclen: u16, type: u8, - name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 + name: [0]u8, }; pub const dl_phdr_info = extern struct { |
