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 {...@@ -595,6 +595,19 @@ pub const IterableDir = struct {
595 /// Memory such as file names referenced in this returned entry becomes invalid595 /// Memory such as file names referenced in this returned entry becomes invalid
596 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.596 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
597 pub fn next(self: *Self) Error!?Entry {597 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 {
598 start_over: while (true) {611 start_over: while (true) {
599 if (self.index >= self.end_index) {612 if (self.index >= self.end_index) {
600 if (self.first_iter) {613 if (self.first_iter) {
...@@ -607,7 +620,7 @@ pub const IterableDir = struct {...@@ -607,7 +620,7 @@ pub const IterableDir = struct {
607 .BADF => unreachable, // Dir is invalid or was opened without iteration ability620 .BADF => unreachable, // Dir is invalid or was opened without iteration ability
608 .FAULT => unreachable,621 .FAULT => unreachable,
609 .NOTDIR => unreachable,622 .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.
611 .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.624 .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.
612 else => |err| return os.unexpectedErrno(err),625 else => |err| return os.unexpectedErrno(err),
613 }626 }
...@@ -729,6 +742,20 @@ pub const IterableDir = struct {...@@ -729,6 +742,20 @@ pub const IterableDir = struct {
729 /// Memory such as file names referenced in this returned entry becomes invalid742 /// Memory such as file names referenced in this returned entry becomes invalid
730 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.743 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
731 pub fn next(self: *Self) Error!?Entry {744 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 {
732 // We intentinally use fd_readdir even when linked with libc,759 // We intentinally use fd_readdir even when linked with libc,
733 // since its implementation is exactly the same as below,760 // since its implementation is exactly the same as below,
734 // and we avoid the code complexity here.761 // and we avoid the code complexity here.
...@@ -742,7 +769,7 @@ pub const IterableDir = struct {...@@ -742,7 +769,7 @@ pub const IterableDir = struct {
742 .FAULT => unreachable,769 .FAULT => unreachable,
743 .NOTDIR => unreachable,770 .NOTDIR => unreachable,
744 .INVAL => unreachable,771 .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.
746 .NOTCAPABLE => return error.AccessDenied,773 .NOTCAPABLE => return error.AccessDenied,
747 else => |err| return os.unexpectedErrno(err),774 else => |err| return os.unexpectedErrno(err),
748 }775 }
lib/std/fs/test.zig+5
...@@ -241,6 +241,11 @@ test "Dir.Iterator but dir is deleted during iteration" {...@@ -241,6 +241,11 @@ test "Dir.Iterator but dir is deleted during iteration" {
241 // Now, when we try to iterate, the next call should return null immediately.241 // Now, when we try to iterate, the next call should return null immediately.
242 const entry = try iterator.next();242 const entry = try iterator.next();
243 try std.testing.expect(entry == null);243 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 }
244}249}
245250
246fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {251fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {