@@ -140,11 +140,25 @@ pub const Header = struct {
}
pub fn mode(header: Header) !u32 {
return @intCast(try header.numeric(100, 8));
return @intCast(try header.octal(100, 8));
}
pub fn size(header: Header) !u64 {
return header.numeric(124, 12);
const start = 124;
const len = 12;
const raw = header.bytes[start..][0..len];
// If the leading byte is 0xff (255), all the bytes of the field
// (including the leading byte) are concatenated in big-endian order,
// with the result being a negative number expressed in two’s
// complement form.
if (raw[0] == 0xff) return error.TarNumericValueNegative;
// If the leading byte is 0x80 (128), the non-leading bytes of the
// field are concatenated in big-endian order.
if (raw[0] == 0x80) {
if (raw[1] + raw[2] + raw[3] != 0) return error.TarNumericValueTooBig;
return std.mem.readInt(u64, raw[4..12], .big);
}
return try header.octal(start, len);
}
pub fn chksum(header: Header) !u64 {
@@ -170,22 +184,6 @@ pub const Header = struct {
return nullStr(header.bytes[start .. start + len]);
}
fn numeric(header: Header, start: usize, len: usize) !u64 {
const raw = header.bytes[start..][0..len];
// If the leading byte is 0xff (255), all the bytes of the field
// (including the leading byte) are concatenated in big-endian order,
// with the result being a negative number expressed in two’s
// complement form.
if (raw[0] == 0xff) return error.TarNumericValueNegative;
// If the leading byte is 0x80 (128), the non-leading bytes of the
// field are concatenated in big-endian order.
if (raw[0] == 0x80) {
if (raw[1] + raw[2] + raw[3] != 0) return error.TarNumericValueTooBig;
return std.mem.readInt(u64, raw[4..12], .big);
}
return try header.octal(start, len);
}
fn octal(header: Header, start: usize, len: usize) !u64 {
const raw = header.bytes[start..][0..len];
// Zero-filled octal number in ASCII. Each numeric field of width w
@@ -756,3 +754,62 @@ test "tar PaxIterator" {
test {
_ = @import("tar/test.zig");
}
test "tar header parse size" {
const cases = [_]struct {
in: []const u8,
want: u64 = 0,
err: ?anyerror = null,
}{
// Test base-256 (binary) encoded values.
.{ .in = "", .want = 0 },
.{ .in = "\x80", .want = 0 },
.{ .in = "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", .want = 1 },
.{ .in = "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02", .want = 0x0102 },
.{ .in = "\x80\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08", .want = 0x0102030405060708 },
.{ .in = "\x80\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09", .err = error.TarNumericValueTooBig },
.{ .in = "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", .want = 537795476381659745 },
// // Test base-8 (octal) encoded values.
.{ .in = "00000000227\x00", .want = 0o227 },
.{ .in = " 000000227\x00", .want = 0o227 },
.{ .in = "00000000228\x00", .err = error.TarHeader },
.{ .in = "11111111111\x00", .want = 0o11111111111 },
};
for (cases) |case| {
var bytes = [_]u8{0} ** Header.SIZE;
@memcpy(bytes[124 .. 124 + case.in.len], case.in);
var header = Header{ .bytes = &bytes };
if (case.err) |err| {
try std.testing.expectError(err, header.size());
} else {
try std.testing.expectEqual(case.want, try header.size());
}
}
}
test "tar header parse mode" {
const cases = [_]struct {
in: []const u8,
want: u64 = 0,
err: ?anyerror = null,
}{
.{ .in = "0000644\x00", .want = 0o644 },
.{ .in = "0000777\x00", .want = 0o777 },
.{ .in = "7777777\x00", .want = 0o7777777 },
.{ .in = "7777778\x00", .err = error.TarHeader },
.{ .in = "77777777", .want = 0o77777777 },
.{ .in = "777777777777", .want = 0o77777777 },
};
for (cases) |case| {
var bytes = [_]u8{0} ** Header.SIZE;
@memcpy(bytes[100 .. 100 + case.in.len], case.in);
var header = Header{ .bytes = &bytes };
if (case.err) |err| {
try std.testing.expectError(err, header.mode());
} else {
try std.testing.expectEqual(case.want, try header.mode());
}
}
}