| ... | @@ -2046,41 +2046,38 @@ pub const Dir = struct { | ... | @@ -2046,41 +2046,38 @@ pub const Dir = struct { |
| 2046 | /// this function recursively removes its entries and then tries again. | 2046 | /// this function recursively removes its entries and then tries again. |
| 2047 | /// This operation is not atomic on most file systems. | 2047 | /// This operation is not atomic on most file systems. |
| 2048 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { | 2048 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { |
| 2049 | start_over: while (true) { | 2049 | // First, try deleting the item as a file. This way we don't follow sym links. |
| 2050 | var got_access_denied = false; | 2050 | if (self.deleteFile(sub_path)) { |
| 2051 | | 2051 | return; |
| 2052 | // First, try deleting the item as a file. This way we don't follow sym links. | 2052 | } else |err| switch (err) { |
| 2053 | if (self.deleteFile(sub_path)) { | 2053 | error.FileNotFound => return, |
| 2054 | return; | 2054 | error.IsDir => {}, |
| 2055 | } else |err| switch (err) { | 2055 | error.AccessDenied, |
| 2056 | error.FileNotFound => return, | 2056 | error.InvalidUtf8, |
| 2057 | error.IsDir => {}, | 2057 | error.SymLinkLoop, |
| 2058 | error.AccessDenied => got_access_denied = true, | 2058 | error.NameTooLong, |
| | 2059 | error.SystemResources, |
| | 2060 | error.ReadOnlyFileSystem, |
| | 2061 | error.NotDir, |
| | 2062 | error.FileSystem, |
| | 2063 | error.FileBusy, |
| | 2064 | error.BadPathName, |
| | 2065 | error.Unexpected, |
| | 2066 | => |e| return e, |
| | 2067 | } |
| 2059 | | 2068 | |
| 2060 | error.InvalidUtf8, | 2069 | start_over: while (true) { |
| 2061 | error.SymLinkLoop, | | |
| 2062 | error.NameTooLong, | | |
| 2063 | error.SystemResources, | | |
| 2064 | error.ReadOnlyFileSystem, | | |
| 2065 | error.NotDir, | | |
| 2066 | error.FileSystem, | | |
| 2067 | error.FileBusy, | | |
| 2068 | error.BadPathName, | | |
| 2069 | error.Unexpected, | | |
| 2070 | => |e| return e, | | |
| 2071 | } | | |
| 2072 | var iterable_dir = self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) { | 2070 | var iterable_dir = self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) { |
| 2073 | error.NotDir => { | 2071 | error.NotDir => { |
| 2074 | if (got_access_denied) { | 2072 | // Somehow the sub_path got changed into a file while we were trying to delete the tree. |
| 2075 | return error.AccessDenied; | 2073 | // This implies that the dir at the sub_path was deleted at some point so we consider this |
| 2076 | } | 2074 | // as a successful delete and return. |
| 2077 | continue :start_over; | 2075 | return; |
| 2078 | }, | 2076 | }, |
| 2079 | error.FileNotFound => { | 2077 | error.FileNotFound => { |
| 2080 | // That's fine, we were trying to remove this directory anyway. | 2078 | // That's fine, we were trying to remove this directory anyway. |
| 2081 | continue :start_over; | 2079 | return; |
| 2082 | }, | 2080 | }, |
| 2083 | | | |
| 2084 | error.InvalidHandle, | 2081 | error.InvalidHandle, |
| 2085 | error.AccessDenied, | 2082 | error.AccessDenied, |
| 2086 | error.SymLinkLoop, | 2083 | error.SymLinkLoop, |
| ... | @@ -2112,63 +2109,69 @@ pub const Dir = struct { | ... | @@ -2112,63 +2109,69 @@ pub const Dir = struct { |
| 2112 | // open it, and close the original directory. Repeat. Then start the entire operation over. | 2109 | // open it, and close the original directory. Repeat. Then start the entire operation over. |
| 2113 | | 2110 | |
| 2114 | scan_dir: while (true) { | 2111 | scan_dir: while (true) { |
| 2115 | var dir_it = iterable_dir.iterate(); | 2112 | var dir_it = iterable_dir.iterateAssumeFirstIteration(); |
| 2116 | while (try dir_it.next()) |entry| { | 2113 | dir_it: while (try dir_it.next()) |entry| { |
| 2117 | if (iterable_dir.dir.deleteFile(entry.name)) { | 2114 | var treat_as_dir = entry.kind == .Directory; |
| 2118 | continue; | 2115 | handle_entry: while (true) { |
| 2119 | } else |err| switch (err) { | 2116 | if (treat_as_dir) { |
| 2120 | error.FileNotFound => continue, | 2117 | const new_dir = iterable_dir.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) { |
| 2121 | | 2118 | error.NotDir => { |
| 2122 | // Impossible because we do not pass any path separators. | 2119 | treat_as_dir = false; |
| 2123 | error.NotDir => unreachable, | 2120 | continue :handle_entry; |
| 2124 | | 2121 | }, |
| 2125 | error.IsDir => {}, | 2122 | error.FileNotFound => { |
| 2126 | error.AccessDenied => got_access_denied = true, | 2123 | // That's fine, we were trying to remove this directory anyway. |
| 2127 | | 2124 | continue :dir_it; |
| 2128 | error.InvalidUtf8, | 2125 | }, |
| 2129 | error.SymLinkLoop, | 2126 | |
| 2130 | error.NameTooLong, | 2127 | error.InvalidHandle, |
| 2131 | error.SystemResources, | 2128 | error.AccessDenied, |
| 2132 | error.ReadOnlyFileSystem, | 2129 | error.SymLinkLoop, |
| 2133 | error.FileSystem, | 2130 | error.ProcessFdQuotaExceeded, |
| 2134 | error.FileBusy, | 2131 | error.NameTooLong, |
| 2135 | error.BadPathName, | 2132 | error.SystemFdQuotaExceeded, |
| 2136 | error.Unexpected, | 2133 | error.NoDevice, |
| 2137 | => |e| return e, | 2134 | error.SystemResources, |
| 2138 | } | 2135 | error.Unexpected, |
| 2139 | | 2136 | error.InvalidUtf8, |
| 2140 | const new_dir = iterable_dir.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) { | 2137 | error.BadPathName, |
| 2141 | error.NotDir => { | 2138 | error.DeviceBusy, |
| 2142 | if (got_access_denied) { | 2139 | => |e| return e, |
| 2143 | return error.AccessDenied; | 2140 | }; |
| 2144 | } | 2141 | if (cleanup_dir_parent) |*d| d.close(); |
| | 2142 | cleanup_dir_parent = iterable_dir; |
| | 2143 | iterable_dir = new_dir; |
| | 2144 | mem.copy(u8, &dir_name_buf, entry.name); |
| | 2145 | dir_name = dir_name_buf[0..entry.name.len]; |
| 2145 | continue :scan_dir; | 2146 | continue :scan_dir; |
| 2146 | }, | 2147 | } else { |
| 2147 | error.FileNotFound => { | 2148 | if (iterable_dir.dir.deleteFile(entry.name)) { |
| 2148 | // That's fine, we were trying to remove this directory anyway. | 2149 | continue :dir_it; |
| 2149 | continue :scan_dir; | 2150 | } else |err| switch (err) { |
| 2150 | }, | 2151 | error.FileNotFound => continue :dir_it, |
| 2151 | | 2152 | |
| 2152 | error.InvalidHandle, | 2153 | // Impossible because we do not pass any path separators. |
| 2153 | error.AccessDenied, | 2154 | error.NotDir => unreachable, |
| 2154 | error.SymLinkLoop, | 2155 | |
| 2155 | error.ProcessFdQuotaExceeded, | 2156 | error.IsDir => { |
| 2156 | error.NameTooLong, | 2157 | treat_as_dir = true; |
| 2157 | error.SystemFdQuotaExceeded, | 2158 | continue :handle_entry; |
| 2158 | error.NoDevice, | 2159 | }, |
| 2159 | error.SystemResources, | 2160 | |
| 2160 | error.Unexpected, | 2161 | error.AccessDenied, |
| 2161 | error.InvalidUtf8, | 2162 | error.InvalidUtf8, |
| 2162 | error.BadPathName, | 2163 | error.SymLinkLoop, |
| 2163 | error.DeviceBusy, | 2164 | error.NameTooLong, |
| 2164 | => |e| return e, | 2165 | error.SystemResources, |
| 2165 | }; | 2166 | error.ReadOnlyFileSystem, |
| 2166 | if (cleanup_dir_parent) |*d| d.close(); | 2167 | error.FileSystem, |
| 2167 | cleanup_dir_parent = iterable_dir; | 2168 | error.FileBusy, |
| 2168 | iterable_dir = new_dir; | 2169 | error.BadPathName, |
| 2169 | mem.copy(u8, &dir_name_buf, entry.name); | 2170 | error.Unexpected, |
| 2170 | dir_name = dir_name_buf[0..entry.name.len]; | 2171 | => |e| return e, |
| 2171 | continue :scan_dir; | 2172 | } |
| | 2173 | } |
| | 2174 | } |
| 2172 | } | 2175 | } |
| 2173 | // Reached the end of the directory entries, which means we successfully deleted all of them. | 2176 | // Reached the end of the directory entries, which means we successfully deleted all of them. |
| 2174 | // Now to remove the directory itself. | 2177 | // Now to remove the directory itself. |