haiku: fix directory iteration

This commit is contained in:
Jacob Young 2024-04-06 17:47:54 -04:00
parent 23ee39116c
commit 0c83fa2fd0
2 changed files with 830 additions and 662 deletions

File diff suppressed because it is too large Load diff

View file

@ -220,71 +220,83 @@ pub const Iterator = switch (native_os) {
}, },
.haiku => struct { .haiku => struct {
dir: Dir, dir: Dir,
buf: [1024]u8, // TODO align(@alignOf(posix.dirent64)), buf: [@sizeOf(DirEnt) + posix.PATH_MAX]u8 align(@alignOf(DirEnt)),
offset: usize,
index: usize, index: usize,
end_index: usize, end_index: usize,
first_iter: bool, first_iter: bool,
const Self = @This(); const Self = @This();
const DirEnt = posix.system.DirEnt;
pub const Error = IteratorError; pub const Error = IteratorError;
/// Memory such as file names referenced in this returned entry becomes invalid /// Memory such as file names referenced in this returned entry becomes invalid
/// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
pub fn next(self: *Self) Error!?Entry { pub fn next(self: *Self) Error!?Entry {
start_over: while (true) { while (true) {
// TODO: find a better max
const HAIKU_MAX_COUNT = 10000;
if (self.index >= self.end_index) { if (self.index >= self.end_index) {
if (self.first_iter) { if (self.first_iter) {
posix.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions switch (@as(posix.E, @enumFromInt(posix.system._kern_rewind_dir(self.dir.fd)))) {
.SUCCESS => {},
.BADF => unreachable, // Dir is invalid
.FAULT => unreachable,
.NOTDIR => unreachable,
.INVAL => unreachable,
.ACCES => return error.AccessDenied,
.PERM => return error.AccessDenied,
else => |err| return posix.unexpectedErrno(err),
}
self.first_iter = false; self.first_iter = false;
} }
const rc = posix.system._kern_read_dir( const rc = posix.system._kern_read_dir(
self.dir.fd, self.dir.fd,
&self.buf, &self.buf,
self.buf.len, self.buf.len,
HAIKU_MAX_COUNT, self.buf.len / @sizeOf(DirEnt),
); );
if (rc == 0) return null; if (rc == 0) return null;
if (rc < 0) { if (rc < 0) {
switch (posix.errno(rc)) { switch (@as(posix.E, @enumFromInt(rc))) {
.BADF => unreachable, // Dir is invalid or was opened without iteration ability .BADF => unreachable, // Dir is invalid
.FAULT => unreachable, .FAULT => unreachable,
.NOTDIR => unreachable, .NOTDIR => unreachable,
.INVAL => unreachable, .INVAL => unreachable,
.OVERFLOW => unreachable,
.ACCES => return error.AccessDenied,
.PERM => return error.AccessDenied,
else => |err| return posix.unexpectedErrno(err), else => |err| return posix.unexpectedErrno(err),
} }
} }
self.offset = 0;
self.index = 0; self.index = 0;
self.end_index = @as(usize, @intCast(rc)); self.end_index = @intCast(rc);
}
const haiku_entry = @as(*align(1) posix.system.dirent, @ptrCast(&self.buf[self.index]));
const next_index = self.index + haiku_entry.reclen;
self.index = next_index;
const name = mem.sliceTo(@as([*:0]u8, @ptrCast(&haiku_entry.name)), 0);
if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or (haiku_entry.ino == 0)) {
continue :start_over;
} }
const dirent: *DirEnt = @ptrCast(@alignCast(&self.buf[self.offset]));
self.offset += dirent.reclen;
self.index += 1;
const name = mem.span(dirent.getName());
if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or dirent.ino == 0) continue;
var stat_info: posix.Stat = undefined; var stat_info: posix.Stat = undefined;
const rc = posix.system._kern_read_stat( switch (@as(posix.E, @enumFromInt(posix.system._kern_read_stat(
self.dir.fd, self.dir.fd,
&haiku_entry.name, name,
false, false,
&stat_info, &stat_info,
0, 0,
); )))) {
if (rc != 0) { .SUCCESS => {},
switch (posix.errno(rc)) { .INVAL => unreachable,
.SUCCESS => {}, .BADF => unreachable, // Dir is invalid
.BADF => unreachable, // Dir is invalid or was opened without iteration ability .NOMEM => return error.SystemResources,
.FAULT => unreachable, .ACCES => return error.AccessDenied,
.NOTDIR => unreachable, .PERM => return error.AccessDenied,
.INVAL => unreachable, .FAULT => unreachable,
else => |err| return posix.unexpectedErrno(err), .NAMETOOLONG => unreachable,
} .LOOP => unreachable,
.NOENT => continue,
else => |err| return posix.unexpectedErrno(err),
} }
const statmode = stat_info.mode & posix.S.IFMT; const statmode = stat_info.mode & posix.S.IFMT;
@ -315,7 +327,7 @@ pub const Iterator = switch (native_os) {
dir: Dir, dir: Dir,
// The if guard is solely there to prevent compile errors from missing `linux.dirent64` // The if guard is solely there to prevent compile errors from missing `linux.dirent64`
// definition when compiling for other OSes. It doesn't do anything when compiling for Linux. // definition when compiling for other OSes. It doesn't do anything when compiling for Linux.
buf: [1024]u8 align(if (native_os != .linux) 1 else @alignOf(linux.dirent64)), buf: [1024]u8 align(@alignOf(linux.dirent64)),
index: usize, index: usize,
end_index: usize, end_index: usize,
first_iter: bool, first_iter: bool,
@ -599,13 +611,21 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator {
.buf = undefined, .buf = undefined,
.first_iter = first_iter_start_value, .first_iter = first_iter_start_value,
}, },
.linux, .haiku => return Iterator{ .linux => return Iterator{
.dir = self, .dir = self,
.index = 0, .index = 0,
.end_index = 0, .end_index = 0,
.buf = undefined, .buf = undefined,
.first_iter = first_iter_start_value, .first_iter = first_iter_start_value,
}, },
.haiku => return Iterator{
.dir = self,
.offset = 0,
.index = 0,
.end_index = 0,
.buf = undefined,
.first_iter = first_iter_start_value,
},
.windows => return Iterator{ .windows => return Iterator{
.dir = self, .dir = self,
.index = 0, .index = 0,
@ -1429,6 +1449,27 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) Open
.wasi => { .wasi => {
return openDir(self, mem.sliceTo(sub_path_c, 0), args); return openDir(self, mem.sliceTo(sub_path_c, 0), args);
}, },
.haiku => {
const rc = posix.system._kern_open_dir(self.fd, sub_path_c);
if (rc >= 0) return .{ .fd = rc };
switch (@as(posix.E, @enumFromInt(rc))) {
.FAULT => unreachable,
.INVAL => unreachable,
.BADF => unreachable,
.ACCES => return error.AccessDenied,
.LOOP => return error.SymLinkLoop,
.MFILE => return error.ProcessFdQuotaExceeded,
.NAMETOOLONG => return error.NameTooLong,
.NFILE => return error.SystemFdQuotaExceeded,
.NODEV => return error.NoDevice,
.NOENT => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOTDIR => return error.NotDir,
.PERM => return error.AccessDenied,
.BUSY => return error.DeviceBusy,
else => |err| return posix.unexpectedErrno(err),
}
},
else => { else => {
var symlink_flags: posix.O = .{ var symlink_flags: posix.O = .{
.ACCMODE = .RDONLY, .ACCMODE = .RDONLY,