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