hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit fb1b551b1e71118e7d8b764642d2e9688fdb0626
parent 7bb8ad6a430050e6951811914ce54ce6c806c7e0
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 24 Feb 2021 17:06:44 -0500

os: implement fs::iter

Diffstat:
Mfs/types.ha | 2++
Mos/+linux/dirfdfs.ha | 84++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mrt/+linux/syscalls.ha | 5+++++
Mrt/+linux/types.ha | 17+++++++++++++++++
4 files changed, 103 insertions(+), 5 deletions(-)

diff --git a/fs/types.ha b/fs/types.ha @@ -64,6 +64,8 @@ export type mode = enum uint { // Entry has the sticky bit set STICKY = 0o1000, + // Entry is of an unknown type + UNKNOWN = 0, // Entry is a FIFO (named pipe) FIFO = 0o010000, // Entry is a directory diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha @@ -1,7 +1,9 @@ +use encoding::utf8; use fs; use io; use rt; use strings; +use strconv; // Controls how symlinks are followed (or not) in a dirfd filesystem. Support // for this feature varies, you should gate usage of this enum behind a build @@ -155,12 +157,8 @@ fn fs_create(fs: *fs::fs, path: fs::path, mode: io::mode) (*io::stream | fs::err return _fs_open(fs, path, mode, &oh); }; -fn fs_iter(fs: *fs::fs, path: fs::path) (*fs::iterator | fs::error) = { - abort(); -}; - fn fs_stat(fs: *fs::fs, path: fs::path) (fs::filestat | fs::error) = { - abort(); + abort(); // TODO }; fn fs_subdir(fs: *fs::fs, path: fs::path) (*fs::fs | fs::error) = { @@ -188,3 +186,79 @@ fn fs_close(fs: *fs::fs) void = { let fs = fs: *os_filesystem; rt::close(fs.dirfd); }; + +def BUFSIZ: size = 2048; + +// Based on musl's readdir +type os_iterator = struct { + iter: fs::iterator, + fd: int, + buf_pos: size, + buf_end: size, + buf: [BUFSIZ]u8, +}; + +fn fs_iter(fs: *fs::fs, path: fs::path) (*fs::iterator | fs::error) = { + let fs = fs: *os_filesystem; + const p: []u8 = match (path) { + s: str => strings::to_utf8(s), + b: []u8 => b, + }; + let oh = rt::open_how { + flags = (rt::O_RDONLY | rt::O_CLOEXEC | rt::O_DIRECTORY): u64, + ... + }; + let fd: int = match (rt::openat2(fs.dirfd, p: *[*]u8: *const char, + &oh, size(rt::open_how))) { + err: rt::errno => return errno_to_io(err), + n: int => n, + }; + + let iter = alloc(os_iterator { + iter = fs::iterator { + next = &iter_next, + }, + fd = fd, + ... + }); + return &iter.iter; +}; + +fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { + let iter = iter: *os_iterator; + if (iter.buf_pos >= iter.buf_end) { + let n = rt::getdents64(iter.fd, &iter.buf, BUFSIZ) as size; + if (n == 0) { + rt::close(iter.fd); + free(iter); + return; + }; + iter.buf_end = n; + iter.buf_pos = 0; + }; + let de = &iter.buf[iter.buf_pos]: *rt::dirent64; + iter.buf_pos += de.d_reclen; + let ln = strings::c_strlen(&de.d_name: *const char); + let name = &de.d_name: *[*]u8; + let name = if (!utf8::valid(name[..ln])) { + name[..ln]; + } else { + strings::from_utf8_unsafe(name[..ln]); + }; + + let ftype: fs::mode = switch (de.d_type) { + rt::DT_UNKNOWN => fs::mode::UNKNOWN, + rt::DT_FIFO => fs::mode::FIFO, + rt::DT_CHR => fs::mode::CHR, + rt::DT_DIR => fs::mode::DIR, + rt::DT_BLK => fs::mode::BLK, + rt::DT_REG => fs::mode::REG, + rt::DT_LNK => fs::mode::LINK, + rt::DT_SOCK => fs::mode::SOCK, + * => fs::mode::UNKNOWN, + }; + return fs::dirent { + name = name, + ftype = ftype, + }; +}; diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha @@ -152,6 +152,11 @@ export fn faccessat( }; }; +export fn getdents64(dirfd: int, dirp: *void, count: size) (size | errno) = { + return wrap_return(syscall3(SYS_getdents64, dirfd: u64, + dirp: uintptr: u64, count: u64))?: size; +}; + // The use of this function is discouraged, as it can create race conditions. // TOCTOU is preferred: attempt to simply use the resource you need and handle // any access errors which occur. diff --git a/rt/+linux/types.ha b/rt/+linux/types.ha @@ -242,3 +242,20 @@ export def RESOLVE_NO_MAGICLINKS: u64 = 0x02; export def RESOLVE_NO_SYMLINKS: u64 = 0x04; export def RESOLVE_BENEATH: u64 = 0x08; export def RESOLVE_IN_ROOT: u64 = 0x10; + +export def DT_UNKNOWN: u8 = 0; +export def DT_FIFO: u8 = 1; +export def DT_CHR: u8 = 2; +export def DT_DIR: u8 = 4; +export def DT_BLK: u8 = 6; +export def DT_REG: u8 = 8; +export def DT_LNK: u8 = 10; +export def DT_SOCK: u8 = 12; + +export type dirent64 = struct { + d_ino: ino_t, + d_off: off_t, + d_reclen: u16, + d_type: u8, + d_name: [*]char, +};