| ... | @@ -1,4 +1,7 @@ | ... | @@ -1,4 +1,7 @@ |
| 1 | pub const Options = struct {}; | 1 | pub const Options = struct { |
| | 2 | /// Number of directory levels to skip when extracting files. |
| | 3 | strip_components: u32 = 0, |
| | 4 | }; |
| 2 | | 5 | |
| 3 | pub const Header = struct { | 6 | pub const Header = struct { |
| 4 | bytes: *const [512]u8, | 7 | bytes: *const [512]u8, |
| ... | @@ -69,7 +72,6 @@ pub const Header = struct { | ... | @@ -69,7 +72,6 @@ pub const Header = struct { |
| 69 | }; | 72 | }; |
| 70 | | 73 | |
| 71 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { | 74 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { |
| 72 | _ = options; | | |
| 73 | var file_name_buffer: [255]u8 = undefined; | 75 | var file_name_buffer: [255]u8 = undefined; |
| 74 | var buffer: [512 * 8]u8 = undefined; | 76 | var buffer: [512 * 8]u8 = undefined; |
| 75 | var start: usize = 0; | 77 | var start: usize = 0; |
| ... | @@ -92,13 +94,17 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi | ... | @@ -92,13 +94,17 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi |
| 92 | const file_size = try header.fileSize(); | 94 | const file_size = try header.fileSize(); |
| 93 | const rounded_file_size = std.mem.alignForwardGeneric(u64, file_size, 512); | 95 | const rounded_file_size = std.mem.alignForwardGeneric(u64, file_size, 512); |
| 94 | const pad_len = rounded_file_size - file_size; | 96 | const pad_len = rounded_file_size - file_size; |
| 95 | const file_name = try header.fullFileName(&file_name_buffer); | 97 | const unstripped_file_name = try header.fullFileName(&file_name_buffer); |
| 96 | switch (header.fileType()) { | 98 | switch (header.fileType()) { |
| 97 | .directory => { | 99 | .directory => { |
| 98 | try dir.makeDir(file_name); | 100 | const file_name = try stripComponents(unstripped_file_name, options.strip_components); |
| | 101 | if (file_name.len != 0) { |
| | 102 | try dir.makeDir(file_name); |
| | 103 | } |
| 99 | }, | 104 | }, |
| 100 | .normal => { | 105 | .normal => { |
| 101 | if (file_size == 0 and file_name.len == 0) return; | 106 | if (file_size == 0 and unstripped_file_name.len == 0) return; |
| | 107 | const file_name = try stripComponents(unstripped_file_name, options.strip_components); |
| 102 | | 108 | |
| 103 | var file = try dir.createFile(file_name, .{}); | 109 | var file = try dir.createFile(file_name, .{}); |
| 104 | defer file.close(); | 110 | defer file.close(); |
| ... | @@ -140,5 +146,25 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi | ... | @@ -140,5 +146,25 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi |
| 140 | } | 146 | } |
| 141 | } | 147 | } |
| 142 | | 148 | |
| | 149 | fn stripComponents(path: []const u8, count: u32) ![]const u8 { |
| | 150 | var i: usize = 0; |
| | 151 | var c = count; |
| | 152 | while (c > 0) : (c -= 1) { |
| | 153 | if (std.mem.indexOfScalarPos(u8, path, i, '/')) |pos| { |
| | 154 | i = pos + 1; |
| | 155 | } else { |
| | 156 | return error.TarComponentsOutsideStrippedPrefix; |
| | 157 | } |
| | 158 | } |
| | 159 | return path[i..]; |
| | 160 | } |
| | 161 | |
| | 162 | test stripComponents { |
| | 163 | const expectEqualStrings = std.testing.expectEqualStrings; |
| | 164 | try expectEqualStrings("a/b/c", try stripComponents("a/b/c", 0)); |
| | 165 | try expectEqualStrings("b/c", try stripComponents("a/b/c", 1)); |
| | 166 | try expectEqualStrings("c", try stripComponents("a/b/c", 2)); |
| | 167 | } |
| | 168 | |
| 143 | const std = @import("std.zig"); | 169 | const std = @import("std.zig"); |
| 144 | const assert = std.debug.assert; | 170 | const assert = std.debug.assert; |