From b171a6f25d5257f615b6369c11f8307dcf076c0d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Oct 2023 16:47:47 -0700 Subject: [PATCH] Package.Fetch: normalize path separators in symlinks closes #17549 --- lib/std/mem.zig | 11 +++++------ src/Package/Fetch.zig | 30 +++++++++++++++++------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 8d05be4117..e9d957d387 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -3810,12 +3810,11 @@ test "replace" { try testing.expectEqualStrings(expected, output[0..expected.len]); } -/// Replace all occurrences of `needle` with `replacement`. -pub fn replaceScalar(comptime T: type, slice: []T, needle: T, replacement: T) void { - for (slice, 0..) |e, i| { - if (e == needle) { - slice[i] = replacement; - } +/// Replace all occurrences of `match` with `replacement`. +pub fn replaceScalar(comptime T: type, slice: []T, match: T, replacement: T) void { + for (slice) |*e| { + if (e.* == match) + e.* = replacement; } } diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 5d315ffed4..ee835031a8 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -1329,7 +1329,7 @@ fn computeHash( const hashed_file = try arena.create(HashedFile); hashed_file.* = .{ .fs_path = fs_path, - .normalized_path = try normalizePath(arena, fs_path), + .normalized_path = try normalizePathAlloc(arena, fs_path), .kind = kind, .hash = undefined, // to be populated by the worker .failure = undefined, // to be populated by the worker @@ -1429,6 +1429,12 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void }, .sym_link => { const link_name = try dir.readLink(hashed_file.fs_path, &buf); + if (fs.path.sep != canonical_sep) { + // Package hashes are intended to be consistent across + // platforms which means we must normalize path separators + // inside symlinks. + normalizePath(link_name); + } hasher.update(link_name); }, } @@ -1484,22 +1490,20 @@ const HashedFile = struct { /// Make a file system path identical independently of operating system path inconsistencies. /// This converts backslashes into forward slashes. -fn normalizePath(arena: Allocator, fs_path: []const u8) ![]const u8 { - const canonical_sep = '/'; - - if (fs.path.sep == canonical_sep) - return fs_path; - +fn normalizePathAlloc(arena: Allocator, fs_path: []const u8) ![]const u8 { + if (fs.path.sep == canonical_sep) return fs_path; const normalized = try arena.dupe(u8, fs_path); - for (normalized) |*byte| { - switch (byte.*) { - fs.path.sep => byte.* = canonical_sep, - else => continue, - } - } + normalizePath(normalized); return normalized; } +const canonical_sep = fs.path.sep_posix; + +fn normalizePath(bytes: []u8) void { + assert(fs.path.sep != canonical_sep); + std.mem.replaceScalar(u8, bytes, fs.path.sep, canonical_sep); +} + const Filter = struct { include_paths: std.StringArrayHashMapUnmanaged(void) = .{},