diff options
| author | Ryan Liptak <squeek502@hotmail.com> | 2022-07-30 23:41:24 -0700 |
|---|---|---|
| committer | Veikka Tuominen <git@vexu.eu> | 2022-08-01 19:38:05 +0300 |
| commit | e7b6a1833106a5d808e4e82a2d61abf417aff407 (patch) | |
| tree | 004d9b0f4dc4fbc7957760ab0299eff52780933e /lib/std | |
| parent | ff125db53d8c18a63872ebdcdf6dd9653eb3f56b (diff) | |
| download | zig-e7b6a1833106a5d808e4e82a2d61abf417aff407.tar.gz zig-e7b6a1833106a5d808e4e82a2d61abf417aff407.zip | |
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
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/fs.zig | 31 | ||||
| -rw-r--r-- | lib/std/fs/test.zig | 5 |
2 files changed, 34 insertions, 2 deletions
diff --git a/lib/std/fs.zig b/lib/std/fs.zig index c96a118399..0968e16812 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -595,6 +595,19 @@ pub const IterableDir = struct { /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. pub fn next(self: *Self) Error!?Entry { + return self.nextLinux() catch |err| switch (err) { + // To be consistent across platforms, iteration ends if the directory being iterated is deleted during iteration. + // This matches the behavior of non-Linux UNIX platforms. + error.DirNotFound => null, + else => |e| return e, + }; + } + + pub const ErrorLinux = error{DirNotFound} || IteratorError; + + /// Implementation of `next` that can return `error.DirNotFound` if the directory being + /// iterated was deleted during iteration (this error is Linux specific). + pub fn nextLinux(self: *Self) ErrorLinux!?Entry { start_over: while (true) { if (self.index >= self.end_index) { if (self.first_iter) { @@ -607,7 +620,7 @@ pub const IterableDir = struct { .BADF => unreachable, // Dir is invalid or was opened without iteration ability .FAULT => unreachable, .NOTDIR => unreachable, - .NOENT => return null, // The directory being iterated was deleted during iteration. + .NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration. .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net. else => |err| return os.unexpectedErrno(err), } @@ -729,6 +742,20 @@ pub const IterableDir = struct { /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. pub fn next(self: *Self) Error!?Entry { + return self.nextWasi() catch |err| switch (err) { + // To be consistent across platforms, iteration ends if the directory being iterated is deleted during iteration. + // This matches the behavior of non-Linux UNIX platforms. + error.DirNotFound => null, + else => |e| return e, + }; + } + + pub const ErrorWasi = error{DirNotFound} || IteratorError; + + /// Implementation of `next` that can return platform-dependent errors depending on the host platform. + /// When the host platform is Linux, `error.DirNotFound` can be returned if the directory being + /// iterated was deleted during iteration. + pub fn nextWasi(self: *Self) ErrorWasi!?Entry { // We intentinally use fd_readdir even when linked with libc, // since its implementation is exactly the same as below, // and we avoid the code complexity here. @@ -742,7 +769,7 @@ pub const IterableDir = struct { .FAULT => unreachable, .NOTDIR => unreachable, .INVAL => unreachable, - .NOENT => return null, // The directory being iterated was deleted during iteration. + .NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration. .NOTCAPABLE => return error.AccessDenied, else => |err| return os.unexpectedErrno(err), } diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index bedec7d4ad..538ce1bf5e 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -241,6 +241,11 @@ test "Dir.Iterator but dir is deleted during iteration" { // Now, when we try to iterate, the next call should return null immediately. const entry = try iterator.next(); try std.testing.expect(entry == null); + + // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux` + if (builtin.os.tag == .linux) { + try std.testing.expectError(error.DirNotFound, iterator.nextLinux()); + } } fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool { |
