Skip to content

Commit 035c1b6

Browse files
ianicandrewrk
authored andcommitted
std.tar: add strip components error to diagnostics
This was the only kind of error which was raised in pipeToFileSystem and not added to Diagnostics. Shell tar silently ignores paths which are stripped out when used with `--strip-components` switch. This enables that same behavior, errors will be collected in diagnostics but caller is free to ignore that type of diagnostics errors. Enables use case where caller knows structure of the tar file and want to extract only some deeply nested folders ignoring upper files/folders. Fixes: ziglang#17620 by giving caller options: - not provide diagnostic and get errors - provide diagnostics and analyze errors - provide diagnostics and ignore errors
1 parent fe66a12 commit 035c1b6

File tree

2 files changed

+39
-3
lines changed

2 files changed

+39
-3
lines changed

lib/std/tar.zig

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ pub const Diagnostics = struct {
4646
file_name: []const u8,
4747
file_type: Header.Kind,
4848
},
49+
components_outside_stripped_prefix: struct {
50+
file_name: []const u8,
51+
},
4952
};
5053

5154
fn findRoot(d: *Diagnostics, path: []const u8) !void {
@@ -97,6 +100,9 @@ pub const Diagnostics = struct {
97100
.unsupported_file_type => |info| {
98101
d.allocator.free(info.file_name);
99102
},
103+
.components_outside_stripped_prefix => |info| {
104+
d.allocator.free(info.file_name);
105+
},
100106
}
101107
}
102108
d.errors.deinit(d.allocator);
@@ -623,18 +629,24 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions)
623629

624630
while (try iter.next()) |file| {
625631
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+
}
626639
if (options.diagnostics) |d| {
627640
try d.findRoot(file_name);
628641
}
629642

630643
switch (file.kind) {
631644
.directory => {
632-
if (file_name.len != 0 and !options.exclude_empty_directories) {
645+
if (file_name.len > 0 and !options.exclude_empty_directories) {
633646
try dir.makePath(file_name);
634647
}
635648
},
636649
.file => {
637-
if (file_name.len == 0) return error.BadFileName;
638650
if (createDirAndFile(dir, file_name, fileMode(file.mode, options))) |fs_file| {
639651
defer fs_file.close();
640652
try file.writeAll(fs_file);
@@ -647,7 +659,6 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: PipeOptions)
647659
}
648660
},
649661
.sym_link => {
650-
if (file_name.len == 0) return error.BadFileName;
651662
const link_name = file.link_name;
652663
createDirAndSymlink(dir, link_name, file_name) catch |err| {
653664
const d = options.diagnostics orelse return error.UnableToCreateSymLink;
@@ -1096,6 +1107,30 @@ test "findRoot without explicit root dir" {
10961107
try testing.expectEqualStrings("root", diagnostics.root_dir);
10971108
}
10981109

1110+
test "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+
10991134
fn normalizePath(bytes: []u8) []u8 {
11001135
const canonical_sep = std.fs.path.sep_posix;
11011136
if (std.fs.path.sep == canonical_sep) return bytes;

src/Package/Fetch.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,7 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackRes
11891189
.unable_to_create_file => |i| res.unableToCreateFile(stripRoot(i.file_name, res.root_dir), i.code),
11901190
.unable_to_create_sym_link => |i| res.unableToCreateSymLink(stripRoot(i.file_name, res.root_dir), i.link_name, i.code),
11911191
.unsupported_file_type => |i| res.unsupportedFileType(stripRoot(i.file_name, res.root_dir), @intFromEnum(i.file_type)),
1192+
.components_outside_stripped_prefix => {}, // impossible with strip_components = 0
11921193
}
11931194
}
11941195
}

0 commit comments

Comments
 (0)