authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2022-07-30 23:41:24-07:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-01 19:38:05+03:00
loge7b6a1833106a5d808e4e82a2d61abf417aff407
tree004d9b0f4dc4fbc7957760ab0299eff52780933e
parentff125db53d8c18a63872ebdcdf6dd9653eb3f56b

std.fs: Split Iterator.next on Linux and WASI to allow for handling platform-specific errors

Follow up to #12226, implements the compromise detailed in https://github.com/ziglang/zig/issues/12211#issuecomment-1196011590

2 files changed, 34 insertions(+), 2 deletions(-)

lib/std/fs.zig+29-2
......@@ -595,6 +595,19 @@ pub const IterableDir = struct {
595595 /// Memory such as file names referenced in this returned entry becomes invalid
596596 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
597597 pub fn next(self: *Self) Error!?Entry {
598 return self.nextLinux() catch |err| switch (err) {
599 // To be consistent across platforms, iteration ends if the directory being iterated is deleted during iteration.
600 // This matches the behavior of non-Linux UNIX platforms.
601 error.DirNotFound => null,
602 else => |e| return e,
603 };
604 }
605
606 pub const ErrorLinux = error{DirNotFound} || IteratorError;
607
608 /// Implementation of `next` that can return `error.DirNotFound` if the directory being
609 /// iterated was deleted during iteration (this error is Linux specific).
610 pub fn nextLinux(self: *Self) ErrorLinux!?Entry {
598611 start_over: while (true) {
599612 if (self.index >= self.end_index) {
600613 if (self.first_iter) {
......@@ -607,7 +620,7 @@ pub const IterableDir = struct {
607620 .BADF => unreachable, // Dir is invalid or was opened without iteration ability
608621 .FAULT => unreachable,
609622 .NOTDIR => unreachable,
610 .NOENT => return null, // The directory being iterated was deleted during iteration.
623 .NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration.
611624 .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.
612625 else => |err| return os.unexpectedErrno(err),
613626 }
......@@ -729,6 +742,20 @@ pub const IterableDir = struct {
729742 /// Memory such as file names referenced in this returned entry becomes invalid
730743 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
731744 pub fn next(self: *Self) Error!?Entry {
745 return self.nextWasi() catch |err| switch (err) {
746 // To be consistent across platforms, iteration ends if the directory being iterated is deleted during iteration.
747 // This matches the behavior of non-Linux UNIX platforms.
748 error.DirNotFound => null,
749 else => |e| return e,
750 };
751 }
752
753 pub const ErrorWasi = error{DirNotFound} || IteratorError;
754
755 /// Implementation of `next` that can return platform-dependent errors depending on the host platform.
756 /// When the host platform is Linux, `error.DirNotFound` can be returned if the directory being
757 /// iterated was deleted during iteration.
758 pub fn nextWasi(self: *Self) ErrorWasi!?Entry {
732759 // We intentinally use fd_readdir even when linked with libc,
733760 // since its implementation is exactly the same as below,
734761 // and we avoid the code complexity here.
......@@ -742,7 +769,7 @@ pub const IterableDir = struct {
742769 .FAULT => unreachable,
743770 .NOTDIR => unreachable,
744771 .INVAL => unreachable,
745 .NOENT => return null, // The directory being iterated was deleted during iteration.
772 .NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration.
746773 .NOTCAPABLE => return error.AccessDenied,
747774 else => |err| return os.unexpectedErrno(err),
748775 }
lib/std/fs/test.zig+5
......@@ -241,6 +241,11 @@ test "Dir.Iterator but dir is deleted during iteration" {
241241 // Now, when we try to iterate, the next call should return null immediately.
242242 const entry = try iterator.next();
243243 try std.testing.expect(entry == null);
244
245 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`
246 if (builtin.os.tag == .linux) {
247 try std.testing.expectError(error.DirNotFound, iterator.nextLinux());
248 }
244249}
245250
246251fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {