| ... | ... | @@ -1050,15 +1050,16 @@ const DeleteTreeError = error { |
| 1050 | 1050 | }; |
| 1051 | 1051 | pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void { |
| 1052 | 1052 | start_over: while (true) { |
| 1053 | var got_access_denied = false; |
| 1053 | 1054 | // First, try deleting the item as a file. This way we don't follow sym links. |
| 1054 | 1055 | if (deleteFile(allocator, full_path)) { |
| 1055 | 1056 | return; |
| 1056 | 1057 | } else |err| switch (err) { |
| 1057 | 1058 | error.FileNotFound => return, |
| 1058 | 1059 | error.IsDir => {}, |
| 1060 | error.AccessDenied => got_access_denied = true, |
| 1059 | 1061 | |
| 1060 | 1062 | error.OutOfMemory, |
| 1061 | | error.AccessDenied, |
| 1062 | 1063 | error.SymLinkLoop, |
| 1063 | 1064 | error.NameTooLong, |
| 1064 | 1065 | error.SystemResources, |
| ... | ... | @@ -1071,7 +1072,12 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! |
| 1071 | 1072 | } |
| 1072 | 1073 | { |
| 1073 | 1074 | var dir = Dir.open(allocator, full_path) catch |err| switch (err) { |
| 1074 | | error.NotDir => continue :start_over, |
| 1075 | error.NotDir => { |
| 1076 | if (got_access_denied) { |
| 1077 | return error.AccessDenied; |
| 1078 | } |
| 1079 | continue :start_over; |
| 1080 | }, |
| 1075 | 1081 | |
| 1076 | 1082 | error.OutOfMemory, |
| 1077 | 1083 | error.AccessDenied, |
| ... | ... | @@ -1109,18 +1115,16 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! |
| 1109 | 1115 | } |
| 1110 | 1116 | |
| 1111 | 1117 | pub const Dir = struct { |
| 1112 | | // See man getdents |
| 1113 | 1118 | fd: i32, |
| 1119 | darwin_seek: darwin_seek_t, |
| 1114 | 1120 | allocator: &Allocator, |
| 1115 | 1121 | buf: []u8, |
| 1116 | 1122 | index: usize, |
| 1117 | 1123 | end_index: usize, |
| 1118 | 1124 | |
| 1119 | | const LinuxEntry = extern struct { |
| 1120 | | d_ino: usize, |
| 1121 | | d_off: usize, |
| 1122 | | d_reclen: u16, |
| 1123 | | d_name: u8, // field address is the address of first byte of name |
| 1125 | const darwin_seek_t = switch (builtin.os) { |
| 1126 | Os.macosx, Os.ios => i64, |
| 1127 | else => void, |
| 1124 | 1128 | }; |
| 1125 | 1129 | |
| 1126 | 1130 | pub const Entry = struct { |
| ... | ... | @@ -1135,15 +1139,26 @@ pub const Dir = struct { |
| 1135 | 1139 | SymLink, |
| 1136 | 1140 | File, |
| 1137 | 1141 | UnixDomainSocket, |
| 1142 | Whiteout, |
| 1138 | 1143 | Unknown, |
| 1139 | 1144 | }; |
| 1140 | 1145 | }; |
| 1141 | 1146 | |
| 1142 | 1147 | pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir { |
| 1143 | | const fd = try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0); |
| 1148 | const fd = switch (builtin.os) { |
| 1149 | Os.windows => @compileError("TODO support Dir.open for windows"), |
| 1150 | Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), |
| 1151 | Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), |
| 1152 | else => @compileError("Dir.open is not supported for this platform"), |
| 1153 | }; |
| 1154 | const darwin_seek_init = switch (builtin.os) { |
| 1155 | Os.macosx, Os.ios => 0, |
| 1156 | else => {}, |
| 1157 | }; |
| 1144 | 1158 | return Dir { |
| 1145 | 1159 | .allocator = allocator, |
| 1146 | 1160 | .fd = fd, |
| 1161 | .darwin_seek = darwin_seek_init, |
| 1147 | 1162 | .index = 0, |
| 1148 | 1163 | .end_index = 0, |
| 1149 | 1164 | .buf = []u8{}, |
| ... | ... | @@ -1158,6 +1173,76 @@ pub const Dir = struct { |
| 1158 | 1173 | /// Memory such as file names referenced in this returned entry becomes invalid |
| 1159 | 1174 | /// with subsequent calls to next, as well as when this ::Dir is deinitialized. |
| 1160 | 1175 | pub fn next(self: &Dir) !?Entry { |
| 1176 | switch (builtin.os) { |
| 1177 | Os.linux => return self.nextLinux(), |
| 1178 | Os.macosx, Os.ios => return self.nextDarwin(), |
| 1179 | Os.windows => return self.nextWindows(), |
| 1180 | else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)), |
| 1181 | } |
| 1182 | } |
| 1183 | |
| 1184 | fn nextDarwin(self: &Dir) !?Entry { |
| 1185 | start_over: while (true) { |
| 1186 | if (self.index >= self.end_index) { |
| 1187 | if (self.buf.len == 0) { |
| 1188 | self.buf = try self.allocator.alloc(u8, page_size); |
| 1189 | } |
| 1190 | |
| 1191 | while (true) { |
| 1192 | const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, |
| 1193 | &self.darwin_seek); |
| 1194 | const err = posix.getErrno(result); |
| 1195 | if (err > 0) { |
| 1196 | switch (err) { |
| 1197 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, |
| 1198 | posix.EINVAL => { |
| 1199 | self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); |
| 1200 | continue; |
| 1201 | }, |
| 1202 | else => return unexpectedErrorPosix(err), |
| 1203 | } |
| 1204 | } |
| 1205 | if (result == 0) |
| 1206 | return null; |
| 1207 | self.index = 0; |
| 1208 | self.end_index = result; |
| 1209 | break; |
| 1210 | } |
| 1211 | } |
| 1212 | const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); |
| 1213 | const next_index = self.index + darwin_entry.d_reclen; |
| 1214 | self.index = next_index; |
| 1215 | |
| 1216 | const name = (&darwin_entry.d_name)[0..darwin_entry.d_namlen]; |
| 1217 | |
| 1218 | // skip . and .. entries |
| 1219 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { |
| 1220 | continue :start_over; |
| 1221 | } |
| 1222 | |
| 1223 | const entry_kind = switch (darwin_entry.d_type) { |
| 1224 | posix.DT_BLK => Entry.Kind.BlockDevice, |
| 1225 | posix.DT_CHR => Entry.Kind.CharacterDevice, |
| 1226 | posix.DT_DIR => Entry.Kind.Directory, |
| 1227 | posix.DT_FIFO => Entry.Kind.NamedPipe, |
| 1228 | posix.DT_LNK => Entry.Kind.SymLink, |
| 1229 | posix.DT_REG => Entry.Kind.File, |
| 1230 | posix.DT_SOCK => Entry.Kind.UnixDomainSocket, |
| 1231 | posix.DT_WHT => Entry.Kind.Whiteout, |
| 1232 | else => Entry.Kind.Unknown, |
| 1233 | }; |
| 1234 | return Entry { |
| 1235 | .name = name, |
| 1236 | .kind = entry_kind, |
| 1237 | }; |
| 1238 | } |
| 1239 | } |
| 1240 | |
| 1241 | fn nextWindows(self: &Dir) !?Entry { |
| 1242 | @compileError("TODO support Dir.next for windows"); |
| 1243 | } |
| 1244 | |
| 1245 | fn nextLinux(self: &Dir) !?Entry { |
| 1161 | 1246 | start_over: while (true) { |
| 1162 | 1247 | if (self.index >= self.end_index) { |
| 1163 | 1248 | if (self.buf.len == 0) { |
| ... | ... | @@ -1166,7 +1251,7 @@ pub const Dir = struct { |
| 1166 | 1251 | |
| 1167 | 1252 | while (true) { |
| 1168 | 1253 | const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len); |
| 1169 | | const err = linux.getErrno(result); |
| 1254 | const err = posix.getErrno(result); |
| 1170 | 1255 | if (err > 0) { |
| 1171 | 1256 | switch (err) { |
| 1172 | 1257 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, |
| ... | ... | @@ -1184,7 +1269,7 @@ pub const Dir = struct { |
| 1184 | 1269 | break; |
| 1185 | 1270 | } |
| 1186 | 1271 | } |
| 1187 | | const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]); |
| 1272 | const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); |
| 1188 | 1273 | const next_index = self.index + linux_entry.d_reclen; |
| 1189 | 1274 | self.index = next_index; |
| 1190 | 1275 | |
| ... | ... | @@ -1679,6 +1764,7 @@ test "std.os" { |
| 1679 | 1764 | _ = @import("linux/index.zig"); |
| 1680 | 1765 | _ = @import("path.zig"); |
| 1681 | 1766 | _ = @import("windows/index.zig"); |
| 1767 | _ = @import("test.zig"); |
| 1682 | 1768 | } |
| 1683 | 1769 | |
| 1684 | 1770 | |