Commit af835111fa

Ryan Liptak <squeek502@hotmail.com>
2023-06-08 07:50:45
Allow recovering from Walker.next errors without continually hitting the same error
Before this commit, if Walker.next errored with e.g. `error.AccessDenied` and the caller did something like `while (true) { walker.next() catch continue; }`, then the directory that errored with AccessDenied would be continually iterated in each `next` call and error every time with AccessDenied. After this commit, the directory that errored will be popped off the stack before the error is returned, meaning that in the subsequent `next` call, it won't be retried and the Walker will continue with whatever directories remain on its stack. For a real example, before this commit, walking `/proc/` on my system would infinitely loop due to repeated AccessDenied errors on the same directory. After this commit, I am able to walk `/proc/` on my system fully (skipping over any directories that are unable to be iterated).
1 parent 7555085
Changed files (1)
lib
std
lib/std/fs.zig
@@ -958,7 +958,17 @@ pub const IterableDir = struct {
                 var top = &self.stack.items[self.stack.items.len - 1];
                 var containing = top;
                 var dirname_len = top.dirname_len;
-                if (try top.iter.next()) |base| {
+                if (top.iter.next() catch |err| {
+                    // If we get an error, then we want the user to be able to continue
+                    // walking if they want, which means that we need to pop the directory
+                    // that errored from the stack. Otherwise, all future `next` calls would
+                    // likely just fail with the same error.
+                    var item = self.stack.pop();
+                    if (self.stack.items.len != 0) {
+                        item.iter.dir.close();
+                    }
+                    return err;
+                }) |base| {
                     self.name_buffer.shrinkRetainingCapacity(dirname_len);
                     if (self.name_buffer.items.len != 0) {
                         try self.name_buffer.append(path.sep);