hare

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

commit 13bde14f31ef083217cc04e4ea07f66d1741379f
parent dfcad55c174ac57fb518038b705baf0ac7cb1b22
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 27 Feb 2021 10:45:25 -0500

rt: add path semantics and nul terminator handling

Diffstat:
Mos/+linux/dirfdfs.ha | 18++++--------------
Mos/+linux/fs.ha | 5+++--
Mos/+linux/open.ha | 4++--
Mos/exec/+linux.ha | 3++-
Mrt/+linux/stat.ha | 10++++++----
Mrt/+linux/syscalls.ha | 46+++++++++++++++++++++++++++++++++++++++-------
6 files changed, 56 insertions(+), 30 deletions(-)

diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha @@ -55,14 +55,6 @@ fn static_dirfdopen(fd: int, filesystem: *os_filesystem) *fs::fs = { return &filesystem.fs; }; -fn pathbytes(path: path::path) *const char = { - let p = match (path) { - s: str => strings::to_utf8(s), - b: []u8 => b, - }; - return p: *[*]u8: *const char; -}; - // Opens a file descriptor as an [fs::fs]. This file descriptor must be a // directory file. The file will be closed when the fs is closed. export fn dirfdopen(fd: int, resolve: resolve...) *fs::fs = { @@ -115,8 +107,7 @@ fn _fs_open( b: []u8 => "<open([]u8)>", }; - let fd = match (rt::openat2(fs.dirfd, pathbytes(path), - oh, size(rt::open_how))) { + let fd = match (rt::openat2(fs.dirfd, path, oh, size(rt::open_how))) { err: rt::errno => return errno_to_io(err), fd: int => fd, }; @@ -161,8 +152,7 @@ fn fs_create(fs: *fs::fs, path: path::path, mode: io::mode) (*io::stream | fs::e fn fs_stat(fs: *fs::fs, path: path::path) (fs::filestat | fs::error) = { let fs = fs: *os_filesystem; let st = rt::st { ... }; - match (rt::fstatat(fs.dirfd, pathbytes(path), - &st, rt::AT_SYMLINK_NOFOLLOW)) { + match (rt::fstatat(fs.dirfd, path, &st, rt::AT_SYMLINK_NOFOLLOW)) { err: rt::errno => return errno_to_io(err), void => void, }; @@ -186,7 +176,7 @@ fn fs_subdir(fs: *fs::fs, path: path::path) (*fs::fs | fs::error) = { ... }; - let fd: int = match (rt::openat2(fs.dirfd, pathbytes(path), + let fd: int = match (rt::openat2(fs.dirfd, path, &oh, size(rt::open_how))) { err: rt::errno => return errno_to_io(err), n: int => n, @@ -217,7 +207,7 @@ fn fs_iter(fs: *fs::fs, path: path::path) (*fs::iterator | fs::error) = { flags = (rt::O_RDONLY | rt::O_CLOEXEC | rt::O_DIRECTORY): u64, ... }; - let fd: int = match (rt::openat2(fs.dirfd, pathbytes(path), + let fd: int = match (rt::openat2(fs.dirfd, path, &oh, size(rt::open_how))) { err: rt::errno => { if (err: int == rt::ENOTDIR) { diff --git a/os/+linux/fs.ha b/os/+linux/fs.ha @@ -5,8 +5,9 @@ use strings; @init fn init() void = { static let root_fs = os_filesystem { ... }; - let dirfd = rt::open("/": *const char, - rt::O_RDONLY | rt::O_DIRECTORY | rt::O_CLOEXEC, 0u) as int; + let dirfd = rt::open("/", + rt::O_RDONLY | rt::O_DIRECTORY | rt::O_CLOEXEC, + 0u) as int; root = static_dirfdopen(dirfd, &root_fs); static let cwd_fs = os_filesystem { ... }; diff --git a/os/+linux/open.ha b/os/+linux/open.ha @@ -53,7 +53,7 @@ export fn open( iomode = io::mode::WRITE; }; - let fd: int = match (rt::open(pathbytes(path), oflags, 0u)) { + let fd: int = match (rt::open(path, oflags, 0u)) { err: rt::errno => return errno_to_io(err), n: int => n, }; @@ -91,7 +91,7 @@ export fn create( iomode = io::mode::WRITE; }; - let fd: int = match (rt::open(pathbytes(path), oflags, mode)) { + let fd: int = match (rt::open(path, oflags, mode)) { err: rt::errno => return errno_to_io(err), n: int => n, }; diff --git a/os/exec/+linux.ha b/os/exec/+linux.ha @@ -38,7 +38,7 @@ fn open(path: str) (platform | os_error) = { }; // O_PATH is used because it allows us to use an executable for which we // have execute permissions, but not read permissions. - return match (rt::open(path: *const char, rt::O_PATH, 0u)) { + return match (rt::open(path, rt::O_PATH, 0u)) { fd: int => fd, err: rt::errno => errno_to_os(err), }; @@ -49,6 +49,7 @@ fn platform_finish(cmd: *command) void = { }; fn platform_exec(cmd: *command) os_error = { + // TODO: These strings need to be NUL terminated let argv: []nullable *const char = alloc([], len(cmd.argv) + 1z); for (let i = 0z; i < len(cmd.argv); i += 1z) { append(argv, cmd.argv[i]: *const char); diff --git a/rt/+linux/stat.ha b/rt/+linux/stat.ha @@ -6,11 +6,12 @@ fn mkdev(major: u32, minor: u32) dev_t = fn fstatat_statx( dirfd: int, - path: *const char, + path: path, flags: int, mask: uint, statbuf: *stx, ) (void | errno) = { + let path = kpath(path)?; wrap_return(syscall5(SYS_statx, dirfd: u64, path: uintptr: u64, flags: u64, mask: u64, statbuf: uintptr: u64))?; @@ -19,10 +20,11 @@ fn fstatat_statx( export fn fstatat( dirfd: int, - path: *const char, + path: path, statbuf: *st, flags: int, ) (errno | void) = { + let path = kpath(path)?; let statxbuf = stx { ... }; fstatat_statx(dirfd, path, flags, STATX_BASIC_STATS, &statxbuf)?; statbuf.dev = mkdev(statxbuf.dev_major, statxbuf.dev_minor); @@ -43,11 +45,11 @@ export fn fstatat( statbuf.ctime.tv_nsec = statxbuf.ctime.tv_nsec: i64; }; -export fn stat(path: *const char, statbuf: *st) (errno | void) = +export fn stat(path: path, statbuf: *st) (errno | void) = fstatat(AT_FDCWD, path, statbuf, 0); export fn fstat(fd: int, statbuf: *st) (errno | void) = fstatat(fd, "", statbuf, AT_EMPTY_PATH); -export fn lstat(path: *const char, statbuf: *st) (errno | void) = +export fn lstat(path: path, statbuf: *st) (errno | void) = fstatat(AT_FDCWD, path, statbuf, AT_SYMLINK_NOFOLLOW); diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha @@ -6,6 +6,32 @@ fn syscall4(u64, u64, u64, u64, u64) u64; fn syscall5(u64, u64, u64, u64, u64, u64) u64; fn syscall6(u64, u64, u64, u64, u64, u64, u64) u64; +export type path = (str | []u8 | *const char); + +// NUL terminates a string and stores it in a static buffer of PATH_MAX+1 bytes +// in length. +fn kpath(path: path) (*const char | errno) = { + static let buf: [4097]u8 = [0...]; + let path = match (path) { + c: *const char => return c, + s: str => { + let ptr = &s: *struct { + buf: *[*]u8, + length: size, + capacity: size, + }; + ptr.buf[..ptr.length]; + }, + b: []u8 => b, + }; + if (len(path) + 1 >= len(buf)) { + return ENAMETOOLONG; + }; + memcpy(&buf, path: *[*]u8, len(path)); + buf[len(path)] = 0; + return &buf: *const char; +}; + export fn read(fd: int, buf: *void, count: size) (size | errno) = { return wrap_return(syscall3(SYS_read, fd: u64, buf: uintptr: u64, count: u64))?: size; @@ -16,17 +42,19 @@ export fn write(fd: int, buf: *const void, count: size) (size | errno) = { fd: u64, buf: uintptr: u64, count: u64))?: size; }; -export fn open(path: *const char, flags: int, mode: uint) (int | errno) = { +export fn open(path: path, flags: int, mode: uint) (int | errno) = { + let path = kpath(path)?; return wrap_return(syscall4(SYS_openat, AT_FDCWD: u64, path: uintptr: u64, flags: u64, mode: u64))?: int; }; export fn openat2( dirfd: int, - path: *const char, + path: path, how: *open_how, how_sz: size, ) (int | errno) = { + let path = kpath(path)?; return wrap_return(syscall4(SYS_openat2, dirfd: u64, path: uintptr: u64, how: uintptr: u64, how_sz: u64))?: int; }; @@ -40,7 +68,8 @@ export fn close(fd: int) (void | errno) = { return; }; -export fn chdir(path: *const char) (void | errno) = { +export fn chdir(path: path) (void | errno) = { + let path = kpath(path)?; wrap_return(syscall1(SYS_chdir, path: uintptr: u64))?; return; }; @@ -50,13 +79,15 @@ export fn fchdir(fd: int) (void | errno) = { return; }; -export fn chroot(path: *const char) (void | errno) = { +export fn chroot(path: path) (void | errno) = { + let path = kpath(path)?; wrap_return(syscall1(SYS_chroot, path: uintptr: u64))?; return; }; -export fn execveat(dirfd: int, path: *const char, argv: *[*]nullable *const char, +export fn execveat(dirfd: int, path: path, argv: *[*]nullable *const char, envp: *[*]nullable *const char, flags: int) errno = { + let path = kpath(path)?; return match (wrap_return(syscall5(SYS_execveat, dirfd: u64, path: uintptr: u64, argv: uintptr: u64, envp: uintptr: u64, flags: u64))) { @@ -140,10 +171,11 @@ export fn lseek(fd: int, off: i64, whence: uint) (i64 | errno) = { // any access errors which occur. export fn faccessat( dirfd: int, - path: *const char, + path: path, mode: int, flags: int, ) (bool | errno) = { + let path = kpath(path)?; return match (wrap_return(syscall4(SYS_faccessat2, dirfd: u64, path: uintptr: u64, mode: u64, flags: u64))) { err: errno => switch (err) { @@ -165,7 +197,7 @@ export fn getdents64(dirfd: int, dirp: *void, count: size) (size | errno) = { // 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. -export fn access(path: *const char, mode: int) (bool | errno) = +export fn access(path: path, mode: int) (bool | errno) = faccessat(AT_FDCWD, path, mode, 0); export type fcntl_arg = (void | int | *st_flock | *f_owner_ex | *u64);