1# ex:ts=8 sw=4: 2# $OpenBSD: Ustar.pm,v 1.76 2014/01/13 18:42:34 espie Exp $ 3# 4# Copyright (c) 2002-2014 Marc Espie <espie@openbsd.org> 5# 6# Permission to use, copy, modify, and distribute this software for any 7# purpose with or without fee is hereby granted, provided that the above 8# copyright notice and this permission notice appear in all copies. 9# 10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 18# Handle utar archives 19 20use strict; 21use warnings; 22 23package OpenBSD::Ustar; 24 25use constant { 26 FILE => "\0", 27 FILE1 => '0', 28 HARDLINK => '1', 29 SOFTLINK => '2', 30 CHARDEVICE => '3', 31 BLOCKDEVICE => '4', 32 DIR => '5', 33 FIFO => '6', 34 CONTFILE => '7', 35 USTAR_HEADER => 'a100a8a8a8a12a12a8aa100a6a2a32a32a8a8a155a12', 36 MAXFILENAME => 100, 37 MAXLINKNAME => 100, 38 MAXPREFIX => 155, 39 MAXUSERNAME => 32, 40 MAXGROUPNAME => 32 41}; 42 43use File::Basename (); 44use OpenBSD::IdCache; 45use OpenBSD::Paths; 46 47my $uidcache = new OpenBSD::UidCache; 48my $gidcache = new OpenBSD::GidCache; 49my $unamecache = new OpenBSD::UnameCache; 50my $gnamecache = new OpenBSD::GnameCache; 51 52# This is a multiple of st_blksize everywhere.... 53my $buffsize = 2 * 1024 * 1024; 54 55sub new 56{ 57 my ($class, $fh, $state, $destdir) = @_; 58 59 $destdir = '' unless defined $destdir; 60 61 return bless { 62 fh => $fh, 63 swallow => 0, 64 state => $state, 65 key => {}, 66 destdir => $destdir} , $class; 67} 68 69sub set_description 70{ 71 my ($self, $d) = @_; 72 $self->{description} = $d; 73} 74 75sub fatal 76{ 77 my ($self, $msg, @args) = @_; 78 $self->{state}->fatal("Ustar [#1][#2]: #3", 79 $self->{description} // '?', $self->{lastname} // '?', 80 $self->{state}->f($msg, @args)); 81} 82 83sub new_object 84{ 85 my ($self, $h, $class) = @_; 86 $h->{archive} = $self; 87 $h->{destdir} = $self->{destdir}; 88 bless $h, $class; 89 return $h; 90} 91 92sub skip 93{ 94 my $self = shift; 95 my $temp; 96 97 while ($self->{swallow} > 0) { 98 my $toread = $self->{swallow}; 99 if ($toread >$buffsize) { 100 $toread = $buffsize; 101 } 102 my $actual = read($self->{fh}, $temp, $toread); 103 if (!defined $actual) { 104 $self->fatal("Error while skipping archive: #1", $!); 105 } 106 if ($actual == 0) { 107 $self->fatal("Premature end of archive in header: #1", $!); 108 } 109 $self->{swallow} -= $actual; 110 } 111} 112 113my $types = { 114 DIR , 'OpenBSD::Ustar::Dir', 115 HARDLINK , 'OpenBSD::Ustar::HardLink', 116 SOFTLINK , 'OpenBSD::Ustar::SoftLink', 117 FILE , 'OpenBSD::Ustar::File', 118 FILE1 , 'OpenBSD::Ustar::File', 119 FIFO , 'OpenBSD::Ustar::Fifo', 120 CHARDEVICE , 'OpenBSD::Ustar::CharDevice', 121 BLOCKDEVICE , 'OpenBSD::Ustar::BlockDevice', 122}; 123 124sub next 125{ 126 my $self = shift; 127 # get rid of the current object 128 $self->skip; 129 my $header; 130 my $n = read($self->{fh}, $header, 512); 131 return if (defined $n) and $n == 0; 132 $self->fatal("Error while reading header") 133 unless defined $n and $n == 512; 134 if ($header eq "\0"x512) { 135 return $self->next; 136 } 137 # decode header 138 my ($name, $mode, $uid, $gid, $size, $mtime, $chksum, $type, 139 $linkname, $magic, $version, $uname, $gname, $major, $minor, 140 $prefix, $pad) = unpack(USTAR_HEADER, $header); 141 if ($magic ne "ustar\0" || $version ne '00') { 142 $self->fatal("Not an ustar archive header"); 143 } 144 # verify checksum 145 my $value = $header; 146 substr($value, 148, 8) = " "x8; 147 my $ck2 = unpack("%C*", $value); 148 if ($ck2 != oct($chksum)) { 149 $self->fatal("Bad archive checksum"); 150 } 151 $name =~ s/\0*$//o; 152 $mode = oct($mode) & 0xfff; 153 $uname =~ s/\0*$//o; 154 $gname =~ s/\0*$//o; 155 $linkname =~ s/\0*$//o; 156 $major = oct($major); 157 $minor = oct($minor); 158 $uid = oct($uid); 159 $gid = oct($gid); 160 $uid = $uidcache->lookup($uname, $uid); 161 $gid = $gidcache->lookup($gname, $gid); 162 { 163 no warnings; # XXX perl warns if oct converts >= 2^32 values 164 $mtime = oct($mtime); 165 } 166 unless ($prefix =~ m/^\0/o) { 167 $prefix =~ s/\0*$//o; 168 $name = "$prefix/$name"; 169 } 170 171 $self->{lastname} = $name; 172 $size = oct($size); 173 my $result= { 174 name => $name, 175 mode => $mode, 176 atime => $mtime, 177 mtime => $mtime, 178 linkname=> $linkname, 179 uname => $uname, 180 uid => $uid, 181 gname => $gname, 182 gid => $gid, 183 size => $size, 184 major => $major, 185 minor => $minor, 186 }; 187 if (defined $types->{$type}) { 188 $self->new_object($result, $types->{$type}); 189 } else { 190 $self->fatal("Unsupported type #1", $type); 191 } 192 if (!$result->isFile && $result->{size} != 0) { 193 $self->fatal("Bad archive: non null size for #1 (#2)", 194 $types->{$type}, $result->{name}); 195 } 196 197 # adjust swallow 198 $self->{swallow} = $size; 199 if ($size % 512) { 200 $self->{swallow} += 512 - $size % 512; 201 } 202 $self->{cachename} = $name; 203 return $result; 204} 205 206sub split_name 207{ 208 my $name = shift; 209 my $prefix = ''; 210 211 my $l = length $name; 212 if ($l > MAXFILENAME && $l <= MAXFILENAME+MAXPREFIX+1) { 213 while (length($name) > MAXFILENAME && 214 $name =~ m/^(.*?\/)(.*)$/o) { 215 $prefix .= $1; 216 $name = $2; 217 } 218 $prefix =~ s|/$||; 219 } 220 return ($prefix, $name); 221} 222 223sub mkheader 224{ 225 my ($archive, $entry, $type) = @_; 226 my ($prefix, $name) = split_name($entry->name); 227 my $linkname = $entry->{linkname}; 228 my $size = $entry->{size}; 229 my ($major, $minor); 230 if ($entry->isDevice) { 231 $major = $entry->{major}; 232 $minor = $entry->{minor}; 233 } else { 234 $major = 0; 235 $minor = 0; 236 } 237 my ($uname, $gname); 238 if (defined $entry->{uname}) { 239 $uname = $entry->{uname}; 240 } else { 241 $uname = $entry->{uid}; 242 } 243 if (defined $entry->{gname}) { 244 $gname = $entry->{gname}; 245 } else { 246 $gname = $entry->{gid}; 247 } 248 249 if (defined $entry->{cwd}) { 250 my $cwd = $entry->{cwd}; 251 $cwd.='/' unless $cwd =~ m/\/$/o; 252 $linkname =~ s/^\Q$cwd\E//; 253 } 254 if (!defined $linkname) { 255 $linkname = ''; 256 } 257 if (length $prefix > MAXPREFIX) { 258 $archive->fatal("Prefix too long #1", $prefix); 259 } 260 if (length $name > MAXFILENAME) { 261 $archive->fatal("Name too long #1", $name); 262 } 263 if (length $linkname > MAXLINKNAME) { 264 $archive->fatal("Linkname too long #1", $linkname); 265 } 266 if (length $uname > MAXUSERNAME) { 267 $archive->fatal("Username too long #1", $uname); 268 } 269 if (length $gname > MAXGROUPNAME) { 270 $archive->fatal("Groupname too long #1", $gname); 271 } 272 my $header; 273 my $cksum = ' 'x8; 274 for (1 .. 2) { 275 $header = pack(USTAR_HEADER, 276 $name, 277 sprintf("%07o", $entry->{mode}), 278 sprintf("%07o", $entry->{uid}), 279 sprintf("%07o", $entry->{gid}), 280 sprintf("%011o", $size), 281 sprintf("%011o", $entry->{mtime}), 282 $cksum, 283 $type, 284 $linkname, 285 'ustar', '00', 286 $uname, 287 $gname, 288 sprintf("%07o", $major), 289 sprintf("%07o", $minor), 290 $prefix, "\0"); 291 $cksum = sprintf("%07o", unpack("%C*", $header)); 292 } 293 return $header; 294} 295 296sub prepare 297{ 298 my ($self, $filename, $destdir) = @_; 299 300 $destdir //= $self->{destdir}; 301 my $realname = "$destdir/$filename"; 302 303 my ($dev, $ino, $mode, $uid, $gid, $rdev, $size, $mtime) = 304 (lstat $realname)[0,1,2,4,5,6, 7,9]; 305 306 my $entry = { 307 key => "$dev/$ino", 308 name => $filename, 309 realname => $realname, 310 mode => $mode, 311 uid => $uid, 312 gid => $gid, 313 size => $size, 314 mtime => $mtime, 315 uname => $unamecache->lookup($uid), 316 gname => $gnamecache->lookup($gid), 317 major => $rdev/256, 318 minor => $rdev%256, 319 }; 320 my $k = $entry->{key}; 321 my $class = "OpenBSD::Ustar::File"; # default 322 if (defined $self->{key}{$k}) { 323 $entry->{linkname} = $self->{key}{$k}; 324 $class = "OpenBSD::Ustar::HardLink"; 325 } elsif (-l $realname) { 326 $entry->{linkname} = readlink($realname); 327 $class = "OpenBSD::Ustar::SoftLink"; 328 } elsif (-p _) { 329 $class = "OpenBSD::Ustar::Fifo"; 330 } elsif (-c _) { 331 $class = "OpenBSD::Ustar::CharDevice"; 332 } elsif (-b _) { 333 $class ="OpenBSD::Ustar::BlockDevice"; 334 } elsif (-d _) { 335 $class = "OpenBSD::Ustar::Dir"; 336 } 337 $self->new_object($entry, $class); 338 if (!$entry->isFile) { 339 $entry->{size} = 0; 340 } 341 return $entry; 342} 343 344sub pad 345{ 346 my $self = shift; 347 my $fh = $self->{fh}; 348 print $fh "\0"x1024 or $self->fatal("Error writing to archive: #1", $!); 349} 350 351sub close 352{ 353 my $self = shift; 354 if (defined $self->{padout}) { 355 $self->pad; 356 } 357 close($self->{fh}); 358} 359 360sub destdir 361{ 362 my $self = shift; 363 if (@_ > 0) { 364 $self->{destdir} = shift; 365 } else { 366 return $self->{destdir}; 367 } 368} 369 370sub fh 371{ 372 return $_[0]->{fh}; 373} 374 375package OpenBSD::Ustar::Object; 376 377sub fatal 378{ 379 my ($self, @args) = @_; 380 $self->{archive}->fatal(@args); 381} 382 383sub system 384{ 385 my ($self, @args) = @_; 386 $self->{archive}{state}->system(@args); 387} 388 389sub errsay 390{ 391 my ($self, @args) = @_; 392 $self->{archive}{state}->errsay(@args); 393} 394sub todo 395{ 396 my ($self, $toread) = @_; 397 return if $toread == 0; 398 return unless defined $self->{archive}{callback}; 399 &{$self->{archive}{callback}}($self->{size} - $toread); 400} 401 402sub name 403{ 404 my $self = shift; 405 return $self->{name}; 406} 407 408sub set_name 409{ 410 my ($self, $v) = @_; 411 $self->{name} = $v; 412} 413 414sub set_modes 415{ 416 my $self = shift; 417 chown $self->{uid}, $self->{gid}, $self->{destdir}.$self->name; 418 chmod $self->{mode}, $self->{destdir}.$self->name; 419 if (defined $self->{mtime} || defined $self->{atime}) { 420 utime $self->{atime} // time, $self->{mtime} // time, 421 $self->{destdir}.$self->name; 422 } 423} 424 425sub ensure_dir 426{ 427 my ($self, $dir) = @_; 428 return if -d $dir; 429 $self->ensure_dir(File::Basename::dirname($dir)); 430 if (mkdir($dir)) { 431 return; 432 } 433 $self->fatal("Error making directory #1: #2", $dir, $!); 434} 435 436sub make_basedir 437{ 438 my $self = shift; 439 my $dir = $self->{destdir}.File::Basename::dirname($self->name); 440 $self->ensure_dir($dir); 441} 442 443sub write 444{ 445 my $self = shift; 446 my $arc = $self->{archive}; 447 my $out = $arc->{fh}; 448 449 $arc->{padout} = 1; 450 my $header = $arc->mkheader($self, $self->type); 451 print $out $header or $self->fatal("Error writing to archive: #1", $!); 452 $self->write_contents($arc); 453 my $k = $self->{key}; 454 if (!defined $arc->{key}{$k}) { 455 $arc->{key}{$k} = $self->name; 456 } 457} 458 459sub alias 460{ 461 my ($self, $arc, $alias) = @_; 462 463 my $k = $self->{archive}.":".$self->{archive}{cachename}; 464 if (!defined $arc->{key}{$k}) { 465 $arc->{key}{$k} = $alias; 466 } 467} 468 469sub write_contents 470{ 471 # only files have anything to write 472} 473 474sub resolve_links 475{ 476 # only hard links must cheat 477} 478 479sub copy_contents 480{ 481 # only files need copying 482} 483 484sub copy 485{ 486 my ($self, $wrarc) = @_; 487 my $out = $wrarc->{fh}; 488 $self->resolve_links($wrarc); 489 $wrarc->{padout} = 1; 490 my $header = $wrarc->mkheader($self, $self->type); 491 print $out $header or $self->fatal("Error writing to archive: #1", $!); 492 493 $self->copy_contents($wrarc); 494} 495 496sub isDir() { 0 } 497sub isFile() { 0 } 498sub isDevice() { 0 } 499sub isFifo() { 0 } 500sub isLink() { 0 } 501sub isSymLink() { 0 } 502sub isHardLink() { 0 } 503 504package OpenBSD::Ustar::Dir; 505our @ISA=qw(OpenBSD::Ustar::Object); 506 507sub create 508{ 509 my $self = shift; 510 $self->ensure_dir($self->{destdir}.$self->name); 511 $self->set_modes; 512} 513 514sub isDir() { 1 } 515 516sub type() { OpenBSD::Ustar::DIR } 517 518package OpenBSD::Ustar::HardLink; 519our @ISA=qw(OpenBSD::Ustar::Object); 520 521sub create 522{ 523 my $self = shift; 524 $self->make_basedir; 525 my $linkname = $self->{linkname}; 526 if (defined $self->{cwd}) { 527 $linkname=$self->{cwd}.'/'.$linkname; 528 } 529 link $self->{destdir}.$linkname, $self->{destdir}.$self->name or 530 $self->fatal("Can't link #1#2 to #1#3: #4", 531 $self->{destdir}, $linkname, $self->name, $!); 532} 533 534sub resolve_links 535{ 536 my ($self, $arc) = @_; 537 538 my $k = $self->{archive}.":".$self->{linkname}; 539 if (defined $arc->{key}{$k}) { 540 $self->{linkname} = $arc->{key}{$k}; 541 } else { 542 print join("\n", keys(%{$arc->{key}})), "\n"; 543 $self->fatal("Can't copy link over: original for #1 NOT available", $k); 544 } 545} 546 547sub isLink() { 1 } 548sub isHardLink() { 1 } 549 550sub type() { OpenBSD::Ustar::HARDLINK } 551 552package OpenBSD::Ustar::SoftLink; 553our @ISA=qw(OpenBSD::Ustar::Object); 554 555sub create 556{ 557 my $self = shift; 558 $self->make_basedir; 559 symlink $self->{linkname}, $self->{destdir}.$self->name or 560 $self->fatal("Can't symlink #1 to #2#3: #4", 561 $self->{linkname}, $self->{destdir}, $self->name, $!); 562 require POSIX; 563 POSIX::lchown($self->{uid}, $self->{gid}, $self->{destdir}.$self->name); 564} 565 566sub isLink() { 1 } 567sub isSymLink() { 1 } 568 569sub type() { OpenBSD::Ustar::SOFTLINK } 570 571package OpenBSD::Ustar::Fifo; 572our @ISA=qw(OpenBSD::Ustar::Object); 573 574sub create 575{ 576 my $self = shift; 577 $self->make_basedir; 578 require POSIX; 579 POSIX::mkfifo($self->{destdir}.$self->name, $self->{mode}) or 580 $self->fatal("Can't create fifo #1#2: #3", $self->{destdir}, 581 $self->name, $!); 582 $self->set_modes; 583} 584 585sub isFifo() { 1 } 586sub type() { OpenBSD::Ustar::FIFO } 587 588package OpenBSD::UStar::Device; 589our @ISA=qw(OpenBSD::Ustar::Object); 590 591sub create 592{ 593 my $self = shift; 594 $self->make_basedir; 595 $self->system(OpenBSD::Paths->mknod, 596 '-m', $self->{mode}, '--', $self->{destdir}.$self->name, 597 $self->devicetype, $self->{major}, $self->{minor}); 598 $self->set_modes; 599} 600 601sub isDevice() { 1 } 602 603package OpenBSD::Ustar::BlockDevice; 604our @ISA=qw(OpenBSD::Ustar::Device); 605 606sub type() { OpenBSD::Ustar::BLOCKDEVICE } 607sub devicetype() { 'b' } 608 609package OpenBSD::Ustar::CharDevice; 610our @ISA=qw(OpenBSD::Ustar::Device); 611 612sub type() { OpenBSD::Ustar::BLOCKDEVICE } 613sub devicetype() { 'c' } 614 615package OpenBSD::CompactWriter; 616 617use constant { 618 FH => 0, 619 BS => 1, 620 ZEROES => 2, 621 UNFINISHED => 3, 622}; 623 624sub new 625{ 626 my ($class, $fname) = @_; 627 open (my $out, '>', $fname) or return; 628 my $bs = (stat $out)[11]; 629 my $zeroes; 630 if (defined $bs) { 631 $zeroes = "\x00"x$bs; 632 } 633 bless [ $out, $bs, $zeroes, 0 ], $class; 634} 635 636sub write 637{ 638 my ($self, $buffer) = @_; 639 my ($fh, $bs, $zeroes, $e) = @$self; 640START: 641 if (defined $bs) { 642 for (my $i = 0; $i + $bs <= length($buffer); $i+= $bs) { 643 if (substr($buffer, $i, $bs) eq $zeroes) { 644 my $r = syswrite($fh, $buffer, $i); 645 unless (defined $r && $r == $i) { 646 return 0; 647 } 648 $i+=$bs; 649 my $seek_forward = $bs; 650 while (substr($buffer, $i, $bs) eq $zeroes) { 651 $i += $bs; 652 $seek_forward += $bs; 653 } 654 defined(sysseek($fh, $seek_forward, 1)) 655 or return 0; 656 $buffer = substr($buffer, $i); 657 if (length $buffer == 0) { 658 $self->[UNFINISHED] = 1; 659 return 1; 660 } 661 goto START; 662 } 663 } 664 } 665 $self->[UNFINISHED] = 0; 666 my $r = syswrite($fh, $buffer); 667 if (defined $r && $r == length $buffer) { 668 return 1; 669 } else { 670 return 0; 671 } 672} 673 674sub close 675{ 676 my ($self) = @_; 677 if ($self->[UNFINISHED]) { 678 defined(sysseek($self->[FH], -1, 1)) or return 0; 679 defined(syswrite($self->[FH], "\0")) or return 0; 680 } 681 return 1; 682} 683 684package OpenBSD::Ustar::File; 685our @ISA=qw(OpenBSD::Ustar::Object); 686 687sub create 688{ 689 my $self = shift; 690 $self->make_basedir; 691 my $buffer; 692 my $out = OpenBSD::CompactWriter->new($self->{destdir}.$self->name); 693 if (!defined $out) { 694 $self->fatal("Can't write to #1#2: #3", $self->{destdir}, 695 $self->name, $!); 696 } 697 my $toread = $self->{size}; 698 if ($self->{partial}) { 699 $toread -= length($self->{partial}); 700 unless ($out->write($self->{partial})) { 701 $self->fatal("Error writing to #1#2: #3", 702 $self->{destdir}, $self->name, $!); 703 } 704 } 705 while ($toread > 0) { 706 my $maxread = $buffsize; 707 $maxread = $toread if $maxread > $toread; 708 my $actual = read($self->{archive}{fh}, $buffer, $maxread); 709 if (!defined $actual) { 710 $self->fatal("Error reading from archive: #1", $!); 711 } 712 if ($actual == 0) { 713 $self->fatal("Premature end of archive"); 714 } 715 $self->{archive}{swallow} -= $actual; 716 unless ($out->write($buffer)) { 717 $self->fatal("Error writing to #1#2: #3", 718 $self->{destdir}, $self->name, $!); 719 } 720 721 $toread -= $actual; 722 $self->todo($toread); 723 } 724 $out->close or $self->fatal("Error closing #1#2: #3", 725 $self->{destdir}, $self->name, $!); 726 $self->set_modes; 727} 728 729sub contents 730{ 731 my ($self, $lookfor) = @_; 732 my $toread = $self->{size}; 733 my $buffer; 734 my $offset = 0; 735 if ($self->{partial}) { 736 $buffer = $self->{partial}; 737 $offset = length($self->{partial}); 738 $toread -= $offset; 739 } 740 741 while ($toread != 0) { 742 my $sz = $toread; 743 if (defined $lookfor) { 744 last if (defined $buffer) and &$lookfor($buffer); 745 $sz = 1024 if $sz > 1024; 746 } 747 my $actual = read($self->{archive}{fh}, $buffer, $sz, $offset); 748 if (!defined $actual) { 749 $self->fatal("Error reading from archive: #1", $!); 750 } 751 if ($actual != $sz) { 752 $self->fatal("Error: short read from archive"); 753 } 754 $self->{archive}{swallow} -= $actual; 755 $toread -= $actual; 756 $offset += $actual; 757 } 758 759 $self->{partial} = $buffer; 760 return $buffer; 761} 762 763sub write_contents 764{ 765 my ($self, $arc) = @_; 766 my $filename = $self->{realname}; 767 my $size = $self->{size}; 768 my $out = $arc->{fh}; 769 open my $fh, "<", $filename or $self->fatal("Can't read file #1: #2", 770 $filename, $!); 771 772 my $buffer; 773 my $toread = $size; 774 while ($toread > 0) { 775 my $maxread = $buffsize; 776 $maxread = $toread if $maxread > $toread; 777 my $actual = read($fh, $buffer, $maxread); 778 if (!defined $actual) { 779 $self->fatal("Error reading from file: #1", $!); 780 } 781 if ($actual == 0) { 782 $self->fatal("Premature end of file"); 783 } 784 unless (print $out $buffer) { 785 $self->fatal("Error writing to archive: #1", $!); 786 } 787 788 $toread -= $actual; 789 $self->todo($toread); 790 } 791 if ($size % 512) { 792 print $out "\0" x (512 - $size % 512) or 793 $self->fatal("Error writing to archive: #1", $!); 794 } 795} 796 797sub copy_contents 798{ 799 my ($self, $arc) = @_; 800 my $out = $arc->{fh}; 801 my $buffer; 802 my $size = $self->{size}; 803 my $toread = $size; 804 while ($toread > 0) { 805 my $maxread = $buffsize; 806 $maxread = $toread if $maxread > $toread; 807 my $actual = read($self->{archive}{fh}, $buffer, $maxread); 808 if (!defined $actual) { 809 $self->fatal("Error reading from archive: #1", $!); 810 } 811 if ($actual == 0) { 812 $self->fatal("Premature end of archive"); 813 } 814 $self->{archive}{swallow} -= $actual; 815 unless (print $out $buffer) { 816 $self->fatal("Error writing to archive #1", $!); 817 } 818 819 $toread -= $actual; 820 } 821 if ($size % 512) { 822 print $out "\0" x (512 - $size % 512) or 823 $self->fatal("Error writing to archive: #1", $!); 824 } 825 $self->alias($arc, $self->name); 826} 827 828sub isFile() { 1 } 829 830sub type() { OpenBSD::Ustar::FILE1 } 831 8321; 833