diff --git a/core/os/os2/dir_linux.odin b/core/os/os2/dir_linux.odin index f26b4fc79..f7723936b 100644 --- a/core/os/os2/dir_linux.odin +++ b/core/os/os2/dir_linux.odin @@ -1,20 +1,101 @@ #+private package os2 +import "core:sys/linux" + Read_Directory_Iterator_Impl :: struct { - + prev_fi: File_Info, + dirent_backing: []u8, + dirent_buflen: int, + dirent_off: int, + index: int, } - @(require_results) _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) { + scan_entries :: proc(dfd: linux.Fd, entries: []u8, offset: ^int) -> (fd: linux.Fd, file_name: string) { + for d in linux.dirent_iterate_buf(entries, offset) { + file_name = linux.dirent_name(d) + if file_name == "." || file_name == ".." { + continue + } + + file_name_cstr := cstring(raw_data(file_name)) + entry_fd, errno := linux.openat(dfd, file_name_cstr, {.NOFOLLOW, .PATH}) + if errno == .NONE { + return entry_fd, file_name + } + } + return -1, "" + } + + index = it.impl.index + it.impl.index += 1 + + dfd := linux.Fd(_fd(it.f)) + + entries := it.impl.dirent_backing[:it.impl.dirent_buflen] + entry_fd, file_name := scan_entries(dfd, entries, &it.impl.dirent_off) + + for entry_fd == -1 { + if len(it.impl.dirent_backing) == 0 { + it.impl.dirent_backing = make([]u8, 512, file_allocator()) + } + + loop: for { + buflen, errno := linux.getdents(linux.Fd(dfd), it.impl.dirent_backing[:]) + #partial switch errno { + case .EINVAL: + delete(it.impl.dirent_backing, file_allocator()) + n := len(it.impl.dirent_backing) * 2 + it.impl.dirent_backing = make([]u8, n, file_allocator()) + continue + case .NONE: + if buflen == 0 { + return + } + it.impl.dirent_off = 0 + it.impl.dirent_buflen = buflen + entries = it.impl.dirent_backing[:buflen] + break loop + case: // error + return + } + } + + entry_fd, file_name = scan_entries(dfd, entries, &it.impl.dirent_off) + } + defer linux.close(entry_fd) + + file_info_delete(it.impl.prev_fi, file_allocator()) + fi, _ = _fstat_internal(entry_fd, file_allocator()) + it.impl.prev_fi = fi + + ok = true return } @(require_results) _read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) { - return {}, .Unsupported + if f == nil || f.impl == nil { + return {}, .Invalid_File + } + + stat: linux.Stat + errno := linux.fstat(linux.Fd(fd(f)), &stat) + if errno != .NONE { + return {}, _get_platform_error(errno) + } + if (stat.mode & linux.S_IFMT) != linux.S_IFDIR { + return {}, .Invalid_Dir + } + return {f = f}, nil } _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) { + if it == nil { + return + } + delete(it.impl.dirent_backing, file_allocator()) + file_info_delete(it.impl.prev_fi, file_allocator()) } diff --git a/core/sys/linux/wrappers.odin b/core/sys/linux/wrappers.odin index 4f6118c80..e367a4db4 100644 --- a/core/sys/linux/wrappers.odin +++ b/core/sys/linux/wrappers.odin @@ -86,22 +86,18 @@ dirent_iterate_buf :: proc "contextless" (buf: []u8, offs: ^int) -> (d: ^Dirent, /// The lifetime of the string is bound to the lifetime of the provided dirent structure dirent_name :: proc "contextless" (dirent: ^Dirent) -> string #no_bounds_check { str := ([^]u8)(&dirent.name) - // Note(flysand): The string size calculated above applies only to the ideal case - // we subtract 1 byte from the string size, because a null terminator is guaranteed - // to be present. But! That said, the dirents are aligned to 8 bytes and the padding - // between the null terminator and the start of the next struct may be not initialized - // which means we also have to scan these garbage bytes. - str_size := int(dirent.reclen) - 1 - cast(int)offset_of(Dirent, name) - // This skips *only* over the garbage, since if we're not garbage we're at nul terminator, - // which skips this loop - for str[str_size] != 0 { - str_size -= 1 + // Dirents are aligned to 8 bytes, so there is guaranteed to be a null + // terminator in the last 8 bytes. + str_size := int(dirent.reclen) - cast(int)offset_of(Dirent, name) + + trunc := min(str_size, 8) + str_size -= trunc + for i in 0.. u64 { return u64(id) | (u64(op) << 8) | (u64(res) << 16) -} \ No newline at end of file +}