authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2023-12-18 21:39:07+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-13 19:37:33-07:00
log7d3a31872eda55438259b54818baaa90b6ecd74c
tree0f37119cde4ccd495925d05ca70a7b939c252f16
parent7923a53996f8d24ad27823db3a45a6dd4a2bf317

tar: improve diagnostic reporting

Using Python testtar file (mentioned in #14310) to test diagnostic reporting. Added computing checksum by using both unsigned and signed header bytes values. Added skipping gnu exteneded sparse headers while reporting unsupported header in diagnostic. Note on testing: wget https://github.com/python/cpython/raw/3.11/Lib/test/testtar.tar -O /tmp/testtar.tar ``` test "Python testtar.tar file" { const file_name = "testtar.tar"; var file = try std.fs.cwd().openFile("/tmp/" ++ file_name, .{}); defer file.close(); var diag = Options.Diagnostics{ .allocator = std.testing.allocator }; defer diag.deinit(); var iter = iterator(file.reader(), &diag); while (try iter.next()) |f| { std.debug.print("supported: {} {s} {d}\n", .{ f.kind, f.name, f.size }); try f.skip(); } for (diag.errors.items) |e| { switch (e) { .unsupported_file_type => |u| { std.debug.print("unsupported: {} {s}\n", .{ u.file_type, u.file_name }); }, else => unreachable, } } } ```

1 files changed, 31 insertions(+), 9 deletions(-)

lib/std/tar.zig+31-9
...@@ -105,6 +105,8 @@ pub const Header = struct {...@@ -105,6 +105,8 @@ pub const Header = struct {
105 // used to store the path or link name for the next file.105 // used to store the path or link name for the next file.
106 gnu_long_name = 'L',106 gnu_long_name = 'L',
107 gnu_long_link = 'K',107 gnu_long_link = 'K',
108 gnu_sparse = 'S',
109 solaris_extended_header = 'X',
108 _,110 _,
109 };111 };
110112
...@@ -194,16 +196,21 @@ pub const Header = struct {...@@ -194,16 +196,21 @@ pub const Header = struct {
194 return std.fmt.parseInt(u64, rtrimmed, 8) catch return error.TarHeader;196 return std.fmt.parseInt(u64, rtrimmed, 8) catch return error.TarHeader;
195 }197 }
196198
199 const Chksums = struct {
200 unsigned: u64,
201 signed: i64,
202 };
203
197 // Sum of all bytes in the header block. The chksum field is treated as if204 // Sum of all bytes in the header block. The chksum field is treated as if
198 // it were filled with spaces (ASCII 32).205 // it were filled with spaces (ASCII 32).
199 fn computeChksum(header: Header) u64 {206 fn computeChksum(header: Header) Chksums {
200 var sum: u64 = 0;207 var cs: Chksums = .{ .signed = 0, .unsigned = 0 };
201 for (header.bytes, 0..) |b, i| {208 for (header.bytes, 0..) |v, i| {
202 if (148 <= i and i < 156) continue; // skip chksum field bytes209 const b = if (148 <= i and i < 156) 32 else v; // Treating chksum bytes as spaces.
203 sum += b;210 cs.unsigned += b;
211 cs.signed += @as(i8, @bitCast(b));
204 }212 }
205 // Treating chksum bytes as spaces. 256 = 8 * 32, 8 spaces.213 return cs;
206 return if (sum > 0) sum + 256 else 0;
207 }214 }
208215
209 // Checks calculated chksum with value of chksum field.216 // Checks calculated chksum with value of chksum field.
...@@ -211,8 +218,9 @@ pub const Header = struct {...@@ -211,8 +218,9 @@ pub const Header = struct {
211 // Zero value indicates empty block.218 // Zero value indicates empty block.
212 pub fn checkChksum(header: Header) !u64 {219 pub fn checkChksum(header: Header) !u64 {
213 const field = try header.chksum();220 const field = try header.chksum();
214 const computed = header.computeChksum();221 const cs = header.computeChksum();
215 if (field != computed) return error.TarHeaderChksum;222 if (field == 0 and cs.unsigned == 256) return 0;
223 if (field != cs.unsigned and field != cs.signed) return error.TarHeaderChksum;
216 return field;224 return field;
217 }225 }
218};226};
...@@ -387,11 +395,25 @@ fn Iterator(comptime ReaderType: type) type {...@@ -387,11 +395,25 @@ fn Iterator(comptime ReaderType: type) type {
387 .file_name = try d.allocator.dupe(u8, header.name()),395 .file_name = try d.allocator.dupe(u8, header.name()),
388 .file_type = kind,396 .file_type = kind,
389 } });397 } });
398 if (kind == .gnu_sparse) {
399 try self.skipGnuSparseExtendedHeaders(header);
400 }
401 self.reader.skipBytes(size, .{}) catch return error.TarHeadersTooBig;
390 },402 },
391 }403 }
392 }404 }
393 return null;405 return null;
394 }406 }
407
408 fn skipGnuSparseExtendedHeaders(self: *Self, header: Header) !void {
409 var is_extended = header.bytes[482] > 0;
410 while (is_extended) {
411 var buf: [Header.SIZE]u8 = undefined;
412 const n = try self.reader.readAll(&buf);
413 if (n < Header.SIZE) return error.UnexpectedEndOfStream;
414 is_extended = buf[504] > 0;
415 }
416 }
395 };417 };
396}418}
397419