authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-23 19:20:38-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-26 11:42:03-08:00
loga57b0a0f2fe5a3a0af740495c60d4f3d0b6abfec
tree55f195dc055261860fd43c27f6c38d966d8e75b6
parente03bc7ac78820b7763d6ecd21cfa19653535f8d0

fix generated hash of by-path dependencies

This branch regressed from master by switching to binary rather than hex digest, allowing null bytes to end up in identifiers in the zig file. This commit fixes it by changing the "hash" to be literally equal to the sub_path (with a prefix '/' to indicate "global") if it can fit. If it is too long then it is actually hashed, and that value used instead.

2 files changed, 23 insertions(+), 14 deletions(-)

src/Package.zig+22
......@@ -15,6 +15,9 @@ pub const MultiHashHexDigest = [multihash_hex_digest_len]u8;
1515///
1616/// This data structure can be used to store the legacy hash format too. Legacy
1717/// hash format is scheduled to be removed after 0.14.0 is tagged.
18///
19/// There's also a third way this structure is used. When using path rather than
20/// hash, a unique hash is still needed, so one is computed based on the path.
1821pub const Hash = struct {
1922 /// Maximum size of a package hash. Unused bytes at the end are
2023 /// filled with zeroes.
......@@ -100,6 +103,25 @@ pub const Hash = struct {
100103 _ = std.base64.url_safe_no_pad.Encoder.encode(&name, digest[5..][0..24]);
101104 return init(digest, &name, "N", size);
102105 }
106
107 /// Produces a unique hash based on the path provided. The result should
108 /// not be user-visible.
109 pub fn initPath(sub_path: []const u8, is_global: bool) Hash {
110 var result: Hash = .{ .bytes = @splat(0) };
111 var i: usize = 0;
112 if (is_global) {
113 result.bytes[0] = '/';
114 i += 1;
115 }
116 if (i + sub_path.len <= result.bytes.len) {
117 @memcpy(result.bytes[i..][0..sub_path.len], sub_path);
118 return result;
119 }
120 var bin_digest: [Algo.digest_length]u8 = undefined;
121 Algo.hash(sub_path, &bin_digest, .{});
122 _ = std.fmt.bufPrint(result.bytes[i..], "{}", .{std.fmt.fmtSliceHexLower(&bin_digest)}) catch unreachable;
123 return result;
124 }
103125};
104126
105127pub const MultihashFunction = enum(u16) {
src/Package/Fetch.zig+1-14
......@@ -775,16 +775,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {
775775}
776776
777777pub fn relativePathDigest(pkg_root: Cache.Path, cache_root: Cache.Directory) Package.Hash {
778 var hasher = Package.Hash.Algo.init(.{});
779 // This hash is a tuple of:
780 // * whether it relative to the global cache directory or to the root package
781 // * the relative file path from there to the build root of the package
782 hasher.update(if (pkg_root.root_dir.eql(cache_root))
783 &package_hash_prefix_cached
784 else
785 &package_hash_prefix_project);
786 hasher.update(pkg_root.sub_path);
787 return .fromSlice(&hasher.finalResult());
778 return .initPath(pkg_root.sub_path, pkg_root.root_dir.eql(cache_root));
788779}
789780
790781pub fn workerRun(f: *Fetch, prog_name: []const u8) void {
......@@ -1793,10 +1784,6 @@ pub fn depDigest(pkg_root: Cache.Path, cache_root: Cache.Directory, dep: Manifes
17931784 }
17941785}
17951786
1796// These are random bytes.
1797const package_hash_prefix_cached = [8]u8{ 0x53, 0x7e, 0xfa, 0x94, 0x65, 0xe9, 0xf8, 0x73 };
1798const package_hash_prefix_project = [8]u8{ 0xe1, 0x25, 0xee, 0xfa, 0xa6, 0x17, 0x38, 0xcc };
1799
18001787const builtin = @import("builtin");
18011788const std = @import("std");
18021789const fs = std.fs;