| ... | ... | @@ -105,6 +105,8 @@ pub const Header = struct { |
| 105 | 105 | // used to store the path or link name for the next file. |
| 106 | 106 | gnu_long_name = 'L', |
| 107 | 107 | gnu_long_link = 'K', |
| 108 | gnu_sparse = 'S', |
| 109 | solaris_extended_header = 'X', |
| 108 | 110 | _, |
| 109 | 111 | }; |
| 110 | 112 | |
| ... | ... | @@ -194,16 +196,21 @@ pub const Header = struct { |
| 194 | 196 | return std.fmt.parseInt(u64, rtrimmed, 8) catch return error.TarHeader; |
| 195 | 197 | } |
| 196 | 198 | |
| 199 | const Chksums = struct { |
| 200 | unsigned: u64, |
| 201 | signed: i64, |
| 202 | }; |
| 203 | |
| 197 | 204 | // Sum of all bytes in the header block. The chksum field is treated as if |
| 198 | 205 | // 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)); |
| 204 | 212 | } |
| 205 | | // Treating chksum bytes as spaces. 256 = 8 * 32, 8 spaces. |
| 206 | | return if (sum > 0) sum + 256 else 0; |
| 213 | return cs; |
| 207 | 214 | } |
| 208 | 215 | |
| 209 | 216 | // Checks calculated chksum with value of chksum field. |
| ... | ... | @@ -211,8 +218,9 @@ pub const Header = struct { |
| 211 | 218 | // Zero value indicates empty block. |
| 212 | 219 | pub fn checkChksum(header: Header) !u64 { |
| 213 | 220 | 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; |
| 216 | 224 | return field; |
| 217 | 225 | } |
| 218 | 226 | }; |
| ... | ... | @@ -387,11 +395,25 @@ fn Iterator(comptime ReaderType: type) type { |
| 387 | 395 | .file_name = try d.allocator.dupe(u8, header.name()), |
| 388 | 396 | .file_type = kind, |
| 389 | 397 | } }); |
| 398 | if (kind == .gnu_sparse) { |
| 399 | try self.skipGnuSparseExtendedHeaders(header); |
| 400 | } |
| 401 | self.reader.skipBytes(size, .{}) catch return error.TarHeadersTooBig; |
| 390 | 402 | }, |
| 391 | 403 | } |
| 392 | 404 | } |
| 393 | 405 | return null; |
| 394 | 406 | } |
| 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 | } |
| 395 | 417 | }; |
| 396 | 418 | } |
| 397 | 419 | |