Skip to content

Commit

Permalink
Merge pull request #4675 from jasonKercher/os2-dir-linux
Browse files Browse the repository at this point in the history
Implement os2 dir linux
  • Loading branch information
gingerBill authored Jan 13, 2025
2 parents 600e0eb + c11dccf commit 48a7ed0
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 19 deletions.
87 changes: 84 additions & 3 deletions core/os/os2/dir_linux.odin
Original file line number Diff line number Diff line change
@@ -1,20 +1,101 @@
#+private
package os2

Read_Directory_Iterator_Impl :: struct {
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())
}
28 changes: 12 additions & 16 deletions core/sys/linux/wrappers.odin
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
for str[str_size-1] == 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 _ in 0..<trunc {
str_size += 1
if str[str_size] == 0 {
break
}
}
// Oh yeah btw i could also just `repne scasb` this thing, but honestly I started doing
// it the painful way, might as well finish doing it that way
return string(str[:str_size])
}

Expand All @@ -117,4 +113,4 @@ perf_cache_config :: #force_inline proc "contextless" (id: Perf_Hardware_Cache_I
op: Perf_Hardware_Cache_Op_Id,
res: Perf_Hardware_Cache_Result_Id) -> u64 {
return u64(id) | (u64(op) << 8) | (u64(res) << 16)
}
}

0 comments on commit 48a7ed0

Please sign in to comment.