commit 858e98e6a733b11c7befea2690cbe5952886de10
parent f9afe6f169063ed90a1df8234cc9c6373aba45d3
Author: Sertonix <sertonix@posteo.net>
Date: Wed, 13 Dec 2023 19:57:58 +0000
os: handle errors on file system iteration
Implements: https://todo.sr.ht/~sircmpwn/hare/914
Signed-off-by: Sertonix <sertonix@posteo.net>
Diffstat:
22 files changed, 64 insertions(+), 37 deletions(-)
diff --git a/cmd/hare/build/gather.ha b/cmd/hare/build/gather.ha
@@ -51,7 +51,7 @@ fn gather_submodules(
let n = 0z;
let it = os::iter(path::string(buf))?;
defer fs::finish(it);
- for (true) match (module::next(it)) {
+ for (true) match (module::next(it)?) {
case void =>
break;
case let dir: fs::dirent =>
diff --git a/cmd/hare/cache.ha b/cmd/hare/cache.ha
@@ -51,7 +51,7 @@ fn dirsize(buf: *path::buffer) (size | error) = {
let s = 0z;
let it = os::iter(path::string(buf))?;
defer fs::finish(it);
- for (true) match (fs::next(it)) {
+ for (true) match (fs::next(it)?) {
case void =>
break;
case let d: fs::dirent =>
diff --git a/cmd/haredoc/doc/types.ha b/cmd/haredoc/doc/types.ha
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
// (c) Hare authors <https://harelang.org>
+use encoding::utf8;
use fmt;
use fs;
use hare::ast;
@@ -14,7 +15,8 @@ use os::exec;
export type haredoc_colors_error = !str;
export type error = !(lex::error | parse::error | doc::error | io::error |
- module::error | exec::error | fs::error | haredoc_colors_error);
+ module::error | exec::error | fs::error | haredoc_colors_error |
+ utf8::invalid);
export fn strerror(err: error) str = {
match (err) {
@@ -32,6 +34,8 @@ export fn strerror(err: error) str = {
return exec::strerror(err);
case let err: fs::error =>
return fs::strerror(err);
+ case let err: utf8::invalid =>
+ return utf8::strerror(err);
case let err: haredoc_colors_error =>
def ERRMSG = "Error parsing HAREDOC_COLORS: invalid key";
if (len(err) == 0) {
diff --git a/cmd/haredoc/doc/util.ha b/cmd/haredoc/doc/util.ha
@@ -34,7 +34,7 @@ export fn submodules(path: str, show_undocumented: bool) ([]str | error) = {
let it = os::iter(path)?;
defer fs::finish(it);
let pathbuf = path::init(path)!;
- for (true) match (module::next(it)) {
+ for (true) match (module::next(it)?) {
case let d: fs::dirent =>
path::set(&pathbuf, path, d.name, "README")!;
if (show_undocumented || os::exists(path::string(&pathbuf))) {
diff --git a/cmd/parsechk/main.ha b/cmd/parsechk/main.ha
@@ -22,7 +22,7 @@ export fn main() void = {
fn iter(buf: *path::buffer, status: *int) void = {
let it = os::iter(path::string(buf))!;
defer fs::finish(it);
- for (true) match (fs::next(it)) {
+ for (true) match (fs::next(it)!) {
case void =>
break;
case let ent: fs::dirent =>
diff --git a/fs/fs.ha b/fs/fs.ha
@@ -302,7 +302,8 @@ export fn symlink(fs: *fs, target: str, path: str) (void | error) = {
// Returns the next directory entry from an iterator, or void if none remain.
// '.' and '..' are skipped. It is a programming error to call this again after
-// it has returned void. The file stat returned may only have the type bits set
-// on the file mode; callers should call [[stat]] to obtain the detailed file
-// mode.
-export fn next(iter: *iterator) (dirent | void) = iter.next(iter);
+// it has returned void. Calling this again after an error is safe. The list is
+// not guaranteed to be complete when an error has been returned. The file stat
+// returned may only have the type bits set on the file mode; callers should
+// call [[stat]] to obtain the detailed file mode.
+export fn next(iter: *iterator) (dirent | void | error) = iter.next(iter);
diff --git a/fs/types.ha b/fs/types.ha
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
+use encoding::utf8;
use errors;
use io;
use path;
@@ -25,6 +26,7 @@ export type error = !(
errors::busy |
errors::invalid |
errors::unsupported |
+ utf8::invalid |
wrongtype |
cannotrename |
io::error);
@@ -47,6 +49,8 @@ case errors::busy =>
yield "Device is busy";
case errors::unsupported =>
yield "Operation not supported";
+case let err: utf8::invalid =>
+ yield utf8::strerror(err);
case let err: io::error =>
yield io::strerror(err);
};
@@ -304,7 +308,7 @@ export type fs = struct {
};
// A function which returns the next directory from an [[iterator]].
-export type nextfunc = fn(iter: *iterator) (dirent | void);
+export type nextfunc = fn(iter: *iterator) (dirent | void | error);
// A function which frees state associated with an [[iterator]].
export type finishfunc = fn(iter: *iterator) void;
diff --git a/glob/glob.ha b/glob/glob.ha
@@ -94,11 +94,9 @@ export fn next(gen: *generator) (str | void | failure) = {
&& len(memio::string(&gen.tmpp.dir)!) == 0
&& len(memio::string(&gen.tmpp.pat)!) == 0
&& len(memio::string(&gen.tmpp.rem)!) == 0;
- match (next_match(gen)) {
+ match (next_match(gen)?) {
case let s: str =>
return s;
- case let f: failure =>
- return f;
case void => void;
};
if (init && gen.flgs & flag::NOCHECK != 0) {
@@ -188,6 +186,11 @@ fn next_match(gen: *generator) (str | void | failure) = {
};
};
gen.matc += 1;
+ case let e: fs::error =>
+ return failure {
+ path = dir,
+ error = e,
+ };
};
if (gen.flgs & flag::NOSORT == 0) {
strstack_sort(&gen.pats, l);
diff --git a/hare/module/srcs.ha b/hare/module/srcs.ha
@@ -277,7 +277,7 @@ fn _findsrcs(
return attach(strings::dup(pathstr), e);
};
defer fs::finish(iter);
- for (true) match (fs::next(iter)) {
+ for (true) match (fs::next(iter)?) {
case void =>
break;
case let d: fs::dirent =>
diff --git a/hare/module/util.ha b/hare/module/util.ha
@@ -44,8 +44,8 @@ export fn outdated(target: str, deps: []str, mtime: time::instant) bool = {
};
// Wrapper for [[fs::next]] that only returns valid submodule directories.
-export fn next(it: *fs::iterator) (fs::dirent | void) = {
- for (true) match (fs::next(it)) {
+export fn next(it: *fs::iterator) (fs::dirent | void | fs::error) = {
+ for (true) match (fs::next(it)?) {
case void =>
return void;
case let d: fs::dirent =>
diff --git a/makefiles/freebsd.aarch64.mk b/makefiles/freebsd.aarch64.mk
@@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
fs_ha = fs/fs.ha fs/types.ha fs/util.ha
-$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
diff --git a/makefiles/freebsd.riscv64.mk b/makefiles/freebsd.riscv64.mk
@@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
fs_ha = fs/fs.ha fs/types.ha fs/util.ha
-$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
diff --git a/makefiles/freebsd.x86_64.mk b/makefiles/freebsd.x86_64.mk
@@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
fs_ha = fs/fs.ha fs/types.ha fs/util.ha
-$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
diff --git a/makefiles/linux.aarch64.mk b/makefiles/linux.aarch64.mk
@@ -142,7 +142,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/linux_vdso.td $(HARECACHE)/math.t
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
fs_ha = fs/fs.ha fs/types.ha fs/util.ha
-$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
diff --git a/makefiles/linux.riscv64.mk b/makefiles/linux.riscv64.mk
@@ -142,7 +142,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/linux_vdso.td $(HARECACHE)/math.t
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
fs_ha = fs/fs.ha fs/types.ha fs/util.ha
-$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
diff --git a/makefiles/linux.x86_64.mk b/makefiles/linux.x86_64.mk
@@ -142,7 +142,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/linux_vdso.td $(HARECACHE)/math.t
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
fs_ha = fs/fs.ha fs/types.ha fs/util.ha
-$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
diff --git a/makefiles/openbsd.aarch64.mk b/makefiles/openbsd.aarch64.mk
@@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
fs_ha = fs/fs.ha fs/types.ha fs/util.ha
-$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
diff --git a/makefiles/openbsd.riscv64.mk b/makefiles/openbsd.riscv64.mk
@@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
fs_ha = fs/fs.ha fs/types.ha fs/util.ha
-$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
diff --git a/makefiles/openbsd.x86_64.mk b/makefiles/openbsd.x86_64.mk
@@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
fs_ha = fs/fs.ha fs/types.ha fs/util.ha
-$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
diff --git a/os/+freebsd/dirfdfs.ha b/os/+freebsd/dirfdfs.ha
@@ -372,11 +372,16 @@ fn fs_iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = {
return &iter.iter;
};
-fn iter_next(iter: *fs::iterator) (fs::dirent | void) = {
+fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = {
let iter = iter: *os_iterator;
if (iter.buf_pos >= iter.buf_end) {
- let n = rt::getdents(iter.fd,
- iter.buf: *[*]u8, len(iter.buf)) as size;
+ let n = match (rt::getdents(iter.fd,
+ iter.buf: *[*]u8, len(iter.buf))) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case let n: size =>
+ yield n;
+ };
if (n == 0) {
return;
};
@@ -385,7 +390,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = {
};
let de = &iter.buf[iter.buf_pos]: *rt::freebsd11_dirent;
iter.buf_pos += de.d_reclen;
- let name = c::tostr(&de.d_name: *const c::char)!;
+ let name = c::tostr(&de.d_name: *const c::char)?;
if (name == "." || name == "..") {
return iter_next(iter);
};
diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha
@@ -415,11 +415,16 @@ fn fs_iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = {
return &iter.iter;
};
-fn iter_next(iter: *fs::iterator) (fs::dirent | void) = {
+fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = {
let iter = iter: *os_iterator;
if (iter.buf_pos >= iter.buf_end) {
- let n = rt::getdents64(iter.fd,
- iter.buf: *[*]u8, len(iter.buf)) as size;
+ let n = match (rt::getdents64(iter.fd,
+ iter.buf: *[*]u8, len(iter.buf))) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case let n: size =>
+ yield n;
+ };
if (n == 0) {
return;
};
@@ -428,7 +433,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = {
};
let de = &iter.buf[iter.buf_pos]: *rt::dirent64;
iter.buf_pos += de.d_reclen;
- let name = c::tostr(&de.d_name: *const c::char)!;
+ let name = c::tostr(&de.d_name: *const c::char)?;
if (name == "." || name == "..") {
return iter_next(iter);
};
diff --git a/os/+openbsd/dirfdfs.ha b/os/+openbsd/dirfdfs.ha
@@ -175,13 +175,18 @@ type os_iterator = struct {
buf: []u8,
};
-fn iter_next(iter: *fs::iterator) (fs::dirent | void) = {
+fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = {
let iter = iter: *os_iterator;
for (true) {
if (iter.buf_pos >= iter.buf_end) {
- let n = rt::getdents(iter.fd,
- iter.buf: *[*]u8, len(iter.buf)) as int;
+ let n = match (rt::getdents(iter.fd,
+ iter.buf: *[*]u8, len(iter.buf))) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case let n: int =>
+ yield n;
+ };
if (n == 0) {
return;
};
@@ -197,7 +202,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = {
continue;
};
- let name = c::tostr(&de.d_name: *const c::char)!;
+ let name = c::tostr(&de.d_name: *const c::char)?;
if (name == "." || name == "..") {
continue;
};