mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 22:35:24 +00:00
tar: add header chksum checking
This commit is contained in:
parent
169f28d3e6
commit
16c40fc471
102
lib/std/tar.zig
102
lib/std/tar.zig
@ -85,31 +85,6 @@ pub const Header = struct {
|
||||
_,
|
||||
};
|
||||
|
||||
pub fn fileSize(header: Header) !u64 {
|
||||
const raw = header.bytes[124..][0..12];
|
||||
// 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.SizeNegative;
|
||||
// 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.SizeTooBig;
|
||||
return std.mem.readInt(u64, raw[4..12], .big);
|
||||
}
|
||||
// Zero-filled octal number in ASCII. Each numeric field of width w
|
||||
// contains w minus 1 digits, and a null
|
||||
const ltrimmed = std.mem.trimLeft(u8, raw, "0 ");
|
||||
const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00");
|
||||
if (rtrimmed.len == 0) return 0;
|
||||
return std.fmt.parseInt(u64, rtrimmed, 8);
|
||||
}
|
||||
|
||||
pub fn is_ustar(header: Header) bool {
|
||||
return std.mem.eql(u8, header.bytes[257..][0..6], "ustar\x00");
|
||||
}
|
||||
|
||||
/// Includes prefix concatenated, if any.
|
||||
/// Return value may point into Header buffer, or might point into the
|
||||
/// argument buffer.
|
||||
@ -128,15 +103,27 @@ pub const Header = struct {
|
||||
}
|
||||
|
||||
pub fn name(header: Header) []const u8 {
|
||||
return str(header, 0, 0 + 100);
|
||||
return header.str(0, 100);
|
||||
}
|
||||
|
||||
pub fn fileSize(header: Header) !u64 {
|
||||
return header.numeric(124, 12);
|
||||
}
|
||||
|
||||
pub fn chksum(header: Header) !u64 {
|
||||
return header.octal(148, 8);
|
||||
}
|
||||
|
||||
pub fn linkName(header: Header) []const u8 {
|
||||
return str(header, 157, 157 + 100);
|
||||
return header.str(157, 100);
|
||||
}
|
||||
|
||||
pub fn is_ustar(header: Header) bool {
|
||||
return std.mem.eql(u8, header.bytes[257..][0..6], "ustar\x00");
|
||||
}
|
||||
|
||||
pub fn prefix(header: Header) []const u8 {
|
||||
return str(header, 345, 345 + 155);
|
||||
return header.str(345, 155);
|
||||
}
|
||||
|
||||
pub fn fileType(header: Header) FileType {
|
||||
@ -145,7 +132,8 @@ pub const Header = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
fn str(header: Header, start: usize, end: usize) []const u8 {
|
||||
fn str(header: Header, start: usize, len: usize) []const u8 {
|
||||
const end = start + len;
|
||||
var i: usize = start;
|
||||
while (i < end) : (i += 1) {
|
||||
if (header.bytes[i] == 0) break;
|
||||
@ -153,11 +141,52 @@ pub const Header = struct {
|
||||
return header.bytes[start..i];
|
||||
}
|
||||
|
||||
pub fn isZero(header: Header) bool {
|
||||
for (header.bytes) |b| {
|
||||
if (b != 0) return false;
|
||||
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 true;
|
||||
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
|
||||
// contains w minus 1 digits, and a null
|
||||
const ltrimmed = std.mem.trimLeft(u8, raw, "0 ");
|
||||
const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00");
|
||||
if (rtrimmed.len == 0) return 0;
|
||||
return std.fmt.parseInt(u64, rtrimmed, 8);
|
||||
}
|
||||
|
||||
// Sum of all bytes in the header block. The chksum field is treated as if
|
||||
// it were filled with spaces (ASCII 32).
|
||||
fn computeChksum(header: Header) u64 {
|
||||
var sum: u64 = 0;
|
||||
for (header.bytes, 0..) |b, i| {
|
||||
if (148 <= i and i < 156) continue; // skip chksum field bytes
|
||||
sum += b;
|
||||
}
|
||||
// Treating chksum bytes as spaces. 256 = 8 * 32, 8 spaces.
|
||||
return if (sum > 0) sum + 256 else 0;
|
||||
}
|
||||
|
||||
// Checks calculated chksum with value of chksum field.
|
||||
// Returns error or chksum value.
|
||||
// Zero value indicates empty block.
|
||||
pub fn checkChksum(header: Header) !u64 {
|
||||
const field = try header.chksum();
|
||||
const computed = header.computeChksum();
|
||||
if (field != computed) return error.TarHeaderChksum;
|
||||
return field;
|
||||
}
|
||||
};
|
||||
|
||||
@ -368,8 +397,8 @@ fn Iterator(comptime ReaderType: type) type {
|
||||
self.attrs.free();
|
||||
|
||||
while (try self.reader.readBlock()) |block_bytes| {
|
||||
const block: Header = .{ .bytes = block_bytes[0..BLOCK_SIZE] };
|
||||
if (block.isZero()) return null;
|
||||
const block = Header{ .bytes = block_bytes[0..BLOCK_SIZE] };
|
||||
if (try block.checkChksum() == 0) return null; // zero block found
|
||||
const file_type = block.fileType();
|
||||
const file_size = try block.fileSize();
|
||||
|
||||
@ -578,9 +607,6 @@ test parsePaxAttribute {
|
||||
try expectError(error.InvalidPaxAttribute, parsePaxAttribute("", 0));
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const TestCase = struct {
|
||||
const File = struct {
|
||||
const empty_string = &[0]u8{};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user