| ... | @@ -258,10 +258,15 @@ pub fn iterator(reader: anytype, options: IteratorOptions) Iterator(@TypeOf(read | ... | @@ -258,10 +258,15 @@ pub fn iterator(reader: anytype, options: IteratorOptions) Iterator(@TypeOf(read |
| 258 | .file_name_buffer = options.file_name_buffer, | 258 | .file_name_buffer = options.file_name_buffer, |
| 259 | .link_name_buffer = options.link_name_buffer, | 259 | .link_name_buffer = options.link_name_buffer, |
| 260 | .padding = 0, | 260 | .padding = 0, |
| 261 | .file = undefined, | | |
| 262 | }; | 261 | }; |
| 263 | } | 262 | } |
| 264 | | 263 | |
| | 264 | pub const FileKind = enum { |
| | 265 | directory, |
| | 266 | sym_link, |
| | 267 | file, |
| | 268 | }; |
| | 269 | |
| 265 | fn Iterator(comptime ReaderType: type) type { | 270 | fn Iterator(comptime ReaderType: type) type { |
| 266 | return struct { | 271 | return struct { |
| 267 | reader: ReaderType, | 272 | reader: ReaderType, |
| ... | @@ -274,35 +279,43 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -274,35 +279,43 @@ fn Iterator(comptime ReaderType: type) type { |
| 274 | | 279 | |
| 275 | // bytes of padding to the end of the block | 280 | // bytes of padding to the end of the block |
| 276 | padding: usize, | 281 | padding: usize, |
| 277 | // current tar file | 282 | // not consumed bytes of file from last next iteration |
| 278 | file: File, | 283 | unread_file_bytes: usize = 0, |
| 279 | | 284 | |
| 280 | pub const File = struct { | 285 | pub const File = struct { |
| 281 | name: []const u8, // name of file, symlink or directory | 286 | name: []const u8, // name of file, symlink or directory |
| 282 | link_name: []const u8, // target name of symlink | 287 | link_name: []const u8, // target name of symlink |
| 283 | size: u64, // size of the file in bytes | 288 | size: u64 = 0, // size of the file in bytes |
| 284 | mode: u32, | 289 | mode: u32 = 0, |
| 285 | kind: Header.Kind, | 290 | kind: FileKind = .file, |
| 286 | | 291 | |
| | 292 | unread_bytes: *usize, |
| 287 | reader: ReaderType, | 293 | reader: ReaderType, |
| 288 | | 294 | |
| | 295 | pub const Reader = std.io.Reader(*Self, ReaderType.Error, read); |
| | 296 | |
| | 297 | pub fn reader(self: *Self) Reader { |
| | 298 | return .{ .context = self }; |
| | 299 | } |
| | 300 | |
| | 301 | pub fn read(self: *Self, dest: []u8) ReaderType.Error!usize { |
| | 302 | const buf = dest[0..@min(dest.len, self.unread_size.*)]; |
| | 303 | const n = try self.reader.read(buf); |
| | 304 | self.unread_size.* -= n; |
| | 305 | return n; |
| | 306 | } |
| | 307 | |
| 289 | // Writes file content to writer. | 308 | // Writes file content to writer. |
| 290 | pub fn write(self: File, writer: anytype) !void { | 309 | pub fn writeAll(self: File, writer: anytype) !void { |
| 291 | var buffer: [4096]u8 = undefined; | 310 | var buffer: [4096]u8 = undefined; |
| 292 | | 311 | |
| 293 | var n: u64 = 0; | 312 | while (self.unread_bytes.* > 0) { |
| 294 | while (n < self.size) { | 313 | const buf = buffer[0..@min(buffer.len, self.unread_bytes.*)]; |
| 295 | const buf = buffer[0..@min(buffer.len, self.size - n)]; | | |
| 296 | try self.reader.readNoEof(buf); | 314 | try self.reader.readNoEof(buf); |
| 297 | try writer.writeAll(buf); | 315 | try writer.writeAll(buf); |
| 298 | n += buf.len; | 316 | self.unread_bytes.* -= buf.len; |
| 299 | } | 317 | } |
| 300 | } | 318 | } |
| 301 | | | |
| 302 | // Skips file content. Advances reader. | | |
| 303 | pub fn skip(self: File) !void { | | |
| 304 | try self.reader.skipBytes(self.size, .{}); | | |
| 305 | } | | |
| 306 | }; | 319 | }; |
| 307 | | 320 | |
| 308 | const Self = @This(); | 321 | const Self = @This(); |
| ... | @@ -326,14 +339,12 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -326,14 +339,12 @@ fn Iterator(comptime ReaderType: type) type { |
| 326 | return nullStr(buf); | 339 | return nullStr(buf); |
| 327 | } | 340 | } |
| 328 | | 341 | |
| 329 | fn initFile(self: *Self) void { | 342 | fn newFile(self: *Self) File { |
| 330 | self.file = .{ | 343 | return .{ |
| 331 | .name = self.file_name_buffer[0..0], | 344 | .name = self.file_name_buffer[0..0], |
| 332 | .link_name = self.link_name_buffer[0..0], | 345 | .link_name = self.link_name_buffer[0..0], |
| 333 | .size = 0, | | |
| 334 | .kind = .normal, | | |
| 335 | .mode = 0, | | |
| 336 | .reader = self.reader, | 346 | .reader = self.reader, |
| | 347 | .unread_bytes = &self.unread_file_bytes, |
| 337 | }; | 348 | }; |
| 338 | } | 349 | } |
| 339 | | 350 | |
| ... | @@ -350,7 +361,12 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -350,7 +361,12 @@ fn Iterator(comptime ReaderType: type) type { |
| 350 | /// loop iterates through one or more entries until it collects a all | 361 | /// loop iterates through one or more entries until it collects a all |
| 351 | /// file attributes. | 362 | /// file attributes. |
| 352 | pub fn next(self: *Self) !?File { | 363 | pub fn next(self: *Self) !?File { |
| 353 | self.initFile(); | 364 | if (self.unread_file_bytes > 0) { |
| | 365 | // If file content was not consumed by caller |
| | 366 | try self.reader.skipBytes(self.unread_file_bytes, .{}); |
| | 367 | self.unread_file_bytes = 0; |
| | 368 | } |
| | 369 | var file: File = self.newFile(); |
| 354 | | 370 | |
| 355 | while (try self.readHeader()) |header| { | 371 | while (try self.readHeader()) |header| { |
| 356 | const kind = header.kind(); | 372 | const kind = header.kind(); |
| ... | @@ -360,46 +376,52 @@ fn Iterator(comptime ReaderType: type) type { | ... | @@ -360,46 +376,52 @@ fn Iterator(comptime ReaderType: type) type { |
| 360 | switch (kind) { | 376 | switch (kind) { |
| 361 | // File types to retrun upstream | 377 | // File types to retrun upstream |
| 362 | .directory, .normal, .symbolic_link => { | 378 | .directory, .normal, .symbolic_link => { |
| 363 | self.file.kind = kind; | 379 | file.kind = switch (kind) { |
| 364 | self.file.mode = try header.mode(); | 380 | .directory => .directory, |
| | 381 | .normal => .file, |
| | 382 | .symbolic_link => .sym_link, |
| | 383 | else => unreachable, |
| | 384 | }; |
| | 385 | file.mode = try header.mode(); |
| 365 | | 386 | |
| 366 | // set file attributes if not already set by prefix/extended headers | 387 | // set file attributes if not already set by prefix/extended headers |
| 367 | if (self.file.size == 0) { | 388 | if (file.size == 0) { |
| 368 | self.file.size = size; | 389 | file.size = size; |
| 369 | } | 390 | } |
| 370 | if (self.file.link_name.len == 0) { | 391 | if (file.link_name.len == 0) { |
| 371 | self.file.link_name = try header.linkName(self.link_name_buffer); | 392 | file.link_name = try header.linkName(self.link_name_buffer); |
| 372 | } | 393 | } |
| 373 | if (self.file.name.len == 0) { | 394 | if (file.name.len == 0) { |
| 374 | self.file.name = try header.fullName(self.file_name_buffer); | 395 | file.name = try header.fullName(self.file_name_buffer); |
| 375 | } | 396 | } |
| 376 | | 397 | |
| 377 | self.padding = blockPadding(self.file.size); | 398 | self.padding = blockPadding(file.size); |
| 378 | return self.file; | 399 | self.unread_file_bytes = file.size; |
| | 400 | return file; |
| 379 | }, | 401 | }, |
| 380 | // Prefix header types | 402 | // Prefix header types |
| 381 | .gnu_long_name => { | 403 | .gnu_long_name => { |
| 382 | self.file.name = try self.readString(@intCast(size), self.file_name_buffer); | 404 | file.name = try self.readString(@intCast(size), self.file_name_buffer); |
| 383 | }, | 405 | }, |
| 384 | .gnu_long_link => { | 406 | .gnu_long_link => { |
| 385 | self.file.link_name = try self.readString(@intCast(size), self.link_name_buffer); | 407 | file.link_name = try self.readString(@intCast(size), self.link_name_buffer); |
| 386 | }, | 408 | }, |
| 387 | .extended_header => { | 409 | .extended_header => { |
| 388 | // Use just attributes from last extended header. | 410 | // Use just attributes from last extended header. |
| 389 | self.initFile(); | 411 | file = self.newFile(); |
| 390 | | 412 | |
| 391 | var rdr = paxIterator(self.reader, @intCast(size)); | 413 | var rdr = paxIterator(self.reader, @intCast(size)); |
| 392 | while (try rdr.next()) |attr| { | 414 | while (try rdr.next()) |attr| { |
| 393 | switch (attr.kind) { | 415 | switch (attr.kind) { |
| 394 | .path => { | 416 | .path => { |
| 395 | self.file.name = try attr.value(self.file_name_buffer); | 417 | file.name = try attr.value(self.file_name_buffer); |
| 396 | }, | 418 | }, |
| 397 | .linkpath => { | 419 | .linkpath => { |
| 398 | self.file.link_name = try attr.value(self.link_name_buffer); | 420 | file.link_name = try attr.value(self.link_name_buffer); |
| 399 | }, | 421 | }, |
| 400 | .size => { | 422 | .size => { |
| 401 | var buf: [pax_max_size_attr_len]u8 = undefined; | 423 | var buf: [pax_max_size_attr_len]u8 = undefined; |
| 402 | self.file.size = try std.fmt.parseInt(u64, try attr.value(&buf), 10); | 424 | file.size = try std.fmt.parseInt(u64, try attr.value(&buf), 10); |
| 403 | }, | 425 | }, |
| 404 | } | 426 | } |
| 405 | } | 427 | } |
| ... | @@ -574,24 +596,23 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi | ... | @@ -574,24 +596,23 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi |
| 574 | try dir.makePath(file_name); | 596 | try dir.makePath(file_name); |
| 575 | } | 597 | } |
| 576 | }, | 598 | }, |
| 577 | .normal => { | 599 | .file => { |
| 578 | if (file.size == 0 and file.name.len == 0) return; | 600 | if (file.size == 0 and file.name.len == 0) return; |
| 579 | const file_name = stripComponents(file.name, options.strip_components); | 601 | const file_name = stripComponents(file.name, options.strip_components); |
| 580 | if (file_name.len == 0) return error.BadFileName; | 602 | if (file_name.len == 0) return error.BadFileName; |
| 581 | | 603 | |
| 582 | if (createDirAndFile(dir, file_name)) |fs_file| { | 604 | if (createDirAndFile(dir, file_name)) |fs_file| { |
| 583 | defer fs_file.close(); | 605 | defer fs_file.close(); |
| 584 | try file.write(fs_file); | 606 | try file.writeAll(fs_file); |
| 585 | } else |err| { | 607 | } else |err| { |
| 586 | const d = options.diagnostics orelse return err; | 608 | const d = options.diagnostics orelse return err; |
| 587 | try d.errors.append(d.allocator, .{ .unable_to_create_file = .{ | 609 | try d.errors.append(d.allocator, .{ .unable_to_create_file = .{ |
| 588 | .code = err, | 610 | .code = err, |
| 589 | .file_name = try d.allocator.dupe(u8, file_name), | 611 | .file_name = try d.allocator.dupe(u8, file_name), |
| 590 | } }); | 612 | } }); |
| 591 | try file.skip(); | | |
| 592 | } | 613 | } |
| 593 | }, | 614 | }, |
| 594 | .symbolic_link => { | 615 | .sym_link => { |
| 595 | // The file system path of the symbolic link. | 616 | // The file system path of the symbolic link. |
| 596 | const file_name = stripComponents(file.name, options.strip_components); | 617 | const file_name = stripComponents(file.name, options.strip_components); |
| 597 | if (file_name.len == 0) return error.BadFileName; | 618 | if (file_name.len == 0) return error.BadFileName; |
| ... | @@ -607,7 +628,6 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi | ... | @@ -607,7 +628,6 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi |
| 607 | } }); | 628 | } }); |
| 608 | }; | 629 | }; |
| 609 | }, | 630 | }, |
| 610 | else => unreachable, | | |
| 611 | } | 631 | } |
| 612 | } | 632 | } |
| 613 | } | 633 | } |