| ... | @@ -463,7 +463,6 @@ fn runResource( | ... | @@ -463,7 +463,6 @@ fn runResource( |
| 463 | | 463 | |
| 464 | // Fetch and unpack a resource into a temporary directory. | 464 | // Fetch and unpack a resource into a temporary directory. |
| 465 | var unpack_result = try unpackResource(f, resource, uri_path, tmp_directory); | 465 | var unpack_result = try unpackResource(f, resource, uri_path, tmp_directory); |
| 466 | defer unpack_result.deinit(); | | |
| 467 | | 466 | |
| 468 | var pkg_path: Cache.Path = .{ .root_dir = tmp_directory, .sub_path = unpack_result.root_dir }; | 467 | var pkg_path: Cache.Path = .{ .root_dir = tmp_directory, .sub_path = unpack_result.root_dir }; |
| 469 | | 468 | |
| ... | @@ -487,11 +486,7 @@ fn runResource( | ... | @@ -487,11 +486,7 @@ fn runResource( |
| 487 | | 486 | |
| 488 | // Ignore errors that were excluded by manifest, such as failure to | 487 | // Ignore errors that were excluded by manifest, such as failure to |
| 489 | // create symlinks that weren't supposed to be included anyway. | 488 | // create symlinks that weren't supposed to be included anyway. |
| 490 | try unpack_result.filterErrors(filter); | 489 | try unpack_result.validate(f, filter); |
| 491 | if (unpack_result.hasErrors()) { | | |
| 492 | try unpack_result.bundleErrors(eb, try f.srcLoc(f.location_tok)); | | |
| 493 | return error.FetchFailed; | | |
| 494 | } | | |
| 495 | | 490 | |
| 496 | // Apply the manifest's inclusion rules to the temporary directory by | 491 | // Apply the manifest's inclusion rules to the temporary directory by |
| 497 | // deleting excluded files. | 492 | // deleting excluded files. |
| ... | @@ -1117,8 +1112,7 @@ fn unpackResource( | ... | @@ -1117,8 +1112,7 @@ fn unpackResource( |
| 1117 | .{ uri_path, @errorName(err) }, | 1112 | .{ uri_path, @errorName(err) }, |
| 1118 | )); | 1113 | )); |
| 1119 | }; | 1114 | }; |
| 1120 | const gpa = f.arena.child_allocator; | 1115 | return .{}; |
| 1121 | return UnpackResult.init(gpa); | | |
| 1122 | }, | 1116 | }, |
| 1123 | }; | 1117 | }; |
| 1124 | | 1118 | |
| ... | @@ -1166,10 +1160,9 @@ fn unpackResource( | ... | @@ -1166,10 +1160,9 @@ fn unpackResource( |
| 1166 | | 1160 | |
| 1167 | fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult { | 1161 | fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult { |
| 1168 | const eb = &f.error_bundle; | 1162 | const eb = &f.error_bundle; |
| 1169 | const gpa = f.arena.child_allocator; | 1163 | const arena = f.arena.allocator(); |
| 1170 | | 1164 | |
| 1171 | var diagnostics: std.tar.Diagnostics = .{ .allocator = gpa }; | 1165 | var diagnostics: std.tar.Diagnostics = .{ .allocator = arena }; |
| 1172 | defer diagnostics.deinit(); | | |
| 1173 | | 1166 | |
| 1174 | std.tar.pipeToFileSystem(out_dir, reader, .{ | 1167 | std.tar.pipeToFileSystem(out_dir, reader, .{ |
| 1175 | .diagnostics = &diagnostics, | 1168 | .diagnostics = &diagnostics, |
| ... | @@ -1181,17 +1174,14 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackRes | ... | @@ -1181,17 +1174,14 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackRes |
| 1181 | .{@errorName(err)}, | 1174 | .{@errorName(err)}, |
| 1182 | )); | 1175 | )); |
| 1183 | | 1176 | |
| 1184 | var res = UnpackResult.init(gpa); | 1177 | var res: UnpackResult = .{ .root_dir = diagnostics.root_dir }; |
| 1185 | if (diagnostics.root_dir.len > 0) { | | |
| 1186 | res.root_dir = try gpa.dupe(u8, diagnostics.root_dir); | | |
| 1187 | } | | |
| 1188 | if (diagnostics.errors.items.len > 0) { | 1178 | if (diagnostics.errors.items.len > 0) { |
| 1189 | try res.rootErrorMessage("unable to unpack tarball"); | 1179 | try res.allocErrors(arena, diagnostics.errors.items.len, "unable to unpack tarball"); |
| 1190 | for (diagnostics.errors.items) |item| { | 1180 | for (diagnostics.errors.items) |item| { |
| 1191 | switch (item) { | 1181 | switch (item) { |
| 1192 | .unable_to_create_file => |i| try res.unableToCreateFile(stripRoot(i.file_name, res.root_dir), i.code), | 1182 | .unable_to_create_file => |i| res.unableToCreateFile(stripRoot(i.file_name, res.root_dir), i.code), |
| 1193 | .unable_to_create_sym_link => |i| try res.unableToCreateSymLink(stripRoot(i.file_name, res.root_dir), i.link_name, i.code), | 1183 | .unable_to_create_sym_link => |i| res.unableToCreateSymLink(stripRoot(i.file_name, res.root_dir), i.link_name, i.code), |
| 1194 | .unsupported_file_type => |i| try res.unsupportedFileType(stripRoot(i.file_name, res.root_dir), @intFromEnum(i.file_type)), | 1184 | .unsupported_file_type => |i| res.unsupportedFileType(stripRoot(i.file_name, res.root_dir), @intFromEnum(i.file_type)), |
| 1195 | } | 1185 | } |
| 1196 | } | 1186 | } |
| 1197 | } | 1187 | } |
| ... | @@ -1199,11 +1189,12 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackRes | ... | @@ -1199,11 +1189,12 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackRes |
| 1199 | } | 1189 | } |
| 1200 | | 1190 | |
| 1201 | fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!UnpackResult { | 1191 | fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!UnpackResult { |
| | 1192 | const arena = f.arena.allocator(); |
| 1202 | const gpa = f.arena.child_allocator; | 1193 | const gpa = f.arena.child_allocator; |
| 1203 | const want_oid = resource.git.want_oid; | 1194 | const want_oid = resource.git.want_oid; |
| 1204 | const reader = resource.git.fetch_stream.reader(); | 1195 | const reader = resource.git.fetch_stream.reader(); |
| 1205 | | 1196 | |
| 1206 | var res = UnpackResult.init(gpa); | 1197 | var res: UnpackResult = .{}; |
| 1207 | // The .git directory is used to store the packfile and associated index, but | 1198 | // The .git directory is used to store the packfile and associated index, but |
| 1208 | // we do not attempt to replicate the exact structure of a real .git | 1199 | // we do not attempt to replicate the exact structure of a real .git |
| 1209 | // directory, since that isn't relevant for fetching a package. | 1200 | // directory, since that isn't relevant for fetching a package. |
| ... | @@ -1234,16 +1225,15 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!Unpac | ... | @@ -1234,16 +1225,15 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!Unpac |
| 1234 | checkout_prog_node.activate(); | 1225 | checkout_prog_node.activate(); |
| 1235 | var repository = try git.Repository.init(gpa, pack_file, index_file); | 1226 | var repository = try git.Repository.init(gpa, pack_file, index_file); |
| 1236 | defer repository.deinit(); | 1227 | defer repository.deinit(); |
| 1237 | var diagnostics: git.Diagnostics = .{ .allocator = gpa }; | 1228 | var diagnostics: git.Diagnostics = .{ .allocator = arena }; |
| 1238 | defer diagnostics.deinit(); | | |
| 1239 | try repository.checkout(out_dir, want_oid, &diagnostics); | 1229 | try repository.checkout(out_dir, want_oid, &diagnostics); |
| 1240 | | 1230 | |
| 1241 | if (diagnostics.errors.items.len > 0) { | 1231 | if (diagnostics.errors.items.len > 0) { |
| 1242 | try res.rootErrorMessage("unable to unpack packfile"); | 1232 | try res.allocErrors(arena, diagnostics.errors.items.len, "unable to unpack packfile"); |
| 1243 | for (diagnostics.errors.items) |item| { | 1233 | for (diagnostics.errors.items) |item| { |
| 1244 | switch (item) { | 1234 | switch (item) { |
| 1245 | .unable_to_create_file => |i| try res.unableToCreateFile(i.file_name, i.code), | 1235 | .unable_to_create_file => |i| res.unableToCreateFile(i.file_name, i.code), |
| 1246 | .unable_to_create_sym_link => |i| try res.unableToCreateSymLink(i.file_name, i.link_name, i.code), | 1236 | .unable_to_create_sym_link => |i| res.unableToCreateSymLink(i.file_name, i.link_name, i.code), |
| 1247 | } | 1237 | } |
| 1248 | } | 1238 | } |
| 1249 | } | 1239 | } |
| ... | @@ -1701,6 +1691,7 @@ const native_os = builtin.os.tag; | ... | @@ -1701,6 +1691,7 @@ const native_os = builtin.os.tag; |
| 1701 | test { | 1691 | test { |
| 1702 | _ = Filter; | 1692 | _ = Filter; |
| 1703 | _ = FileType; | 1693 | _ = FileType; |
| | 1694 | _ = UnpackResult; |
| 1704 | } | 1695 | } |
| 1705 | | 1696 | |
| 1706 | // Detects executable header: ELF magic header or shebang line. | 1697 | // Detects executable header: ELF magic header or shebang line. |
| ... | @@ -1741,8 +1732,8 @@ test FileHeader { | ... | @@ -1741,8 +1732,8 @@ test FileHeader { |
| 1741 | // tar/git diagnostic, filtering that errors by manifest inclusion rules and | 1732 | // tar/git diagnostic, filtering that errors by manifest inclusion rules and |
| 1742 | // emitting remaining errors to an `ErrorBundle`. | 1733 | // emitting remaining errors to an `ErrorBundle`. |
| 1743 | const UnpackResult = struct { | 1734 | const UnpackResult = struct { |
| 1744 | allocator: std.mem.Allocator, | 1735 | errors: []Error = undefined, |
| 1745 | errors: std.ArrayListUnmanaged(Error) = .{}, | 1736 | errors_count: usize = 0, |
| 1746 | root_error_message: []const u8 = "", | 1737 | root_error_message: []const u8 = "", |
| 1747 | | 1738 | |
| 1748 | // A non empty value means that the package contents are inside a | 1739 | // A non empty value means that the package contents are inside a |
| ... | @@ -1772,97 +1763,82 @@ const UnpackResult = struct { | ... | @@ -1772,97 +1763,82 @@ const UnpackResult = struct { |
| 1772 | }; | 1763 | }; |
| 1773 | return !filter.includePath(file_name); | 1764 | return !filter.includePath(file_name); |
| 1774 | } | 1765 | } |
| 1775 | | | |
| 1776 | fn free(self: Error, allocator: std.mem.Allocator) void { | | |
| 1777 | switch (self) { | | |
| 1778 | .unable_to_create_sym_link => |info| { | | |
| 1779 | allocator.free(info.file_name); | | |
| 1780 | allocator.free(info.link_name); | | |
| 1781 | }, | | |
| 1782 | .unable_to_create_file => |info| { | | |
| 1783 | allocator.free(info.file_name); | | |
| 1784 | }, | | |
| 1785 | .unsupported_file_type => |info| { | | |
| 1786 | allocator.free(info.file_name); | | |
| 1787 | }, | | |
| 1788 | } | | |
| 1789 | } | | |
| 1790 | }; | 1766 | }; |
| 1791 | | 1767 | |
| 1792 | fn init(allocator: std.mem.Allocator) UnpackResult { | 1768 | fn allocErrors(self: *UnpackResult, arena: std.mem.Allocator, n: usize, root_error_message: []const u8) !void { |
| 1793 | return .{ .allocator = allocator }; | 1769 | self.root_error_message = try arena.dupe(u8, root_error_message); |
| 1794 | } | 1770 | self.errors = try arena.alloc(UnpackResult.Error, n); |
| 1795 | | | |
| 1796 | fn deinit(self: *UnpackResult) void { | | |
| 1797 | for (self.errors.items) |item| { | | |
| 1798 | item.free(self.allocator); | | |
| 1799 | } | | |
| 1800 | self.errors.deinit(self.allocator); | | |
| 1801 | self.allocator.free(self.root_error_message); | | |
| 1802 | self.allocator.free(self.root_dir); | | |
| 1803 | self.* = undefined; | | |
| 1804 | } | 1771 | } |
| 1805 | | 1772 | |
| 1806 | fn hasErrors(self: *UnpackResult) bool { | 1773 | fn hasErrors(self: *UnpackResult) bool { |
| 1807 | return self.errors.items.len > 0; | 1774 | return self.errors_count > 0; |
| 1808 | } | 1775 | } |
| 1809 | | 1776 | |
| 1810 | fn unableToCreateFile(self: *UnpackResult, file_name: []const u8, err: anyerror) !void { | 1777 | fn unableToCreateFile(self: *UnpackResult, file_name: []const u8, err: anyerror) void { |
| 1811 | try self.errors.append(self.allocator, .{ .unable_to_create_file = .{ | 1778 | self.errors[self.errors_count] = .{ .unable_to_create_file = .{ |
| 1812 | .code = err, | 1779 | .code = err, |
| 1813 | .file_name = try self.allocator.dupe(u8, file_name), | 1780 | .file_name = file_name, |
| 1814 | } }); | 1781 | } }; |
| | 1782 | self.errors_count += 1; |
| 1815 | } | 1783 | } |
| 1816 | | 1784 | |
| 1817 | fn unableToCreateSymLink(self: *UnpackResult, file_name: []const u8, link_name: []const u8, err: anyerror) !void { | 1785 | fn unableToCreateSymLink(self: *UnpackResult, file_name: []const u8, link_name: []const u8, err: anyerror) void { |
| 1818 | try self.errors.append(self.allocator, .{ .unable_to_create_sym_link = .{ | 1786 | self.errors[self.errors_count] = .{ .unable_to_create_sym_link = .{ |
| 1819 | .code = err, | 1787 | .code = err, |
| 1820 | .file_name = try self.allocator.dupe(u8, file_name), | 1788 | .file_name = file_name, |
| 1821 | .link_name = try self.allocator.dupe(u8, link_name), | 1789 | .link_name = link_name, |
| 1822 | } }); | 1790 | } }; |
| | 1791 | self.errors_count += 1; |
| 1823 | } | 1792 | } |
| 1824 | | 1793 | |
| 1825 | fn unsupportedFileType(self: *UnpackResult, file_name: []const u8, file_type: u8) !void { | 1794 | fn unsupportedFileType(self: *UnpackResult, file_name: []const u8, file_type: u8) void { |
| 1826 | try self.errors.append(self.allocator, .{ .unsupported_file_type = .{ | 1795 | self.errors[self.errors_count] = .{ .unsupported_file_type = .{ |
| 1827 | .file_name = try self.allocator.dupe(u8, file_name), | 1796 | .file_name = file_name, |
| 1828 | .file_type = file_type, | 1797 | .file_type = file_type, |
| 1829 | } }); | 1798 | } }; |
| | 1799 | self.errors_count += 1; |
| | 1800 | } |
| | 1801 | |
| | 1802 | fn validate(self: *UnpackResult, f: *Fetch, filter: Filter) !void { |
| | 1803 | self.filterErrors(filter); |
| | 1804 | if (self.hasErrors()) { |
| | 1805 | const eb = &f.error_bundle; |
| | 1806 | try self.bundleErrors(eb, try f.srcLoc(f.location_tok)); |
| | 1807 | return error.FetchFailed; |
| | 1808 | } |
| 1830 | } | 1809 | } |
| 1831 | | 1810 | |
| 1832 | // Filter errors by manifest inclusion rules. | 1811 | // Filter errors by manifest inclusion rules. |
| 1833 | fn filterErrors(self: *UnpackResult, filter: Filter) !void { | 1812 | fn filterErrors(self: *UnpackResult, filter: Filter) void { |
| 1834 | var i = self.errors.items.len; | 1813 | var i = self.errors_count; |
| 1835 | while (i > 0) { | 1814 | while (i > 0) { |
| 1836 | i -= 1; | 1815 | i -= 1; |
| 1837 | const item = self.errors.items[i]; | 1816 | if (self.errors[i].excluded(filter)) { |
| 1838 | if (item.excluded(filter)) { | 1817 | self.errors_count -= 1; |
| 1839 | _ = self.errors.swapRemove(i); | 1818 | const tmp = self.errors[i]; |
| 1840 | item.free(self.allocator); | 1819 | self.errors[i] = self.errors[self.errors_count]; |
| | 1820 | self.errors[self.errors_count] = tmp; |
| 1841 | } | 1821 | } |
| 1842 | } | 1822 | } |
| 1843 | } | 1823 | } |
| 1844 | | 1824 | |
| 1845 | fn rootErrorMessage(self: *UnpackResult, msg: []const u8) !void { | | |
| 1846 | self.root_error_message = try self.allocator.dupe(u8, msg); | | |
| 1847 | } | | |
| 1848 | | | |
| 1849 | // Emmit errors to an `ErrorBundle`. | 1825 | // Emmit errors to an `ErrorBundle`. |
| 1850 | fn bundleErrors( | 1826 | fn bundleErrors( |
| 1851 | self: *UnpackResult, | 1827 | self: *UnpackResult, |
| 1852 | eb: *ErrorBundle.Wip, | 1828 | eb: *ErrorBundle.Wip, |
| 1853 | src_loc: ErrorBundle.SourceLocationIndex, | 1829 | src_loc: ErrorBundle.SourceLocationIndex, |
| 1854 | ) !void { | 1830 | ) !void { |
| 1855 | if (self.errors.items.len == 0 and self.root_error_message.len == 0) | 1831 | if (self.errors_count == 0 and self.root_error_message.len == 0) |
| 1856 | return; | 1832 | return; |
| 1857 | | 1833 | |
| 1858 | const notes_len: u32 = @intCast(self.errors.items.len); | 1834 | const notes_len: u32 = @intCast(self.errors_count); |
| 1859 | try eb.addRootErrorMessage(.{ | 1835 | try eb.addRootErrorMessage(.{ |
| 1860 | .msg = try eb.addString(self.root_error_message), | 1836 | .msg = try eb.addString(self.root_error_message), |
| 1861 | .src_loc = src_loc, | 1837 | .src_loc = src_loc, |
| 1862 | .notes_len = notes_len, | 1838 | .notes_len = notes_len, |
| 1863 | }); | 1839 | }); |
| 1864 | const notes_start = try eb.reserveNotes(notes_len); | 1840 | const notes_start = try eb.reserveNotes(notes_len); |
| 1865 | for (self.errors.items, notes_start..) |item, note_i| { | 1841 | for (self.errors, notes_start..) |item, note_i| { |
| 1866 | switch (item) { | 1842 | switch (item) { |
| 1867 | .unable_to_create_sym_link => |info| { | 1843 | .unable_to_create_sym_link => |info| { |
| 1868 | eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ | 1844 | eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ |
| ... | @@ -1888,6 +1864,36 @@ const UnpackResult = struct { | ... | @@ -1888,6 +1864,36 @@ const UnpackResult = struct { |
| 1888 | } | 1864 | } |
| 1889 | } | 1865 | } |
| 1890 | } | 1866 | } |
| | 1867 | |
| | 1868 | test filterErrors { |
| | 1869 | var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator); |
| | 1870 | defer arena_instance.deinit(); |
| | 1871 | const arena = arena_instance.allocator(); |
| | 1872 | |
| | 1873 | // init |
| | 1874 | var res: UnpackResult = .{}; |
| | 1875 | try res.allocErrors(arena, 4, "error"); |
| | 1876 | try std.testing.expectEqual(0, res.errors_count); |
| | 1877 | |
| | 1878 | // create errors |
| | 1879 | res.unableToCreateFile("dir1/file1", error.File1); |
| | 1880 | res.unableToCreateSymLink("dir2/file2", "", error.File2); |
| | 1881 | res.unableToCreateFile("dir1/file3", error.File3); |
| | 1882 | res.unsupportedFileType("dir2/file4", 'x'); |
| | 1883 | try std.testing.expectEqual(4, res.errors_count); |
| | 1884 | |
| | 1885 | // filter errors |
| | 1886 | var filter: Filter = .{}; |
| | 1887 | try filter.include_paths.put(arena, "dir2", {}); |
| | 1888 | res.filterErrors(filter); |
| | 1889 | |
| | 1890 | try std.testing.expectEqual(2, res.errors_count); |
| | 1891 | try std.testing.expect(res.errors[0] == Error.unsupported_file_type); |
| | 1892 | try std.testing.expect(res.errors[1] == Error.unable_to_create_sym_link); |
| | 1893 | // filtered: moved to the list end |
| | 1894 | try std.testing.expect(res.errors[2] == Error.unable_to_create_file); |
| | 1895 | try std.testing.expect(res.errors[3] == Error.unable_to_create_file); |
| | 1896 | } |
| 1891 | }; | 1897 | }; |
| 1892 | | 1898 | |
| 1893 | test "tarball with duplicate paths" { | 1899 | test "tarball with duplicate paths" { |