| ... | ... | @@ -85,31 +85,6 @@ pub const Header = struct { |
| 85 | 85 | _, |
| 86 | 86 | }; |
| 87 | 87 | |
| 88 | | pub fn fileSize(header: Header) !u64 { |
| 89 | | const raw = header.bytes[124..][0..12]; |
| 90 | | // If the leading byte is 0xff (255), all the bytes of the field |
| 91 | | // (including the leading byte) are concatenated in big-endian order, |
| 92 | | // with the result being a negative number expressed in two’s |
| 93 | | // complement form. |
| 94 | | if (raw[0] == 0xff) return error.SizeNegative; |
| 95 | | // If the leading byte is 0x80 (128), the non-leading bytes of the |
| 96 | | // field are concatenated in big-endian order. |
| 97 | | if (raw[0] == 0x80) { |
| 98 | | if (raw[1] + raw[2] + raw[3] != 0) return error.SizeTooBig; |
| 99 | | return std.mem.readInt(u64, raw[4..12], .big); |
| 100 | | } |
| 101 | | // Zero-filled octal number in ASCII. Each numeric field of width w |
| 102 | | // contains w minus 1 digits, and a null |
| 103 | | const ltrimmed = std.mem.trimLeft(u8, raw, "0 "); |
| 104 | | const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00"); |
| 105 | | if (rtrimmed.len == 0) return 0; |
| 106 | | return std.fmt.parseInt(u64, rtrimmed, 8); |
| 107 | | } |
| 108 | | |
| 109 | | pub fn is_ustar(header: Header) bool { |
| 110 | | return std.mem.eql(u8, header.bytes[257..][0..6], "ustar\x00"); |
| 111 | | } |
| 112 | | |
| 113 | 88 | /// Includes prefix concatenated, if any. |
| 114 | 89 | /// Return value may point into Header buffer, or might point into the |
| 115 | 90 | /// argument buffer. |
| ... | ... | @@ -128,15 +103,27 @@ pub const Header = struct { |
| 128 | 103 | } |
| 129 | 104 | |
| 130 | 105 | pub fn name(header: Header) []const u8 { |
| 131 | | return str(header, 0, 0 + 100); |
| 106 | return header.str(0, 100); |
| 107 | } |
| 108 | |
| 109 | pub fn fileSize(header: Header) !u64 { |
| 110 | return header.numeric(124, 12); |
| 111 | } |
| 112 | |
| 113 | pub fn chksum(header: Header) !u64 { |
| 114 | return header.octal(148, 8); |
| 132 | 115 | } |
| 133 | 116 | |
| 134 | 117 | pub fn linkName(header: Header) []const u8 { |
| 135 | | return str(header, 157, 157 + 100); |
| 118 | return header.str(157, 100); |
| 119 | } |
| 120 | |
| 121 | pub fn is_ustar(header: Header) bool { |
| 122 | return std.mem.eql(u8, header.bytes[257..][0..6], "ustar\x00"); |
| 136 | 123 | } |
| 137 | 124 | |
| 138 | 125 | pub fn prefix(header: Header) []const u8 { |
| 139 | | return str(header, 345, 345 + 155); |
| 126 | return header.str(345, 155); |
| 140 | 127 | } |
| 141 | 128 | |
| 142 | 129 | pub fn fileType(header: Header) FileType { |
| ... | ... | @@ -145,7 +132,8 @@ pub const Header = struct { |
| 145 | 132 | return result; |
| 146 | 133 | } |
| 147 | 134 | |
| 148 | | fn str(header: Header, start: usize, end: usize) []const u8 { |
| 135 | fn str(header: Header, start: usize, len: usize) []const u8 { |
| 136 | const end = start + len; |
| 149 | 137 | var i: usize = start; |
| 150 | 138 | while (i < end) : (i += 1) { |
| 151 | 139 | if (header.bytes[i] == 0) break; |
| ... | ... | @@ -153,11 +141,52 @@ pub const Header = struct { |
| 153 | 141 | return header.bytes[start..i]; |
| 154 | 142 | } |
| 155 | 143 | |
| 156 | | pub fn isZero(header: Header) bool { |
| 157 | | for (header.bytes) |b| { |
| 158 | | if (b != 0) return false; |
| 144 | fn numeric(header: Header, start: usize, len: usize) !u64 { |
| 145 | const raw = header.bytes[start..][0..len]; |
| 146 | // If the leading byte is 0xff (255), all the bytes of the field |
| 147 | // (including the leading byte) are concatenated in big-endian order, |
| 148 | // with the result being a negative number expressed in two’s |
| 149 | // complement form. |
| 150 | if (raw[0] == 0xff) return error.TarNumericValueNegative; |
| 151 | // If the leading byte is 0x80 (128), the non-leading bytes of the |
| 152 | // field are concatenated in big-endian order. |
| 153 | if (raw[0] == 0x80) { |
| 154 | if (raw[1] + raw[2] + raw[3] != 0) return error.TarNumericValueTooBig; |
| 155 | return std.mem.readInt(u64, raw[4..12], .big); |
| 159 | 156 | } |
| 160 | | return true; |
| 157 | return try header.octal(start, len); |
| 158 | } |
| 159 | |
| 160 | fn octal(header: Header, start: usize, len: usize) !u64 { |
| 161 | const raw = header.bytes[start..][0..len]; |
| 162 | // Zero-filled octal number in ASCII. Each numeric field of width w |
| 163 | // contains w minus 1 digits, and a null |
| 164 | const ltrimmed = std.mem.trimLeft(u8, raw, "0 "); |
| 165 | const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00"); |
| 166 | if (rtrimmed.len == 0) return 0; |
| 167 | return std.fmt.parseInt(u64, rtrimmed, 8); |
| 168 | } |
| 169 | |
| 170 | // Sum of all bytes in the header block. The chksum field is treated as if |
| 171 | // it were filled with spaces (ASCII 32). |
| 172 | fn computeChksum(header: Header) u64 { |
| 173 | var sum: u64 = 0; |
| 174 | for (header.bytes, 0..) |b, i| { |
| 175 | if (148 <= i and i < 156) continue; // skip chksum field bytes |
| 176 | sum += b; |
| 177 | } |
| 178 | // Treating chksum bytes as spaces. 256 = 8 * 32, 8 spaces. |
| 179 | return if (sum > 0) sum + 256 else 0; |
| 180 | } |
| 181 | |
| 182 | // Checks calculated chksum with value of chksum field. |
| 183 | // Returns error or chksum value. |
| 184 | // Zero value indicates empty block. |
| 185 | pub fn checkChksum(header: Header) !u64 { |
| 186 | const field = try header.chksum(); |
| 187 | const computed = header.computeChksum(); |
| 188 | if (field != computed) return error.TarHeaderChksum; |
| 189 | return field; |
| 161 | 190 | } |
| 162 | 191 | }; |
| 163 | 192 | |
| ... | ... | @@ -368,8 +397,8 @@ fn Iterator(comptime ReaderType: type) type { |
| 368 | 397 | self.attrs.free(); |
| 369 | 398 | |
| 370 | 399 | while (try self.reader.readBlock()) |block_bytes| { |
| 371 | | const block: Header = .{ .bytes = block_bytes[0..BLOCK_SIZE] }; |
| 372 | | if (block.isZero()) return null; |
| 400 | const block = Header{ .bytes = block_bytes[0..BLOCK_SIZE] }; |
| 401 | if (try block.checkChksum() == 0) return null; // zero block found |
| 373 | 402 | const file_type = block.fileType(); |
| 374 | 403 | const file_size = try block.fileSize(); |
| 375 | 404 | |
| ... | ... | @@ -578,9 +607,6 @@ test parsePaxAttribute { |
| 578 | 607 | try expectError(error.InvalidPaxAttribute, parsePaxAttribute("", 0)); |
| 579 | 608 | } |
| 580 | 609 | |
| 581 | | const std = @import("std"); |
| 582 | | const assert = std.debug.assert; |
| 583 | | |
| 584 | 610 | const TestCase = struct { |
| 585 | 611 | const File = struct { |
| 586 | 612 | const empty_string = &[0]u8{}; |