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:
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);