| ... | @@ -198,6 +198,16 @@ fn nullStr(str: []const u8) []const u8 { | ... | @@ -198,6 +198,16 @@ fn nullStr(str: []const u8) []const u8 { |
| 198 | return str; | 198 | return str; |
| 199 | } | 199 | } |
| 200 | | 200 | |
| | 201 | // File size rounded to te block boundary. |
| | 202 | inline fn roundedFileSize(file_size: usize) usize { |
| | 203 | return std.mem.alignForward(usize, file_size, BLOCK_SIZE); |
| | 204 | } |
| | 205 | |
| | 206 | // Number of padding bytes in the last file block. |
| | 207 | inline fn filePadding(file_size: usize) usize { |
| | 208 | return roundedFileSize(file_size) - file_size; |
| | 209 | } |
| | 210 | |
| 201 | fn BufferedReader(comptime ReaderType: type) type { | 211 | fn BufferedReader(comptime ReaderType: type) type { |
| 202 | return struct { | 212 | return struct { |
| 203 | unbuffered_reader: ReaderType, | 213 | unbuffered_reader: ReaderType, |
| ... | @@ -207,16 +217,32 @@ fn BufferedReader(comptime ReaderType: type) type { | ... | @@ -207,16 +217,32 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 207 | | 217 | |
| 208 | const Self = @This(); | 218 | const Self = @This(); |
| 209 | | 219 | |
| 210 | pub fn readChunk(self: *Self, count: usize) ![]const u8 { | 220 | fn readChunk(self: *Self, count: usize) ![]const u8 { |
| 211 | self.ensureCapacity(1024); | 221 | self.ensureCapacity(BLOCK_SIZE * 2); |
| 212 | | | |
| 213 | const ask = @min(self.buffer.len - self.end, count -| (self.end - self.start)); | 222 | const ask = @min(self.buffer.len - self.end, count -| (self.end - self.start)); |
| 214 | self.end += try self.unbuffered_reader.readAtLeast(self.buffer[self.end..], ask); | 223 | self.end += try self.unbuffered_reader.readAtLeast(self.buffer[self.end..], ask); |
| 215 | | | |
| 216 | return self.buffer[self.start..self.end]; | 224 | return self.buffer[self.start..self.end]; |
| 217 | } | 225 | } |
| 218 | | 226 | |
| 219 | pub fn readBlock(self: *Self) !?[]const u8 { | 227 | // Returns slice of size count or part of it. |
| | 228 | pub fn readSlice(self: *Self, count: usize) ![]const u8 { |
| | 229 | if (count <= self.end - self.start) { |
| | 230 | // fastpath, we have enough bytes in buffer |
| | 231 | return self.buffer[self.start .. self.start + count]; |
| | 232 | } |
| | 233 | |
| | 234 | const chunk_size = roundedFileSize(count) + BLOCK_SIZE; |
| | 235 | const temp = try self.readChunk(chunk_size); |
| | 236 | if (temp.len == 0) return error.UnexpectedEndOfStream; |
| | 237 | return temp[0..@min(count, temp.len)]; |
| | 238 | } |
| | 239 | |
| | 240 | // Returns tar header block, 512 bytes. Before reading advances buffer |
| | 241 | // for padding of the previous block, to position reader at the start of |
| | 242 | // new block. After reading advances for block size, to position reader |
| | 243 | // at the start of the file body. |
| | 244 | pub fn readBlock(self: *Self, padding: usize) !?[]const u8 { |
| | 245 | try self.skip(padding); |
| 220 | const block_bytes = try self.readChunk(BLOCK_SIZE * 2); | 246 | const block_bytes = try self.readChunk(BLOCK_SIZE * 2); |
| 221 | switch (block_bytes.len) { | 247 | switch (block_bytes.len) { |
| 222 | 0 => return null, | 248 | 0 => return null, |
| ... | @@ -227,11 +253,19 @@ fn BufferedReader(comptime ReaderType: type) type { | ... | @@ -227,11 +253,19 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 227 | return block_bytes[0..BLOCK_SIZE]; | 253 | return block_bytes[0..BLOCK_SIZE]; |
| 228 | } | 254 | } |
| 229 | | 255 | |
| | 256 | // Retruns byte at current position in buffer. |
| | 257 | pub fn readByte(self: *@This()) u8 { |
| | 258 | return self.buffer[self.start]; |
| | 259 | } |
| | 260 | |
| | 261 | // Advances reader for count bytes, assumes that we have that number of |
| | 262 | // bytes in buffer. |
| 230 | pub fn advance(self: *Self, count: usize) void { | 263 | pub fn advance(self: *Self, count: usize) void { |
| 231 | self.start += count; | 264 | self.start += count; |
| 232 | assert(self.start <= self.end); | 265 | assert(self.start <= self.end); |
| 233 | } | 266 | } |
| 234 | | 267 | |
| | 268 | // Advances reader without assuming that count bytes are in the buffer. |
| 235 | pub fn skip(self: *Self, count: usize) !void { | 269 | pub fn skip(self: *Self, count: usize) !void { |
| 236 | if (self.start + count > self.end) { | 270 | if (self.start + count > self.end) { |
| 237 | try self.unbuffered_reader.skipBytes(self.start + count - self.end, .{}); | 271 | try self.unbuffered_reader.skipBytes(self.start + count - self.end, .{}); |
| ... | @@ -241,14 +275,6 @@ fn BufferedReader(comptime ReaderType: type) type { | ... | @@ -241,14 +275,6 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 241 | } | 275 | } |
| 242 | } | 276 | } |
| 243 | | 277 | |
| 244 | pub fn skipPadding(self: *Self, file_size: usize) !void { | | |
| 245 | return self.skip(filePadding(file_size)); | | |
| 246 | } | | |
| 247 | | | |
| 248 | pub fn skipFile(self: *Self, file_size: usize) !void { | | |
| 249 | return self.skip(roundedFileSize(file_size)); | | |
| 250 | } | | |
| 251 | | | |
| 252 | inline fn ensureCapacity(self: *Self, count: usize) void { | 278 | inline fn ensureCapacity(self: *Self, count: usize) void { |
| 253 | if (self.buffer.len - self.start < count) { | 279 | if (self.buffer.len - self.start < count) { |
| 254 | const dest_end = self.end - self.start; | 280 | const dest_end = self.end - self.start; |
| ... | @@ -258,16 +284,26 @@ fn BufferedReader(comptime ReaderType: type) type { | ... | @@ -258,16 +284,26 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 258 | } | 284 | } |
| 259 | } | 285 | } |
| 260 | | 286 | |
| 261 | pub fn write(self: *Self, writer: anytype, size: usize) !void { | 287 | // Write count bytes to the writer. |
| 262 | var rdr = self.sliceReader(size, true); | 288 | pub fn write(self: *Self, writer: anytype, count: usize) !void { |
| | 289 | if (self.read(count)) |buf| { |
| | 290 | try writer.writeAll(buf); |
| | 291 | return; |
| | 292 | } |
| | 293 | var rdr = self.sliceReader(count); |
| 263 | while (try rdr.next()) |slice| { | 294 | while (try rdr.next()) |slice| { |
| 264 | try writer.writeAll(slice); | 295 | try writer.writeAll(slice); |
| 265 | } | 296 | } |
| 266 | } | 297 | } |
| 267 | | 298 | |
| 268 | // copy dst.len bytes into dst | 299 | // Copy dst.len bytes into dst buffer. |
| 269 | pub fn copy(self: *Self, dst: []u8) ![]const u8 { | 300 | pub fn copy(self: *Self, dst: []u8) ![]const u8 { |
| 270 | var rdr = self.sliceReader(dst.len, true); | 301 | if (self.read(dst.len)) |buf| { |
| | 302 | // fastpath we already have enough bytes in buffer |
| | 303 | @memcpy(dst, buf); |
| | 304 | return dst; |
| | 305 | } |
| | 306 | var rdr = self.sliceReader(dst.len); |
| 271 | var pos: usize = 0; | 307 | var pos: usize = 0; |
| 272 | while (try rdr.next()) |slice| : (pos += slice.len) { | 308 | while (try rdr.next()) |slice| : (pos += slice.len) { |
| 273 | @memcpy(dst[pos .. pos + slice.len], slice); | 309 | @memcpy(dst[pos .. pos + slice.len], slice); |
| ... | @@ -275,91 +311,151 @@ fn BufferedReader(comptime ReaderType: type) type { | ... | @@ -275,91 +311,151 @@ fn BufferedReader(comptime ReaderType: type) type { |
| 275 | return dst; | 311 | return dst; |
| 276 | } | 312 | } |
| 277 | | 313 | |
| | 314 | // Retruns count bytes from buffer and advances for that number of |
| | 315 | // bytes. If we don't have that much bytes buffered returns null. |
| | 316 | fn read(self: *Self, count: usize) ?[]const u8 { |
| | 317 | if (count <= self.end - self.start) { |
| | 318 | const buf = self.buffer[self.start .. self.start + count]; |
| | 319 | self.advance(count); |
| | 320 | return buf; |
| | 321 | } |
| | 322 | return null; |
| | 323 | } |
| | 324 | |
| 278 | const SliceReader = struct { | 325 | const SliceReader = struct { |
| 279 | size: usize, | 326 | size: usize, |
| 280 | chunk_size: usize, | | |
| 281 | offset: usize, | 327 | offset: usize, |
| 282 | reader: *Self, | 328 | reader: *Self, |
| 283 | auto_advance: bool, | | |
| 284 | | | |
| 285 | pub fn next(self: *@This()) !?[]const u8 { | | |
| 286 | if (self.offset >= self.size) return null; | | |
| 287 | | 329 | |
| 288 | const temp = try self.reader.readChunk(self.chunk_size - self.offset); | 330 | pub fn next(self: *SliceReader) !?[]const u8 { |
| 289 | if (temp.len == 0) return error.UnexpectedEndOfStream; | 331 | const remaining_size = self.size - self.offset; |
| 290 | const slice = temp[0..@min(self.remainingSize(), temp.len)]; | 332 | if (remaining_size == 0) return null; |
| 291 | if (self.auto_advance) try self.advance(slice.len); | 333 | const slice = try self.reader.readSlice(remaining_size); |
| | 334 | self.advance(slice.len); |
| 292 | return slice; | 335 | return slice; |
| 293 | } | 336 | } |
| 294 | | 337 | |
| 295 | pub fn advance(self: *@This(), len: usize) !void { | 338 | fn advance(self: *SliceReader, len: usize) void { |
| 296 | self.offset += len; | 339 | self.offset += len; |
| 297 | try self.reader.skip(len); | 340 | self.reader.advance(len); |
| 298 | } | | |
| 299 | | | |
| 300 | pub fn byte(self: *@This()) u8 { | | |
| 301 | return self.reader.buffer[self.reader.start]; | | |
| 302 | } | | |
| 303 | | | |
| 304 | pub fn copy(self: *@This(), dst: []u8) ![]const u8 { | | |
| 305 | _ = try self.reader.copy(dst); | | |
| 306 | self.offset += dst.len; | | |
| 307 | return dst; | | |
| 308 | } | | |
| 309 | | | |
| 310 | pub fn remainingSize(self: *@This()) usize { | | |
| 311 | return self.size - self.offset; | | |
| 312 | } | 341 | } |
| 313 | }; | 342 | }; |
| 314 | | 343 | |
| 315 | pub fn sliceReader(self: *Self, size: usize, auto_advance: bool) Self.SliceReader { | 344 | pub fn sliceReader(self: *Self, size: usize) SliceReader { |
| 316 | return .{ | 345 | return .{ |
| 317 | .size = size, | 346 | .size = size, |
| 318 | .chunk_size = roundedFileSize(size) + BLOCK_SIZE, | 347 | .reader = self, |
| 319 | .offset = 0, | 348 | .offset = 0, |
| | 349 | }; |
| | 350 | } |
| | 351 | |
| | 352 | pub fn paxFileReader(self: *Self, size: usize) PaxFileReader { |
| | 353 | return .{ |
| | 354 | .size = size, |
| 320 | .reader = self, | 355 | .reader = self, |
| 321 | .auto_advance = auto_advance, | 356 | .offset = 0, |
| 322 | }; | 357 | }; |
| 323 | } | 358 | } |
| 324 | }; | | |
| 325 | } | | |
| 326 | | 359 | |
| 327 | // File size rounded to te block boundary. | 360 | const PaxFileReader = struct { |
| 328 | inline fn roundedFileSize(file_size: usize) usize { | 361 | size: usize, |
| 329 | return std.mem.alignForward(usize, file_size, BLOCK_SIZE); | 362 | offset: usize = 0, |
| 330 | } | 363 | reader: *Self, |
| 331 | | 364 | |
| 332 | // Number of padding bytes in the last file block. | 365 | const PaxKey = enum { |
| 333 | inline fn filePadding(file_size: usize) usize { | 366 | path, |
| 334 | return roundedFileSize(file_size) - file_size; | 367 | linkpath, |
| | 368 | size, |
| | 369 | }; |
| | 370 | |
| | 371 | const PaxAttribute = struct { |
| | 372 | key: PaxKey, |
| | 373 | value_len: usize, |
| | 374 | parent: *PaxFileReader, |
| | 375 | |
| | 376 | // Copies pax attribute value into destination buffer. |
| | 377 | // Must be called with destination buffer of size at least value_len. |
| | 378 | pub fn value(self: PaxAttribute, dst: []u8) ![]u8 { |
| | 379 | assert(dst.len >= self.value_len); |
| | 380 | const buf = dst[0..self.value_len]; |
| | 381 | _ = try self.parent.reader.copy(buf); |
| | 382 | self.parent.offset += buf.len; |
| | 383 | try self.parent.checkAttributeEnding(); |
| | 384 | return buf; |
| | 385 | } |
| | 386 | }; |
| | 387 | |
| | 388 | // Caller of the next has to call value in PaxAttribute, to advance |
| | 389 | // reader across value. |
| | 390 | pub fn next(self: *PaxFileReader) !?PaxAttribute { |
| | 391 | const rdr = self.reader; |
| | 392 | _ = rdr; |
| | 393 | |
| | 394 | while (true) { |
| | 395 | const remaining_size = self.size - self.offset; |
| | 396 | if (remaining_size == 0) return null; |
| | 397 | |
| | 398 | const inf = try parsePaxAttribute( |
| | 399 | try self.reader.readSlice(remaining_size), |
| | 400 | remaining_size, |
| | 401 | ); |
| | 402 | const key: PaxKey = if (inf.is("path")) |
| | 403 | .path |
| | 404 | else if (inf.is("linkpath")) |
| | 405 | .linkpath |
| | 406 | else if (inf.is("size")) |
| | 407 | .size |
| | 408 | else { |
| | 409 | try self.advance(inf.value_off + inf.value_len); |
| | 410 | try self.checkAttributeEnding(); |
| | 411 | continue; |
| | 412 | }; |
| | 413 | try self.advance(inf.value_off); // position reader at the start of the value |
| | 414 | return PaxAttribute{ .key = key, .value_len = inf.value_len, .parent = self }; |
| | 415 | } |
| | 416 | } |
| | 417 | |
| | 418 | fn checkAttributeEnding(self: *PaxFileReader) !void { |
| | 419 | if (self.reader.readByte() != '\n') return error.InvalidPaxAttribute; |
| | 420 | try self.advance(1); |
| | 421 | } |
| | 422 | |
| | 423 | fn advance(self: *PaxFileReader, len: usize) !void { |
| | 424 | self.offset += len; |
| | 425 | try self.reader.skip(len); |
| | 426 | } |
| | 427 | }; |
| | 428 | }; |
| 335 | } | 429 | } |
| 336 | | 430 | |
| 337 | fn Iterator(comptime ReaderType: type) type { | 431 | fn Iterator(comptime ReaderType: type) type { |
| 338 | const BufferedReaderType = BufferedReader(ReaderType); | 432 | const BufferedReaderType = BufferedReader(ReaderType); |
| 339 | return struct { | 433 | return struct { |
| 340 | attrs: struct { | 434 | // scratch buffer for file attributes |
| 341 | buffer: [std.fs.MAX_PATH_BYTES * 2]u8 = undefined, | 435 | scratch: struct { |
| | 436 | // size: two paths (name and link_name) and size (24 in pax attribute) |
| | 437 | buffer: [std.fs.MAX_PATH_BYTES * 2 + 24]u8 = undefined, |
| 342 | tail: usize = 0, | 438 | tail: usize = 0, |
| 343 | | 439 | |
| | 440 | // Allocate size of the buffer for some attribute. |
| 344 | fn alloc(self: *@This(), size: usize) ![]u8 { | 441 | fn alloc(self: *@This(), size: usize) ![]u8 { |
| 345 | if (size > self.len()) return error.NameTooLong; | 442 | const free_size = self.buffer.len - self.tail; |
| | 443 | if (size > free_size) return error.TarScratchBufferOverflow; |
| 346 | const head = self.tail; | 444 | const head = self.tail; |
| 347 | self.tail += size; | 445 | self.tail += size; |
| 348 | assert(self.tail <= self.buffer.len); | 446 | assert(self.tail <= self.buffer.len); |
| 349 | return self.buffer[head..self.tail]; | 447 | return self.buffer[head..self.tail]; |
| 350 | } | 448 | } |
| 351 | | 449 | |
| | 450 | // Free whole buffer. |
| 352 | fn free(self: *@This()) void { | 451 | fn free(self: *@This()) void { |
| 353 | self.tail = 0; | 452 | self.tail = 0; |
| 354 | } | 453 | } |
| 355 | | | |
| 356 | fn len(self: *@This()) usize { | | |
| 357 | return self.buffer.len - self.tail; | | |
| 358 | } | | |
| 359 | } = .{}, | 454 | } = .{}, |
| 360 | | 455 | |
| 361 | reader: BufferedReaderType, | 456 | reader: BufferedReaderType, |
| 362 | diagnostics: ?*Options.Diagnostics, | 457 | diagnostics: ?*Options.Diagnostics, |
| | 458 | padding: usize = 0, // bytes of file padding |
| 363 | | 459 | |
| 364 | const Self = @This(); | 460 | const Self = @This(); |
| 365 | | 461 | |
| ... | @@ -372,28 +468,22 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -372,28 +468,22 @@ fn Iterator(comptime ReaderType: type) type { |
| 372 | | 468 | |
| 373 | pub fn write(self: File, writer: anytype) !void { | 469 | pub fn write(self: File, writer: anytype) !void { |
| 374 | try self.reader.write(writer, self.size); | 470 | try self.reader.write(writer, self.size); |
| 375 | try self.skipPadding(); | | |
| 376 | } | 471 | } |
| 377 | | 472 | |
| 378 | pub fn skip(self: File) !void { | 473 | pub fn skip(self: File) !void { |
| 379 | try self.reader.skip(roundedFileSize(self.size)); | 474 | try self.reader.skip(self.size); |
| 380 | } | | |
| 381 | | | |
| 382 | fn skipPadding(self: File) !void { | | |
| 383 | try self.reader.skip(filePadding(self.size)); | | |
| 384 | } | 475 | } |
| 385 | | 476 | |
| 386 | fn chksum(self: File) ![16]u8 { | 477 | fn chksum(self: File) ![16]u8 { |
| 387 | var sum = [_]u8{0} ** 16; | 478 | var sum = [_]u8{0} ** 16; |
| 388 | if (self.size == 0) return sum; | 479 | if (self.size == 0) return sum; |
| 389 | | 480 | |
| 390 | var rdr = self.reader.sliceReader(self.size, true); | 481 | var rdr = self.reader.sliceReader(self.size); |
| 391 | var h = std.crypto.hash.Md5.init(.{}); | 482 | var h = std.crypto.hash.Md5.init(.{}); |
| 392 | while (try rdr.next()) |slice| { | 483 | while (try rdr.next()) |slice| { |
| 393 | h.update(slice); | 484 | h.update(slice); |
| 394 | } | 485 | } |
| 395 | h.final(&sum); | 486 | h.final(&sum); |
| 396 | try self.skipPadding(); | | |
| 397 | return sum; | 487 | return sum; |
| 398 | } | 488 | } |
| 399 | }; | 489 | }; |
| ... | @@ -406,64 +496,65 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -406,64 +496,65 @@ fn Iterator(comptime ReaderType: type) type { |
| 406 | // "normal file". | 496 | // "normal file". |
| 407 | pub fn next(self: *Self) !?File { | 497 | pub fn next(self: *Self) !?File { |
| 408 | var file: File = .{ .reader = &self.reader }; | 498 | var file: File = .{ .reader = &self.reader }; |
| 409 | self.attrs.free(); | 499 | self.scratch.free(); |
| 410 | | 500 | |
| 411 | while (try self.reader.readBlock()) |block_bytes| { | 501 | while (try self.reader.readBlock(self.padding)) |block_bytes| { |
| 412 | const block = Header{ .bytes = block_bytes[0..BLOCK_SIZE] }; | 502 | const header = Header{ .bytes = block_bytes[0..BLOCK_SIZE] }; |
| 413 | if (try block.checkChksum() == 0) return null; // zero block found | 503 | if (try header.checkChksum() == 0) return null; // zero block found |
| 414 | const file_type = block.fileType(); | 504 | |
| 415 | const file_size = try block.fileSize(); | 505 | const file_type = header.fileType(); |
| | 506 | const file_size = try header.fileSize(); |
| | 507 | self.padding = filePadding(file_size); |
| 416 | | 508 | |
| 417 | switch (file_type) { | 509 | switch (file_type) { |
| | 510 | // file types to retrun from next |
| 418 | .directory, .normal, .symbolic_link => { | 511 | .directory, .normal, .symbolic_link => { |
| 419 | if (file.size == 0) file.size = file_size; | 512 | if (file.size == 0) file.size = file_size; |
| | 513 | self.padding = filePadding(file.size); |
| | 514 | |
| 420 | if (file.name.len == 0) | 515 | if (file.name.len == 0) |
| 421 | file.name = try block.fullFileName((try self.attrs.alloc(std.fs.MAX_PATH_BYTES))[0..std.fs.MAX_PATH_BYTES]); | 516 | file.name = try header.fullFileName((try self.scratch.alloc(std.fs.MAX_PATH_BYTES))[0..std.fs.MAX_PATH_BYTES]); |
| 422 | if (file.link_name.len == 0) file.link_name = block.linkName(); | 517 | if (file.link_name.len == 0) file.link_name = header.linkName(); |
| 423 | file.file_type = file_type; | 518 | file.file_type = file_type; |
| 424 | return file; | 519 | return file; |
| 425 | }, | 520 | }, |
| 426 | .global_extended_header => { | 521 | // prefix header types |
| 427 | self.reader.skipFile(file_size) catch return error.TarHeadersTooBig; | 522 | .gnu_long_name => { |
| | 523 | file.name = nullStr(try self.reader.copy(try self.scratch.alloc(file_size))); |
| | 524 | }, |
| | 525 | .gnu_long_link => { |
| | 526 | file.link_name = nullStr(try self.reader.copy(try self.scratch.alloc(file_size))); |
| 428 | }, | 527 | }, |
| 429 | .extended_header => { | 528 | .extended_header => { |
| 430 | if (file_size == 0) continue; | 529 | if (file_size == 0) continue; |
| 431 | // TODO: ovo resetiranje je nezgodno | 530 | // use just last extended header data |
| 432 | self.attrs.free(); | 531 | self.scratch.free(); |
| 433 | file = File{ .reader = &self.reader }; | 532 | file = File{ .reader = &self.reader }; |
| 434 | | 533 | |
| 435 | var rdr = self.reader.sliceReader(file_size, false); | 534 | var rdr = self.reader.paxFileReader(file_size); |
| 436 | while (try rdr.next()) |slice| { | 535 | while (try rdr.next()) |attr| { |
| 437 | const attr = try parsePaxAttribute(slice, rdr.remainingSize()); | 536 | switch (attr.key) { |
| 438 | try rdr.advance(attr.value_off); | 537 | .path => { |
| 439 | if (attr.is("path")) { | 538 | file.name = try noNull(try attr.value(try self.scratch.alloc(attr.value_len))); |
| 440 | file.name = try noNull(try rdr.copy(try self.attrs.alloc(attr.value_len))); | 539 | }, |
| 441 | } else if (attr.is("linkpath")) { | 540 | .linkpath => { |
| 442 | file.link_name = try noNull(try rdr.copy(try self.attrs.alloc(attr.value_len))); | 541 | file.link_name = try noNull(try attr.value(try self.scratch.alloc(attr.value_len))); |
| 443 | } else if (attr.is("size")) { | 542 | }, |
| 444 | var buf = [_]u8{'0'} ** 32; | 543 | .size => { |
| 445 | file.size = try std.fmt.parseInt(usize, try rdr.copy(buf[0..attr.value_len]), 10); | 544 | file.size = try std.fmt.parseInt(usize, try attr.value(try self.scratch.alloc(attr.value_len)), 10); |
| 446 | } else { | 545 | }, |
| 447 | try rdr.advance(attr.value_len); | | |
| 448 | } | 546 | } |
| 449 | if (rdr.byte() != '\n') return error.InvalidPaxAttribute; | | |
| 450 | try rdr.advance(1); | | |
| 451 | } | 547 | } |
| 452 | try self.reader.skipPadding(file_size); | | |
| 453 | }, | | |
| 454 | .gnu_long_name => { | | |
| 455 | file.name = nullStr(try self.reader.copy(try self.attrs.alloc(file_size))); | | |
| 456 | try self.reader.skipPadding(file_size); | | |
| 457 | }, | 548 | }, |
| 458 | .gnu_long_link => { | 549 | // ignored header types |
| 459 | file.link_name = nullStr(try self.reader.copy(try self.attrs.alloc(file_size))); | 550 | .global_extended_header => { |
| 460 | try self.reader.skipPadding(file_size); | 551 | self.reader.skip(file_size) catch return error.TarHeadersTooBig; |
| 461 | }, | 552 | }, |
| 462 | .hard_link => return error.TarUnsupportedFileType, | 553 | // unsupported header types |
| 463 | else => { | 554 | else => { |
| 464 | const d = self.diagnostics orelse return error.TarUnsupportedFileType; | 555 | const d = self.diagnostics orelse return error.TarUnsupportedFileType; |
| 465 | try d.errors.append(d.allocator, .{ .unsupported_file_type = .{ | 556 | try d.errors.append(d.allocator, .{ .unsupported_file_type = .{ |
| 466 | .file_name = try d.allocator.dupe(u8, block.name()), | 557 | .file_name = try d.allocator.dupe(u8, header.name()), |
| 467 | .file_type = file_type, | 558 | .file_type = file_type, |
| 468 | } }); | 559 | } }); |
| 469 | }, | 560 | }, |