mirror of
https://github.com/ziglang/zig.git
synced 2026-01-21 06:45:24 +00:00
std.fs: End iteration on Linux/WASI during Iterator.next when hitting ENOENT
`getdents` on Linux can return `ENOENT` if the directory referred to by the fd is deleted during iteration. Returning null when this happens makes sense because: - `ENOENT` is specific to the Linux implementation of `getdents` - On other platforms like FreeBSD, `getdents` returns `0` in this scenario, which is functionally equivalent to the `.NOENT => return null` handling on Linux - In all the usage sites of `Iterator.next` throughout the standard library, translating `ENOENT` returned from `next` as null was the best way to handle it, so the use-case for handling the exact `ENOENT` scenario specifically may not exist to a relevant extent Previously, ENOENT being returned would trigger `os.unexpectedErrno`. Closes #12211
This commit is contained in:
parent
8f3ab96b0e
commit
75e5b38410
@ -607,6 +607,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.
|
||||
.INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.
|
||||
else => |err| return os.unexpectedErrno(err),
|
||||
}
|
||||
@ -741,6 +742,7 @@ pub const IterableDir = struct {
|
||||
.FAULT => unreachable,
|
||||
.NOTDIR => unreachable,
|
||||
.INVAL => unreachable,
|
||||
.NOENT => return null, // The directory being iterated was deleted during iteration.
|
||||
.NOTCAPABLE => return error.AccessDenied,
|
||||
else => |err| return os.unexpectedErrno(err),
|
||||
}
|
||||
|
||||
@ -219,6 +219,30 @@ test "Dir.Iterator twice" {
|
||||
}
|
||||
}
|
||||
|
||||
test "Dir.Iterator but dir is deleted during iteration" {
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
// Create directory and setup an iterator for it
|
||||
var iterable_subdir = try tmp.dir.makeOpenPathIterable("subdir", .{});
|
||||
defer iterable_subdir.close();
|
||||
|
||||
var iterator = iterable_subdir.iterate();
|
||||
|
||||
// Create something to iterate over within the subdir
|
||||
try tmp.dir.makePath("subdir/b");
|
||||
|
||||
// Then, before iterating, delete the directory that we're iterating.
|
||||
// This is a contrived reproduction, but this could happen outside of the program, in another thread, etc.
|
||||
// If we get an error while trying to delete, we can skip this test (this will happen on platforms
|
||||
// like Windows which will give FileBusy if the directory is currently open for iteration).
|
||||
tmp.dir.deleteTree("subdir") catch return error.SkipZigTest;
|
||||
|
||||
// Now, when we try to iterate, the next call should return null immediately.
|
||||
const entry = try iterator.next();
|
||||
try std.testing.expect(entry == null);
|
||||
}
|
||||
|
||||
fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {
|
||||
return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user