authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-03 21:29:04-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-03 21:29:04-04:00
log768b17755e7735b328b92212de2dd7018f78fb4b
tree162e5853959a567ad942fe9b685766292834cf6f
parentefb00c20eefff7bf1ef2d2d7bb9ef16374ebfd27
parente0350859bb88d70b95a6b99932a9a5452f8b6b14
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19615 from ianic/tar_diagnostic

std.tar: add strip components error to diagnostics

2 files changed, 39 insertions(+), 3 deletions(-)

lib/std/tar.zig+38-3
...@@ -46,6 +46,9 @@ pub const Diagnostics = struct {...@@ -46,6 +46,9 @@ pub const Diagnostics = struct {
46 file_name: []const u8,46 file_name: []const u8,
47 file_type: Header.Kind,47 file_type: Header.Kind,
48 },48 },
49 components_outside_stripped_prefix: struct {
50 file_name: []const u8,
51 },
49 };52 };
5053
51 fn findRoot(d: *Diagnostics, path: []const u8) !void {54 fn findRoot(d: *Diagnostics, path: []const u8) !void {
...@@ -97,6 +100,9 @@ pub const Diagnostics = struct {...@@ -97,6 +100,9 @@ pub const Diagnostics = struct {
97 .unsupported_file_type => |info| {100 .unsupported_file_type => |info| {
98 d.allocator.free(info.file_name);101 d.allocator.free(info.file_name);
99 },102 },
103 .components_outside_stripped_prefix => |info| {
104 d.allocator.free(info.file_name);
105 },
100 }106 }
101 }107 }
102 d.errors.deinit(d.allocator);108 d.errors.deinit(d.allocator);
...@@ -623,18 +629,24 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions)...@@ -623,18 +629,24 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions)
623629
624 while (try iter.next()) |file| {630 while (try iter.next()) |file| {
625 const file_name = stripComponents(file.name, options.strip_components);631 const file_name = stripComponents(file.name, options.strip_components);
632 if (file_name.len == 0 and file.kind != .directory) {
633 const d = options.diagnostics orelse return error.TarComponentsOutsideStrippedPrefix;
634 try d.errors.append(d.allocator, .{ .components_outside_stripped_prefix = .{
635 .file_name = try d.allocator.dupe(u8, file.name),
636 } });
637 continue;
638 }
626 if (options.diagnostics) |d| {639 if (options.diagnostics) |d| {
627 try d.findRoot(file_name);640 try d.findRoot(file_name);
628 }641 }
629642
630 switch (file.kind) {643 switch (file.kind) {
631 .directory => {644 .directory => {
632 if (file_name.len != 0 and !options.exclude_empty_directories) {645 if (file_name.len > 0 and !options.exclude_empty_directories) {
633 try dir.makePath(file_name);646 try dir.makePath(file_name);
634 }647 }
635 },648 },
636 .file => {649 .file => {
637 if (file_name.len == 0) return error.BadFileName;
638 if (createDirAndFile(dir, file_name, fileMode(file.mode, options))) |fs_file| {650 if (createDirAndFile(dir, file_name, fileMode(file.mode, options))) |fs_file| {
639 defer fs_file.close();651 defer fs_file.close();
640 try file.writeAll(fs_file);652 try file.writeAll(fs_file);
...@@ -647,7 +659,6 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions)...@@ -647,7 +659,6 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions)
647 }659 }
648 },660 },
649 .sym_link => {661 .sym_link => {
650 if (file_name.len == 0) return error.BadFileName;
651 const link_name = file.link_name;662 const link_name = file.link_name;
652 createDirAndSymlink(dir, link_name, file_name) catch |err| {663 createDirAndSymlink(dir, link_name, file_name) catch |err| {
653 const d = options.diagnostics orelse return error.UnableToCreateSymLink;664 const d = options.diagnostics orelse return error.UnableToCreateSymLink;
...@@ -1096,6 +1107,30 @@ test "findRoot without explicit root dir" {...@@ -1096,6 +1107,30 @@ test "findRoot without explicit root dir" {
1096 try testing.expectEqualStrings("root", diagnostics.root_dir);1107 try testing.expectEqualStrings("root", diagnostics.root_dir);
1097}1108}
10981109
1110test "pipeToFileSystem strip_components" {
1111 const data = @embedFile("tar/testdata/example.tar");
1112 var fbs = std.io.fixedBufferStream(data);
1113 const reader = fbs.reader();
1114
1115 var tmp = testing.tmpDir(.{ .no_follow = true });
1116 defer tmp.cleanup();
1117 var diagnostics: Diagnostics = .{ .allocator = testing.allocator };
1118 defer diagnostics.deinit();
1119
1120 pipeToFileSystem(tmp.dir, reader, .{
1121 .strip_components = 3,
1122 .diagnostics = &diagnostics,
1123 }) catch |err| {
1124 // Skip on platform which don't support symlinks
1125 if (err == error.UnableToCreateSymLink) return error.SkipZigTest;
1126 return err;
1127 };
1128
1129 try testing.expectEqual(2, diagnostics.errors.items.len);
1130 try testing.expectEqualStrings("example/b/symlink", diagnostics.errors.items[0].components_outside_stripped_prefix.file_name);
1131 try testing.expectEqualStrings("example/a/file", diagnostics.errors.items[1].components_outside_stripped_prefix.file_name);
1132}
1133
1099fn normalizePath(bytes: []u8) []u8 {1134fn normalizePath(bytes: []u8) []u8 {
1100 const canonical_sep = std.fs.path.sep_posix;1135 const canonical_sep = std.fs.path.sep_posix;
1101 if (std.fs.path.sep == canonical_sep) return bytes;1136 if (std.fs.path.sep == canonical_sep) return bytes;
src/Package/Fetch.zig+1
...@@ -1189,6 +1189,7 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackRes...@@ -1189,6 +1189,7 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackRes
1189 .unable_to_create_file => |i| res.unableToCreateFile(stripRoot(i.file_name, res.root_dir), i.code),1189 .unable_to_create_file => |i| res.unableToCreateFile(stripRoot(i.file_name, res.root_dir), i.code),
1190 .unable_to_create_sym_link => |i| res.unableToCreateSymLink(stripRoot(i.file_name, res.root_dir), i.link_name, i.code),1190 .unable_to_create_sym_link => |i| res.unableToCreateSymLink(stripRoot(i.file_name, res.root_dir), i.link_name, i.code),
1191 .unsupported_file_type => |i| res.unsupportedFileType(stripRoot(i.file_name, res.root_dir), @intFromEnum(i.file_type)),1191 .unsupported_file_type => |i| res.unsupportedFileType(stripRoot(i.file_name, res.root_dir), @intFromEnum(i.file_type)),
1192 .components_outside_stripped_prefix => unreachable, // unreachable with strip_components = 0
1192 }1193 }
1193 }1194 }
1194 }1195 }