diff options
| author | Igor Anić <igor.anic@gmail.com> | 2023-11-29 17:17:20 +0100 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-01-13 19:37:33 -0700 |
| commit | 16c40fc4713c195c7a6b8544c9dffbfc6201dc9d (patch) | |
| tree | 2e17e3e675065d54f1e900c8bb3b714094ba2ab5 /lib/std | |
| parent | 169f28d3e6a908717a0e42323ba1a0ee765976da (diff) | |
| download | zig-16c40fc4713c195c7a6b8544c9dffbfc6201dc9d.tar.gz zig-16c40fc4713c195c7a6b8544c9dffbfc6201dc9d.zip | |
tar: add header chksum checking
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/tar.zig | 102 |
1 files changed, 64 insertions, 38 deletions
diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 6e13909905..40ca26da79 100644 --- a/lib/std/tar.zig +++ b/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{}; |
