5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
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;
var buffer_index: usize = 0;
while (buffer.len - buffer_index != 0) {
if (dr.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(dr.dir.handle, 0) catch |err| switch (err) {
error.Unseekable => return error.Unexpected,
else => |e| return e,
};
dr.state = .reading;
}
const syscall: Syscall = try .start();
const n = while (true) {
const rc = linux.getdents64(dr.dir.handle, dr.buffer.ptr, @min(dr.buffer.len, std.math.maxInt(c_uint)));
switch (linux.errno(rc)) {
.SUCCESS => {
syscall.finish();
break rc;
},
.INTR => {
try syscall.checkCancel();
continue;
},
else => |e| {
syscall.finish();
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, non-WASI UNIX platforms.
.NOENT => {
dr.state = .finished;
return 0;
},
// 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),
}
},
}
};
if (n == 0) {
dr.state = .finished;
return 0;
}
dr.index = 0;
dr.end = n;
}
// 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.
//
// Furthermore, I observed qemu user mode to not align this struct, so
// this code makes the conservative choice to not assume alignment.
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_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];
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;
}