diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-10 22:21:51 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-23 22:15:09 -0800 |
| commit | 7bc0166b7c34ac0120f50aceb7132ffaa4aeec83 (patch) | |
| tree | e9fff72403af7557e5b7b4aa3aa7cc781981d3e1 /lib/std/Io/Threaded.zig | |
| parent | b042e935228db5d46271d4d3d17afeb9ba5d7ce3 (diff) | |
| download | zig-7bc0166b7c34ac0120f50aceb7132ffaa4aeec83.tar.gz zig-7bc0166b7c34ac0120f50aceb7132ffaa4aeec83.zip | |
std.Io: implement dirRead for Linux
unfortunately, Io.Dir.SelectiveWalker is copying the iterator which
has multiple problems
Diffstat (limited to 'lib/std/Io/Threaded.zig')
| -rw-r--r-- | lib/std/Io/Threaded.zig | 181 |
1 files changed, 144 insertions, 37 deletions
diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 41e93cee5b..55cc21db0e 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -3271,12 +3271,113 @@ fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void { for (dirs) |dir| posix.close(dir.handle); } -fn dirRead(userdata: ?*anyopaque, dir_reader: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { +const dirRead = switch (native_os) { + .linux => dirReadLinux, + else => dirReadUnimplemented, +}; + +fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { + const linux = std.os.linux; const t: *Threaded = @ptrCast(@alignCast(userdata)); - _ = t; + const current_thread = Thread.getCurrent(t); + const Header = extern struct { + fill_end: usize, + }; + const header: *Header = @ptrCast(&dr.buffer); + const header_end: usize = @sizeOf(Header); + if (dr.index < header_end) { + // Initialize header. + dr.index = header_end; + header.* = .{ .fill_end = header_end }; + } + var buffer_index: usize = 0; + while (buffer.len - buffer_index != 0) { + if (header.fill_end - dr.index == 0) { + // Refill the buffer, unless we've already created references to + // buffered data. + if (buffer_index != 0) break; + if (dr.state == .reset) { + posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) { + error.Unseekable => return error.Unexpected, + else => |e| return e, + }; + dr.state = .reading; + } + const dents_buffer = dr.buffer[header_end..]; + try current_thread.beginSyscall(); + const n = while (true) { + const rc = linux.getdents64(dr.dir.handle, dents_buffer.ptr, dents_buffer.len); + switch (linux.errno(rc)) { + .SUCCESS => { + current_thread.endSyscall(); + break rc; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability. + .FAULT => |err| return errnoBug(err), + .NOTDIR => |err| return errnoBug(err), + // To be consistent across platforms, iteration + // ends if the directory being iterated is deleted + // during iteration. This matches the behavior of + // non-Linux UNIX platforms. + .NOENT => { + dr.state = .finished; + return 0; + }, + .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net. + .ACCES => return error.AccessDenied, // Lacking permission to iterate this directory. + else => |err| return posix.unexpectedErrno(err), + } + }, + } + }; + if (n == 0) { + dr.state = .finished; + return 0; + } + dr.index = header_end; + header.fill_end = header_end + n; + } + const linux_entry: *align(1) linux.dirent64 = @ptrCast(&dr.buffer[dr.index]); + const next_index = dr.index + linux_entry.reclen; + dr.index = next_index; + + const name = std.mem.sliceTo(@as([*:0]u8, @ptrCast(&linux_entry.name)), 0); + // Skip "." and ".." entries. + if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue; + + const entry_kind: File.Kind = switch (linux_entry.type) { + linux.DT.BLK => .block_device, + linux.DT.CHR => .character_device, + linux.DT.DIR => .directory, + linux.DT.FIFO => .named_pipe, + linux.DT.LNK => .sym_link, + linux.DT.REG => .file, + linux.DT.SOCK => .unix_domain_socket, + else => .unknown, + }; + buffer[buffer_index] = .{ + .name = name, + .kind = entry_kind, + .inode = linux_entry.ino, + }; + buffer_index += 1; + } + return buffer_index; +} + +fn dirReadUnimplemented(userdata: ?*anyopaque, dir_reader: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { + _ = userdata; _ = dir_reader; _ = buffer; - @panic("TODO"); + return error.Unimplemented; } const dirRealPath = switch (native_os) { @@ -5908,11 +6009,16 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi const current_thread = Thread.getCurrent(t); const fd = file.handle; - if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { + if (native_os == .windows) { + try current_thread.checkCancel(); + return windows.SetFilePointerEx_BEGIN(fd, offset); + } + + if (native_os == .wasi and !builtin.link_libc) { try current_thread.beginSyscall(); while (true) { - var result: u64 = undefined; - switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) { + var new_offset: std.os.wasi.filesize_t = undefined; + switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) { .SUCCESS => { current_thread.endSyscall(); return; @@ -5930,6 +6036,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi .OVERFLOW => return error.Unseekable, .SPIPE => return error.Unseekable, .NXIO => return error.Unseekable, + .NOTCAPABLE => return error.AccessDenied, else => |err| return posix.unexpectedErrno(err), } }, @@ -5937,40 +6044,40 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi } } - if (native_os == .windows) { - try current_thread.checkCancel(); - return windows.SetFilePointerEx_BEGIN(fd, offset); - } + if (posix.SEEK == void) return error.Unseekable; - if (native_os == .wasi and !builtin.link_libc) while (true) { - var new_offset: std.os.wasi.filesize_t = undefined; + return posixSeekTo(current_thread, fd, offset); +} + +fn posixSeekTo(current_thread: *Thread, fd: posix.fd_t, offset: u64) File.SeekError!void { + if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { try current_thread.beginSyscall(); - switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) { - .SUCCESS => { - current_thread.endSyscall(); - return; - }, - .INTR => { - try current_thread.checkCancel(); - continue; - }, - .CANCELED => return current_thread.endSyscallCanceled(), - else => |e| { - current_thread.endSyscall(); - switch (e) { - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .INVAL => return error.Unseekable, - .OVERFLOW => return error.Unseekable, - .SPIPE => return error.Unseekable, - .NXIO => return error.Unseekable, - .NOTCAPABLE => return error.AccessDenied, - else => |err| return posix.unexpectedErrno(err), - } - }, + while (true) { + var result: u64 = undefined; + switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) { + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .INVAL => return error.Unseekable, + .OVERFLOW => return error.Unseekable, + .SPIPE => return error.Unseekable, + .NXIO => return error.Unseekable, + else => |err| return posix.unexpectedErrno(err), + } + }, + } } - }; - - if (posix.SEEK == void) return error.Unseekable; + } try current_thread.beginSyscall(); while (true) { |
