diff --git a/src/Package.zig b/src/Package.zig index 0215db24a8cf7361c5e19f9a0f6085e86001dad7..0dca095833309542439b83270d219f6894e40502 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -6,10 +6,6 @@ pub const Fetch = @import("Package/Fetch.zig"); pub const build_zig_basename = "build.zig"; pub const Manifest = @import("Package/Manifest.zig"); -pub const multihash_len = 1 + 1 + Hash.Algo.digest_length; -pub const multihash_hex_digest_len = 2 * multihash_len; -pub const MultiHashHexDigest = [multihash_hex_digest_len]u8; - pub const Fingerprint = packed struct(u64) { id: u32, checksum: u32, @@ -77,20 +73,6 @@ pub const Hash = struct { return std.mem.eql(u8, &a.bytes, &b.bytes); } - /// Distinguishes whether the legacy multihash format is being stored here. - pub fn isOld(h: *const Hash) bool { - if (h.bytes.len < 2) return false; - const their_multihash_func = std.fmt.parseInt(u8, h.bytes[0..2], 16) catch return false; - if (@as(MultihashFunction, @enumFromInt(their_multihash_func)) != multihash_function) return false; - if (h.toSlice().len != multihash_hex_digest_len) return false; - return std.mem.indexOfScalar(u8, &h.bytes, '-') == null; - } - - test isOld { - const h: Hash = .fromSlice("1220138f4aba0c01e66b68ed9e1e1e74614c06e4743d88bc58af4f1c3dd0aae5fea7"); - try std.testing.expect(h.isOld()); - } - /// Produces "$name-$semver-$hashplus". /// * name is the name field from build.zig.zon, asserted to be at most 32 /// bytes and assumed be a valid zig identifier @@ -197,54 +179,6 @@ pub const ProjectId = struct { } }; -pub const MultihashFunction = enum(u16) { - identity = 0x00, - sha1 = 0x11, - @"sha2-256" = 0x12, - @"sha2-512" = 0x13, - @"sha3-512" = 0x14, - @"sha3-384" = 0x15, - @"sha3-256" = 0x16, - @"sha3-224" = 0x17, - @"sha2-384" = 0x20, - @"sha2-256-trunc254-padded" = 0x1012, - @"sha2-224" = 0x1013, - @"sha2-512-224" = 0x1014, - @"sha2-512-256" = 0x1015, - @"blake2b-256" = 0xb220, - _, -}; - -pub const multihash_function: MultihashFunction = switch (Hash.Algo) { - std.crypto.hash.sha2.Sha256 => .@"sha2-256", - else => unreachable, -}; - -pub fn multiHashHexDigest(digest: Hash.Digest) MultiHashHexDigest { - const hex_charset = std.fmt.hex_charset; - - var result: MultiHashHexDigest = undefined; - - result[0] = hex_charset[@intFromEnum(multihash_function) >> 4]; - result[1] = hex_charset[@intFromEnum(multihash_function) & 15]; - - result[2] = hex_charset[Hash.Algo.digest_length >> 4]; - result[3] = hex_charset[Hash.Algo.digest_length & 15]; - - for (digest, 0..) |byte, i| { - result[4 + i * 2] = hex_charset[byte >> 4]; - result[5 + i * 2] = hex_charset[byte & 15]; - } - return result; -} - -comptime { - // We avoid unnecessary uleb128 code in hexDigest by asserting here the - // values are small enough to be contained in the one-byte encoding. - assert(@intFromEnum(multihash_function) < 127); - assert(Hash.Algo.digest_length < 127); -} - test Hash { const example_digest: Hash.Digest = .{ 0xc7, 0xf5, 0x71, 0xb7, 0xb4, 0xe7, 0x6f, 0x3c, 0xdb, 0x87, 0x7a, 0x7f, 0xdd, 0xf9, 0x77, 0x87, diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index d597aa0a87a6114da08e9006d5862ecc87d1f65b..f51532b10596a21be4ac665d34f0600b4c78b097 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -779,21 +779,11 @@ fn runResource( if (remote_hash) |declared_hash| { const hash_tok = f.hash_tok.unwrap().?; - if (declared_hash.isOld()) { - const actual_hex = Package.multiHashHexDigest(f.computed_hash.digest); - if (!std.mem.eql(u8, declared_hash.toSlice(), &actual_hex)) { - return f.fail(hash_tok, try eb.printString( - "hash mismatch: manifest declares '{s}' but the fetched package has '{s}'", - .{ declared_hash.toSlice(), actual_hex }, - )); - } - } else { - if (!computed_package_hash.eql(&declared_hash)) { - return f.fail(hash_tok, try eb.printString( - "hash mismatch: manifest declares '{s}' but the fetched package has '{s}'", - .{ declared_hash.toSlice(), computed_package_hash.toSlice() }, - )); - } + if (!computed_package_hash.eql(&declared_hash)) { + return f.fail(hash_tok, try eb.printString( + "hash mismatch: manifest declares '{s}' but the fetched package has '{s}'", + .{ declared_hash.toSlice(), computed_package_hash.toSlice() }, + )); } } else if (!f.omit_missing_hash_error) { const notes_len = 1; @@ -2239,207 +2229,6 @@ const UnpackResult = struct { } }; -test "set executable bit based on file content" { - if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest; - const gpa = std.testing.allocator; - const io = std.testing.io; - - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); - - const tarball_name = "executables.tar.gz"; - try saveEmbedFile(io, tarball_name, tmp.dir); - const tarball_path = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); - defer gpa.free(tarball_path); - - // $ tar -tvf executables.tar.gz - // drwxrwxr-x 0 executables/ - // -rwxrwxr-x 170 executables/hello - // lrwxrwxrwx 0 executables/hello_ln -> hello - // -rw-rw-r-- 0 executables/file1 - // -rw-rw-r-- 17 executables/script_with_shebang_without_exec_bit - // -rwxrwxr-x 7 executables/script_without_shebang - // -rwxrwxr-x 17 executables/script - - var fb: TestFetchBuilder = undefined; - var fetch = try fb.build(gpa, io, tmp.dir, tarball_path); - defer fb.deinit(); - - try fetch.run(); - try std.testing.expectEqualStrings( - "1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef3", - &Package.multiHashHexDigest(fetch.computed_hash.digest), - ); - - var out = try fb.packageDir(); - defer out.close(io); - const S = std.posix.S; - // expect executable bit not set - try std.testing.expect((try out.statFile(io, "file1", .{})).permissions.toMode() & S.IXUSR == 0); - try std.testing.expect((try out.statFile(io, "script_without_shebang", .{})).permissions.toMode() & S.IXUSR == 0); - // expect executable bit set - try std.testing.expect((try out.statFile(io, "hello", .{})).permissions.toMode() & S.IXUSR != 0); - try std.testing.expect((try out.statFile(io, "script", .{})).permissions.toMode() & S.IXUSR != 0); - try std.testing.expect((try out.statFile(io, "script_with_shebang_without_exec_bit", .{})).permissions.toMode() & S.IXUSR != 0); - try std.testing.expect((try out.statFile(io, "hello_ln", .{})).permissions.toMode() & S.IXUSR != 0); - - // - // $ ls -al zig-cache/tmp/OCz9ovUcstDjTC_U/zig-global-cache/p/1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef3 - // -rw-rw-r-- 1 0 Apr file1 - // -rwxrwxr-x 1 170 Apr hello - // lrwxrwxrwx 1 5 Apr hello_ln -> hello - // -rwxrwxr-x 1 17 Apr script - // -rw-rw-r-- 1 7 Apr script_without_shebang - // -rwxrwxr-x 1 17 Apr script_with_shebang_without_exec_bit -} - -fn saveEmbedFile(io: Io, comptime tarball_name: []const u8, dir: Io.Dir) !void { - //const tarball_name = "duplicate_paths_excluded.tar.gz"; - const tarball_content = @embedFile("Fetch/testdata/" ++ tarball_name); - var tmp_file = try dir.createFile(io, tarball_name, .{}); - defer tmp_file.close(io); - try tmp_file.writeStreamingAll(io, tarball_content); -} - -// Builds Fetch with required dependencies, clears dependencies on deinit(). -const TestFetchBuilder = struct { - http_client: std.http.Client, - global_cache_directory: Cache.Directory, - local_cache_path: Cache.Path, - job_queue: Fetch.JobQueue, - fetch: Fetch, - - fn build( - self: *TestFetchBuilder, - allocator: std.mem.Allocator, - io: Io, - cache_parent_dir: std.Io.Dir, - path_or_url: []const u8, - ) !*Fetch { - const global_cache_dir = try cache_parent_dir.createDirPathOpen(io, "zig-global-cache", .{}); - const package_root_dir = try cache_parent_dir.createDirPathOpen(io, "local-project-root", .{}); - - self.http_client = .{ .allocator = allocator, .io = io }; - self.global_cache_directory = .{ .handle = global_cache_dir, .path = "zig-global-cache" }; - self.local_cache_path = .{ - .root_dir = .{ .handle = package_root_dir, .path = "local-project-root" }, - .sub_path = ".zig-cache", - }; - - self.job_queue = .{ - .io = io, - .http_client = &self.http_client, - .global_cache = self.global_cache_directory, - .local_cache = self.local_cache_path, - .root_pkg_path = .{ - .root_dir = .{ .handle = package_root_dir, .path = "local-project-root" }, - .sub_path = "zig-pkg", - }, - .recursive = false, - .read_only = false, - .debug_hash = false, - .mode = .needed, - .prog_node = std.Progress.Node.none, - }; - - self.fetch = .{ - .arena = std.heap.ArenaAllocator.init(allocator), - .location = .{ .path_or_url = path_or_url }, - .location_tok = 0, - .hash_tok = .none, - .name_tok = 0, - .lazy_status = .eager, - .parent_package_root = .{ .root_dir = .{ .handle = package_root_dir, .path = null } }, - .parent_manifest_ast = null, - .prog_node = std.Progress.Node.none, - .job_queue = &self.job_queue, - .omit_missing_hash_error = true, - .allow_missing_paths_field = false, - .use_latest_commit = true, - - .package_root = undefined, - .error_bundle = undefined, - .manifest = undefined, - .manifest_ast = undefined, - .have_manifest = false, - .computed_hash = undefined, - .has_build_zig = false, - .oom_flag = false, - .latest_commit = null, - - .module = null, - }; - return &self.fetch; - } - - fn deinit(self: *TestFetchBuilder) void { - const io = self.job_queue.io; - self.fetch.deinit(); - self.job_queue.deinit(); - self.fetch.prog_node.end(); - self.global_cache_directory.handle.close(io); - self.http_client.deinit(); - } - - fn packageDir(self: *TestFetchBuilder) !Io.Dir { - const io = self.job_queue.io; - const root = self.fetch.package_root; - return try root.root_dir.handle.openDir(io, root.sub_path, .{ .iterate = true }); - } - - // Test helper, asserts thet package dir constains expected_files. - // expected_files must be sorted. - fn expectPackageFiles(self: *TestFetchBuilder, expected_files: []const []const u8) !void { - const io = self.job_queue.io; - const gpa = std.testing.allocator; - - var package_dir = try self.packageDir(); - defer package_dir.close(io); - - var actual_files: std.ArrayList([]u8) = .empty; - defer actual_files.deinit(gpa); - defer for (actual_files.items) |file| gpa.free(file); - var walker = try package_dir.walk(gpa); - defer walker.deinit(); - while (try walker.next(io)) |entry| { - if (entry.kind != .file) continue; - const path = try gpa.dupe(u8, entry.path); - errdefer gpa.free(path); - std.mem.replaceScalar(u8, path, std.fs.path.sep, '/'); - try actual_files.append(gpa, path); - } - std.mem.sortUnstable([]u8, actual_files.items, {}, struct { - fn lessThan(_: void, a: []u8, b: []u8) bool { - return std.mem.lessThan(u8, a, b); - } - }.lessThan); - - try std.testing.expectEqual(expected_files.len, actual_files.items.len); - for (expected_files, 0..) |file_name, i| { - try std.testing.expectEqualStrings(file_name, actual_files.items[i]); - } - try std.testing.expectEqualDeep(expected_files, actual_files.items); - } - - // Test helper, asserts that fetch has failed with `msg` error message. - fn expectFetchErrors(self: *TestFetchBuilder, notes_len: usize, msg: []const u8) !void { - const gpa = std.testing.allocator; - - var errors = try self.fetch.error_bundle.toOwnedBundle(""); - defer errors.deinit(gpa); - - const em = errors.getErrorMessage(errors.getMessages()[0]); - try std.testing.expectEqual(1, em.count); - if (notes_len > 0) { - try std.testing.expectEqual(notes_len, em.notes_len); - } - var aw: Io.Writer.Allocating = .init(gpa); - defer aw.deinit(); - try errors.renderToWriter(.{}, &aw.writer); - try std.testing.expectEqualStrings(msg, aw.written()); - } -}; - test { _ = Filter; _ = FileType; diff --git a/src/Package/Fetch/testdata/executables.tar.gz b/src/Package/Fetch/testdata/executables.tar.gz deleted file mode 100644 index abc650801ea6d416ad3cef2b87a6df205c71663b..0000000000000000000000000000000000000000 Binary files a/src/Package/Fetch/testdata/executables.tar.gz and /dev/null differ