Skip to content

Commit bf53d4e

Browse files
committed
tar: improve diagnostic reporting
Using Python testtar file (mentioned in ziglang#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 parent 26a4035 commit bf53d4e

File tree

1 file changed

+31
-9
lines changed

1 file changed

+31
-9
lines changed

lib/std/tar.zig

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ pub const Header = struct {
105105
// used to store the path or link name for the next file.
106106
gnu_long_name = 'L',
107107
gnu_long_link = 'K',
108+
gnu_sparse = 'S',
109+
solaris_extended_header = 'X',
108110
_,
109111
};
110112

@@ -194,25 +196,31 @@ pub const Header = struct {
194196
return std.fmt.parseInt(u64, rtrimmed, 8) catch return error.TarHeader;
195197
}
196198

199+
const Chksums = struct {
200+
unsigned: u64,
201+
signed: i64,
202+
};
203+
197204
// Sum of all bytes in the header block. The chksum field is treated as if
198205
// it were filled with spaces (ASCII 32).
199-
fn computeChksum(header: Header) u64 {
200-
var sum: u64 = 0;
201-
for (header.bytes, 0..) |b, i| {
202-
if (148 <= i and i < 156) continue; // skip chksum field bytes
203-
sum += b;
206+
fn computeChksum(header: Header) Chksums {
207+
var cs: Chksums = .{ .signed = 0, .unsigned = 0 };
208+
for (header.bytes, 0..) |v, i| {
209+
const b = if (148 <= i and i < 156) 32 else v; // Treating chksum bytes as spaces.
210+
cs.unsigned += b;
211+
cs.signed += @as(i8, @bitCast(b));
204212
}
205-
// Treating chksum bytes as spaces. 256 = 8 * 32, 8 spaces.
206-
return if (sum > 0) sum + 256 else 0;
213+
return cs;
207214
}
208215

209216
// Checks calculated chksum with value of chksum field.
210217
// Returns error or valid chksum value.
211218
// Zero value indicates empty block.
212219
pub fn checkChksum(header: Header) !u64 {
213220
const field = try header.chksum();
214-
const computed = header.computeChksum();
215-
if (field != computed) return error.TarHeaderChksum;
221+
const cs = header.computeChksum();
222+
if (field == 0 and cs.unsigned == 256) return 0;
223+
if (field != cs.unsigned and field != cs.signed) return error.TarHeaderChksum;
216224
return field;
217225
}
218226
};
@@ -387,11 +395,25 @@ fn Iterator(comptime ReaderType: type) type {
387395
.file_name = try d.allocator.dupe(u8, header.name()),
388396
.file_type = kind,
389397
} });
398+
if (kind == .gnu_sparse) {
399+
try self.skipGnuSparseExtendedHeaders(header);
400+
}
401+
self.reader.skipBytes(size, .{}) catch return error.TarHeadersTooBig;
390402
},
391403
}
392404
}
393405
return null;
394406
}
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+
}
395417
};
396418
}
397419

0 commit comments

Comments
 (0)