authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-25 13:50:42-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-25 23:22:17-05:00
logee6df50678d985947bfb85cf7bfefb25ab68cdfc
treec74711a6e1b5671e98fbfca05042c0ef66f2b4d0
parent81a47dc874d8468db7120e1733acd122f90d8eab

fix package hashes on Windows

closes #14602

1 files changed, 32 insertions(+), 6 deletions(-)

src/Package.zig+32-6
...@@ -493,6 +493,11 @@ fn fetchAndUnpack(...@@ -493,6 +493,11 @@ fn fetchAndUnpack(
493 // apply those rules directly to the filesystem right here. This ensures that files493 // apply those rules directly to the filesystem right here. This ensures that files
494 // not protected by the hash are not present on the file system.494 // not protected by the hash are not present on the file system.
495495
496 // TODO: raise an error for files that have illegal paths on some operating systems.
497 // For example, on Linux a path with a backslash should raise an error here.
498 // Of course, if the ignore rules above omit the file from the package, then everything
499 // is fine and no error should be raised.
500
496 break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle });501 break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle });
497 };502 };
498503
...@@ -546,7 +551,8 @@ fn unpackTarball(...@@ -546,7 +551,8 @@ fn unpackTarball(
546}551}
547552
548const HashedFile = struct {553const HashedFile = struct {
549 path: []const u8,554 fs_path: []const u8,
555 normalized_path: []const u8,
550 hash: [Manifest.Hash.digest_length]u8,556 hash: [Manifest.Hash.digest_length]u8,
551 failure: Error!void,557 failure: Error!void,
552558
...@@ -554,7 +560,7 @@ const HashedFile = struct {...@@ -554,7 +560,7 @@ const HashedFile = struct {
554560
555 fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool {561 fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool {
556 _ = context;562 _ = context;
557 return mem.lessThan(u8, lhs.path, rhs.path);563 return mem.lessThan(u8, lhs.normalized_path, rhs.normalized_path);
558 }564 }
559};565};
560566
...@@ -590,8 +596,10 @@ fn computePackageHash(...@@ -590,8 +596,10 @@ fn computePackageHash(
590 else => return error.IllegalFileTypeInPackage,596 else => return error.IllegalFileTypeInPackage,
591 }597 }
592 const hashed_file = try arena.create(HashedFile);598 const hashed_file = try arena.create(HashedFile);
599 const fs_path = try arena.dupe(u8, entry.path);
593 hashed_file.* = .{600 hashed_file.* = .{
594 .path = try arena.dupe(u8, entry.path),601 .fs_path = fs_path,
602 .normalized_path = try normalizePath(arena, fs_path),
595 .hash = undefined, // to be populated by the worker603 .hash = undefined, // to be populated by the worker
596 .failure = undefined, // to be populated by the worker604 .failure = undefined, // to be populated by the worker
597 };605 };
...@@ -609,7 +617,7 @@ fn computePackageHash(...@@ -609,7 +617,7 @@ fn computePackageHash(
609 for (all_files.items) |hashed_file| {617 for (all_files.items) |hashed_file| {
610 hashed_file.failure catch |err| {618 hashed_file.failure catch |err| {
611 any_failures = true;619 any_failures = true;
612 std.log.err("unable to hash '{s}': {s}", .{ hashed_file.path, @errorName(err) });620 std.log.err("unable to hash '{s}': {s}", .{ hashed_file.fs_path, @errorName(err) });
613 };621 };
614 hasher.update(&hashed_file.hash);622 hasher.update(&hashed_file.hash);
615 }623 }
...@@ -617,6 +625,24 @@ fn computePackageHash(...@@ -617,6 +625,24 @@ fn computePackageHash(
617 return hasher.finalResult();625 return hasher.finalResult();
618}626}
619627
628/// Make a file system path identical independently of operating system path inconsistencies.
629/// This converts backslashes into forward slashes.
630fn normalizePath(arena: Allocator, fs_path: []const u8) ![]const u8 {
631 const canonical_sep = '/';
632
633 if (fs.path.sep == canonical_sep)
634 return fs_path;
635
636 const normalized = try arena.dupe(u8, fs_path);
637 for (normalized) |*byte| {
638 switch (byte.*) {
639 fs.path.sep => byte.* = canonical_sep,
640 else => continue,
641 }
642 }
643 return normalized;
644}
645
620fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void {646fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void {
621 defer wg.finish();647 defer wg.finish();
622 hashed_file.failure = hashFileFallible(dir, hashed_file);648 hashed_file.failure = hashFileFallible(dir, hashed_file);
...@@ -624,10 +650,10 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void {...@@ -624,10 +650,10 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void {
624650
625fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void {651fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void {
626 var buf: [8000]u8 = undefined;652 var buf: [8000]u8 = undefined;
627 var file = try dir.openFile(hashed_file.path, .{});653 var file = try dir.openFile(hashed_file.fs_path, .{});
628 defer file.close();654 defer file.close();
629 var hasher = Manifest.Hash.init(.{});655 var hasher = Manifest.Hash.init(.{});
630 hasher.update(hashed_file.path);656 hasher.update(hashed_file.normalized_path);
631 hasher.update(&.{ 0, @boolToInt(try isExecutable(file)) });657 hasher.update(&.{ 0, @boolToInt(try isExecutable(file)) });
632 while (true) {658 while (true) {
633 const bytes_read = try file.read(&buf);659 const bytes_read = try file.read(&buf);