| ... | @@ -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 | }; |
| 50 | | 53 | |
| 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) |
| 623 | | 629 | |
| 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 | } |
| 629 | | 642 | |
| 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 | } |
| 1098 | | 1109 | |
| | 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 | |
| 1099 | fn normalizePath(bytes: []u8) []u8 { | 1134 | fn 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; |