| ... | ... | @@ -461,13 +461,16 @@ fn runResource( |
| 461 | 461 | }; |
| 462 | 462 | defer tmp_directory.handle.close(); |
| 463 | 463 | |
| 464 | | // Unpack resource into tmp_directory. A non-null return value means |
| 465 | | // that the package contents are inside a `pkg_dir` sub-directory. |
| 466 | | const pkg_dir = try unpackResource(f, resource, uri_path, tmp_directory); |
| 464 | var unpack_result = try unpackResource(f, resource, uri_path, tmp_directory); |
| 465 | defer unpack_result.deinit(); |
| 466 | if (unpack_result.hasErrors()) { |
| 467 | try unpack_result.bundleErrors(eb, try f.srcLoc(f.location_tok)); |
| 468 | return error.FetchFailed; |
| 469 | } |
| 467 | 470 | |
| 468 | 471 | var pkg_path: Cache.Path = .{ |
| 469 | 472 | .root_dir = tmp_directory, |
| 470 | | .sub_path = if (pkg_dir) |pkg_dir_name| pkg_dir_name else "", |
| 473 | .sub_path = if (unpack_result.root_dir) |root_dir| root_dir else "", |
| 471 | 474 | }; |
| 472 | 475 | |
| 473 | 476 | // Apply btrfs workaround if needed. Reopen tmp_directory. |
| ... | ... | @@ -500,8 +503,8 @@ fn runResource( |
| 500 | 503 | // directory. |
| 501 | 504 | f.actual_hash = try computeHash(f, pkg_path, filter); |
| 502 | 505 | |
| 503 | | break :blk if (pkg_dir) |pkg_dir_name| |
| 504 | | try fs.path.join(arena, &.{ tmp_dir_sub_path, pkg_dir_name }) |
| 506 | break :blk if (unpack_result.root_dir) |root_dir| |
| 507 | try fs.path.join(arena, &.{ tmp_dir_sub_path, root_dir }) |
| 505 | 508 | else |
| 506 | 509 | tmp_dir_sub_path; |
| 507 | 510 | }; |
| ... | ... | @@ -1053,7 +1056,7 @@ fn unpackResource( |
| 1053 | 1056 | resource: *Resource, |
| 1054 | 1057 | uri_path: []const u8, |
| 1055 | 1058 | tmp_directory: Cache.Directory, |
| 1056 | | ) RunError!?[]const u8 { |
| 1059 | ) RunError!UnpackResult { |
| 1057 | 1060 | const eb = &f.error_bundle; |
| 1058 | 1061 | const file_type = switch (resource.*) { |
| 1059 | 1062 | .file => FileType.fromPath(uri_path) orelse |
| ... | ... | @@ -1121,7 +1124,8 @@ fn unpackResource( |
| 1121 | 1124 | .{ uri_path, @errorName(err) }, |
| 1122 | 1125 | )); |
| 1123 | 1126 | }; |
| 1124 | | return null; |
| 1127 | const gpa = f.arena.child_allocator; |
| 1128 | return UnpackResult.init(gpa); |
| 1125 | 1129 | }, |
| 1126 | 1130 | }; |
| 1127 | 1131 | |
| ... | ... | @@ -1156,23 +1160,19 @@ fn unpackResource( |
| 1156 | 1160 | }); |
| 1157 | 1161 | return try unpackTarball(f, tmp_directory.handle, dcp.reader()); |
| 1158 | 1162 | }, |
| 1159 | | .git_pack => { |
| 1160 | | unpackGitPack(f, tmp_directory.handle, resource) catch |err| switch (err) { |
| 1161 | | error.FetchFailed => return error.FetchFailed, |
| 1162 | | error.OutOfMemory => return error.OutOfMemory, |
| 1163 | | else => |e| return f.fail(f.location_tok, try eb.printString( |
| 1164 | | "unable to unpack git files: {s}", |
| 1165 | | .{@errorName(e)}, |
| 1166 | | )), |
| 1167 | | }; |
| 1168 | | return null; |
| 1163 | .git_pack => return unpackGitPack(f, tmp_directory.handle, resource) catch |err| switch (err) { |
| 1164 | error.FetchFailed => return error.FetchFailed, |
| 1165 | error.OutOfMemory => return error.OutOfMemory, |
| 1166 | else => |e| return f.fail(f.location_tok, try eb.printString( |
| 1167 | "unable to unpack git files: {s}", |
| 1168 | .{@errorName(e)}, |
| 1169 | )), |
| 1169 | 1170 | }, |
| 1170 | 1171 | } |
| 1171 | 1172 | } |
| 1172 | 1173 | |
| 1173 | | fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!?[]const u8 { |
| 1174 | fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult { |
| 1174 | 1175 | const eb = &f.error_bundle; |
| 1175 | | const arena = f.arena.allocator(); |
| 1176 | 1176 | const gpa = f.arena.child_allocator; |
| 1177 | 1177 | |
| 1178 | 1178 | var diagnostics: std.tar.Diagnostics = .{ .allocator = gpa }; |
| ... | ... | @@ -1188,10 +1188,11 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!?[]const |
| 1188 | 1188 | .{@errorName(err)}, |
| 1189 | 1189 | )); |
| 1190 | 1190 | |
| 1191 | var res = UnpackResult.init(gpa); |
| 1192 | if (diagnostics.root_dir) |root_dir| { |
| 1193 | res.root_dir = try gpa.dupe(u8, root_dir); |
| 1194 | } |
| 1191 | 1195 | if (diagnostics.errors.items.len > 0) { |
| 1192 | | var res = UnpackResult.init(gpa); |
| 1193 | | defer res.deinit(); |
| 1194 | | |
| 1195 | 1196 | for (diagnostics.errors.items) |item| { |
| 1196 | 1197 | switch (item) { |
| 1197 | 1198 | .unable_to_create_file => |i| try res.createFile(i.file_name, i.code), |
| ... | ... | @@ -1199,21 +1200,17 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!?[]const |
| 1199 | 1200 | .unsupported_file_type => |i| try res.unsupportedFileType(i.file_name, @intFromEnum(i.file_type)), |
| 1200 | 1201 | } |
| 1201 | 1202 | } |
| 1202 | | try res.bundleErrors(eb, "unable to unpack tarball", try f.srcLoc(f.location_tok)); |
| 1203 | | return error.FetchFailed; |
| 1203 | try res.rootErrorMessage("unable to unpack tarball"); |
| 1204 | 1204 | } |
| 1205 | | |
| 1206 | | return if (diagnostics.root_dir) |root_dir| |
| 1207 | | return try arena.dupe(u8, root_dir) |
| 1208 | | else |
| 1209 | | null; |
| 1205 | return res; |
| 1210 | 1206 | } |
| 1211 | 1207 | |
| 1212 | | fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!void { |
| 1213 | | const eb = &f.error_bundle; |
| 1208 | fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!UnpackResult { |
| 1214 | 1209 | const gpa = f.arena.child_allocator; |
| 1215 | 1210 | const want_oid = resource.git.want_oid; |
| 1216 | 1211 | const reader = resource.git.fetch_stream.reader(); |
| 1212 | |
| 1213 | var res = UnpackResult.init(gpa); |
| 1217 | 1214 | // The .git directory is used to store the packfile and associated index, but |
| 1218 | 1215 | // we do not attempt to replicate the exact structure of a real .git |
| 1219 | 1216 | // directory, since that isn't relevant for fetching a package. |
| ... | ... | @@ -1249,7 +1246,6 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!void |
| 1249 | 1246 | try repository.checkout(out_dir, want_oid, &diagnostics); |
| 1250 | 1247 | |
| 1251 | 1248 | if (diagnostics.errors.items.len > 0) { |
| 1252 | | var res = UnpackResult.init(gpa); |
| 1253 | 1249 | defer res.deinit(); |
| 1254 | 1250 | |
| 1255 | 1251 | for (diagnostics.errors.items) |item| { |
| ... | ... | @@ -1257,14 +1253,13 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!void |
| 1257 | 1253 | .unable_to_create_sym_link => |i| try res.symLink(i.file_name, i.link_name, i.code), |
| 1258 | 1254 | } |
| 1259 | 1255 | } |
| 1260 | | try res.bundleErrors(eb, "unable to unpack packfile", try f.srcLoc(f.location_tok)); |
| 1261 | | |
| 1262 | | return error.InvalidGitPack; |
| 1256 | try res.rootErrorMessage("unable to unpack packfile"); |
| 1263 | 1257 | } |
| 1264 | 1258 | } |
| 1265 | 1259 | } |
| 1266 | 1260 | |
| 1267 | 1261 | try out_dir.deleteTree(".git"); |
| 1262 | return res; |
| 1268 | 1263 | } |
| 1269 | 1264 | |
| 1270 | 1265 | fn recursiveDirectoryCopy(f: *Fetch, dir: fs.Dir, tmp_dir: fs.Dir) anyerror!void { |
| ... | ... | @@ -1753,6 +1748,8 @@ test FileHeader { |
| 1753 | 1748 | const UnpackResult = struct { |
| 1754 | 1749 | allocator: std.mem.Allocator, |
| 1755 | 1750 | errors: std.ArrayListUnmanaged(Error) = .{}, |
| 1751 | root_error_message: []const u8 = "", |
| 1752 | root_dir: ?[]const u8 = null, |
| 1756 | 1753 | |
| 1757 | 1754 | const Error = union(enum) { |
| 1758 | 1755 | unable_to_create_sym_link: struct { |
| ... | ... | @@ -1802,6 +1799,10 @@ const UnpackResult = struct { |
| 1802 | 1799 | item.free(self.allocator); |
| 1803 | 1800 | } |
| 1804 | 1801 | self.errors.deinit(self.allocator); |
| 1802 | self.allocator.free(self.root_error_message); |
| 1803 | if (self.root_dir) |root_dir| { |
| 1804 | self.allocator.free(root_dir); |
| 1805 | } |
| 1805 | 1806 | self.* = undefined; |
| 1806 | 1807 | } |
| 1807 | 1808 | |
| ... | ... | @@ -1843,15 +1844,18 @@ const UnpackResult = struct { |
| 1843 | 1844 | } |
| 1844 | 1845 | } |
| 1845 | 1846 | |
| 1847 | fn rootErrorMessage(self: *UnpackResult, msg: []const u8) !void { |
| 1848 | self.root_error_message = try self.allocator.dupe(u8, msg); |
| 1849 | } |
| 1850 | |
| 1846 | 1851 | fn bundleErrors( |
| 1847 | 1852 | self: *UnpackResult, |
| 1848 | 1853 | eb: *ErrorBundle.Wip, |
| 1849 | | msg: []const u8, |
| 1850 | 1854 | src_loc: ErrorBundle.SourceLocationIndex, |
| 1851 | 1855 | ) !void { |
| 1852 | 1856 | const notes_len: u32 = @intCast(self.errors.items.len); |
| 1853 | 1857 | try eb.addRootErrorMessage(.{ |
| 1854 | | .msg = try eb.addString(msg), |
| 1858 | .msg = try eb.addString(self.root_error_message), |
| 1855 | 1859 | .src_loc = src_loc, |
| 1856 | 1860 | .notes_len = notes_len, |
| 1857 | 1861 | }); |