authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2024-02-27 19:43:30+01:00
committergravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2024-04-03 17:06:20+02:00
loga5a928b9668f244a7c5d53f5c75de0152c79f5f6
tree8be198c4bf2f360366af055d3c8ff85cb3965493
parentbc5076715be066540b0b61a5599404f45bfa6d5f

package manager: don't strip components in tar

Unpack tar without removing leading root folder. Then find package root in unpacked tmp folder.

1 files changed, 86 insertions(+), 11 deletions(-)

src/Package/Fetch.zig+86-11
...@@ -439,7 +439,8 @@ fn runResource(...@@ -439,7 +439,8 @@ fn runResource(
439 const s = fs.path.sep_str;439 const s = fs.path.sep_str;
440 const cache_root = f.job_queue.global_cache;440 const cache_root = f.job_queue.global_cache;
441 const rand_int = std.crypto.random.int(u64);441 const rand_int = std.crypto.random.int(u64);
442 const tmp_dir_sub_path = "tmp" ++ s ++ Manifest.hex64(rand_int);442 const tmp_dir_sub_path = "tmp" ++ s ++ Manifest.hex64(rand_int); // root of the temporary directory
443 var tmp_package_root_sub_path: ?[]const u8 = null; // package root inside temporary directory
443444
444 {445 {
445 const tmp_directory_path = try cache_root.join(arena, &.{tmp_dir_sub_path});446 const tmp_directory_path = try cache_root.join(arena, &.{tmp_dir_sub_path});
...@@ -463,6 +464,34 @@ fn runResource(...@@ -463,6 +464,34 @@ fn runResource(
463464
464 try unpackResource(f, resource, uri_path, tmp_directory);465 try unpackResource(f, resource, uri_path, tmp_directory);
465466
467 // Strip leading root directory if needed.
468 if (findPackageRootSubPath(arena, tmp_directory) catch null) |sub_path| {
469 // Position tmp_directory to sub_path.
470 const handle = tmp_directory.handle.openDir(sub_path, .{ .iterate = true }) catch |err| {
471 try eb.addRootErrorMessage(.{
472 .msg = try eb.printString("fail to open temporary directory '{s}' sub path '{s}': {s}", .{
473 tmp_directory_path, sub_path, @errorName(err),
474 }),
475 });
476 return error.FetchFailed;
477 };
478 tmp_package_root_sub_path = try fs.path.join(arena, &[_][]const u8{ tmp_dir_sub_path, sub_path });
479 tmp_directory.handle.close();
480 tmp_directory = .{
481 .path = try cache_root.join(arena, &.{tmp_package_root_sub_path.?}),
482 .handle = handle,
483 };
484 } else {
485 // btrfs workaround; reopen tmp_directory
486 if (native_os == .linux and f.job_queue.work_around_btrfs_bug) {
487 // https://github.com/ziglang/zig/issues/17095
488 tmp_directory.handle.close();
489 tmp_directory.handle = cache_root.handle.makeOpenPath(tmp_dir_sub_path, .{
490 .iterate = true,
491 }) catch @panic("btrfs workaround failed");
492 }
493 }
494
466 // Load, parse, and validate the unpacked build.zig.zon file. It is allowed495 // Load, parse, and validate the unpacked build.zig.zon file. It is allowed
467 // for the file to be missing, in which case this fetched package is496 // for the file to be missing, in which case this fetched package is
468 // considered to be a "naked" package.497 // considered to be a "naked" package.
...@@ -481,15 +510,6 @@ fn runResource(...@@ -481,15 +510,6 @@ fn runResource(
481510
482 // Compute the package hash based on the remaining files in the temporary511 // Compute the package hash based on the remaining files in the temporary
483 // directory.512 // directory.
484
485 if (native_os == .linux and f.job_queue.work_around_btrfs_bug) {
486 // https://github.com/ziglang/zig/issues/17095
487 tmp_directory.handle.close();
488 tmp_directory.handle = cache_root.handle.makeOpenPath(tmp_dir_sub_path, .{
489 .iterate = true,
490 }) catch @panic("btrfs workaround failed");
491 }
492
493 f.actual_hash = try computeHash(f, tmp_directory, filter);513 f.actual_hash = try computeHash(f, tmp_directory, filter);
494 }514 }
495515
...@@ -503,7 +523,11 @@ fn runResource(...@@ -503,7 +523,11 @@ fn runResource(
503 .root_dir = cache_root,523 .root_dir = cache_root,
504 .sub_path = try arena.dupe(u8, "p" ++ s ++ Manifest.hexDigest(f.actual_hash)),524 .sub_path = try arena.dupe(u8, "p" ++ s ++ Manifest.hexDigest(f.actual_hash)),
505 };525 };
506 renameTmpIntoCache(cache_root.handle, tmp_dir_sub_path, f.package_root.sub_path) catch |err| {526 renameTmpIntoCache(
527 cache_root.handle,
528 if (tmp_package_root_sub_path) |p| p else tmp_dir_sub_path,
529 f.package_root.sub_path,
530 ) catch |err| {
507 const src = try cache_root.join(arena, &.{tmp_dir_sub_path});531 const src = try cache_root.join(arena, &.{tmp_dir_sub_path});
508 const dest = try cache_root.join(arena, &.{f.package_root.sub_path});532 const dest = try cache_root.join(arena, &.{f.package_root.sub_path});
509 try eb.addRootErrorMessage(.{ .msg = try eb.printString(533 try eb.addRootErrorMessage(.{ .msg = try eb.printString(
...@@ -512,6 +536,10 @@ fn runResource(...@@ -512,6 +536,10 @@ fn runResource(
512 ) });536 ) });
513 return error.FetchFailed;537 return error.FetchFailed;
514 };538 };
539 // Remove temporary directory root if that not already done in rename.
540 if (tmp_package_root_sub_path) |_| {
541 cache_root.handle.deleteTree(tmp_dir_sub_path) catch {};
542 }
515543
516 // Validate the computed hash against the expected hash. If invalid, this544 // Validate the computed hash against the expected hash. If invalid, this
517 // job is done.545 // job is done.
...@@ -608,6 +636,19 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {...@@ -608,6 +636,19 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {
608 }636 }
609}637}
610638
639// Finds package root subpath.
640// Skips single root directory, returns null in all other cases.
641fn findPackageRootSubPath(allocator: Allocator, parent: Cache.Directory) !?[]const u8 {
642 var iter = parent.handle.iterate();
643 if (try iter.next()) |entry| {
644 if (try iter.next() != null) return null;
645 if (entry.kind == .directory) {
646 return try allocator.dupe(u8, entry.name);
647 }
648 }
649 return null;
650}
651
611fn queueJobsForDeps(f: *Fetch) RunError!void {652fn queueJobsForDeps(f: *Fetch) RunError!void {
612 assert(f.job_queue.recursive);653 assert(f.job_queue.recursive);
613654
...@@ -1700,3 +1741,37 @@ test {...@@ -1700,3 +1741,37 @@ test {
1700 _ = Filter;1741 _ = Filter;
1701 _ = FileType;1742 _ = FileType;
1702}1743}
1744
1745test "findPackageRootSubPath" {
1746 const testing = std.testing;
1747
1748 var root = std.testing.tmpDir(.{ .iterate = true });
1749 defer root.cleanup();
1750
1751 // folder1
1752 // ├── folder2
1753 // ├── file1
1754 //
1755 try root.dir.makePath("folder1/folder2");
1756 (try root.dir.createFile("folder1/file1", .{})).close();
1757
1758 // start at root returns folder1 as package root
1759 const sub_path = (try findPackageRootSubPath(
1760 testing.allocator,
1761 Cache.Directory{ .path = null, .handle = root.dir },
1762 )).?;
1763 try testing.expectEqualStrings("folder1", sub_path);
1764 testing.allocator.free(sub_path);
1765
1766 // start at folder1 returns null
1767 try testing.expect(null == (try findPackageRootSubPath(
1768 testing.allocator,
1769 Cache.Directory{ .path = null, .handle = try root.dir.openDir("folder1", .{ .iterate = true }) },
1770 )));
1771
1772 // start at folder1/folder2 returns null
1773 try testing.expect(null == (try findPackageRootSubPath(
1774 testing.allocator,
1775 Cache.Directory{ .path = null, .handle = try root.dir.openDir("folder1/folder2", .{ .iterate = true }) },
1776 )));
1777}