File Coverage

blib/lib/Archive/Tar/Stream.pm
Criterion Covered Total %
statement 27 247 10.9
branch 0 84 0.0
condition 0 20 0.0
subroutine 9 30 30.0
pod 21 21 100.0
total 57 402 14.1


line stmt bran cond sub pod time code
1             package Archive::Tar::Stream;
2              
3 2     2   415043 use strict;
  2         5  
  2         86  
4 2     2   11 use warnings;
  2         5  
  2         159  
5              
6             # this is pretty fixed by the format!
7 2     2   16 use constant BLOCKSIZE => 512;
  2         5  
  2         180  
8              
9 2     2   18 use constant BLOCKCOUNT => 2048;
  2         4  
  2         171  
10 2     2   13 use constant BUFSIZE => (512*2048);
  2         4  
  2         115  
11              
12             # dependencies
13 2     2   1220 use IO::File;
  2         23811  
  2         318  
14 2     2   16 use IO::Handle;
  2         4  
  2         82  
15 2     2   2157 use File::Temp;
  2         35568  
  2         311  
16 2     2   20 use List::Util qw(min);
  2         6  
  2         7811  
17              
18             # XXX - make this an OO attribute
19             our $VERBOSE = 0;
20              
21             =head1 NAME
22              
23             Archive::Tar::Stream - pure perl IO-friendly tar file management
24              
25             =head1 VERSION
26              
27             Version 0.05
28              
29             =cut
30              
31             our $VERSION = '0.05';
32              
33              
34             =head1 SYNOPSIS
35              
36             Archive::Tar::Stream grew from a requirement to process very large
37             archives containing email backups, where the IO hit for unpacking
38             a tar file, repacking parts of it, and then unlinking all the files
39             was prohibitive.
40              
41             Archive::Tar::Stream takes two file handles, one purely for reads,
42             one purely for writes. It does no seeking, it just unpacks
43             individual records from the input filehandle, and packs records
44             to the output filehandle.
45              
46             This module does not attempt to do any file handle management or
47             compression for you. External zcat and gzip are quite fast and
48             use separate cores.
49              
50             use Archive::Tar::Stream;
51              
52             my $ts = Archive::Tar::Stream->new(outfh => $fh);
53             $ts->AddFile($name, -s $fh, $fh);
54              
55             # remove large non-jpeg files from a tar.gz
56             my $infh = IO::File->new("zcat $infile |") || die "oops";
57             my $outfh = IO::File->new("| gzip > $outfile") || die "double oops";
58             my $ts = Archive::Tar::Stream->new(infh => $infh, outfh => $outfh);
59             $ts->StreamCopy(sub {
60             my ($header, $outpos, $fh) = @_;
61              
62             # we want all small files
63             return 'KEEP' if $header->{size} < 64 * 1024;
64             # and any other jpegs
65             return 'KEEP' if $header->{name} =~ m/\.jpg$/i;
66              
67             # no, seriously
68             return 'EDIT' unless $fh;
69              
70             return 'KEEP' if mimetype_of_filehandle($fh) eq 'image/jpeg';
71              
72             # ok, we don't want other big files
73             return 'SKIP';
74             });
75              
76              
77             =head1 SUBROUTINES/METHODS
78              
79             =head2 new
80              
81             my $ts = Archive::Tar::Stream->new(%args);
82              
83             Args:
84             infh - filehandle to read from
85             outfh - filehandle to write to
86             inpos - initial offset in infh
87             outpos - initial offset in outfh
88             safe_copy - boolean.
89              
90             Offsets are for informational purposes only, but can be
91             useful if you are tracking offsets of items within your
92             tar files separately. All read and write functions
93             update these offsets. If you don't provide offsets, they
94             will default to zero.
95              
96             Safe Copy is the default - you have to explicitly turn it
97             off. If Safe Copy is set, every file is first extracted
98             from the input filehandle and stored in a temporary file
99             before appending to the output filehandle. This uses
100             slightly more IO, but guarantees that a truncated input
101             file will not corrupt the output file.
102              
103             =cut
104              
105             sub new {
106 0     0 1   my $class = shift;
107 0           my %args = @_;
108 0   0       my $Self = bless {
109             # defaults
110             safe_copy => 1,
111             inpos => 0,
112             outpos => 0,
113             %args
114             }, ref($class) || $class;
115 0           return $Self;
116             }
117              
118             =head2 SafeCopy
119              
120             $ts->SafeCopy(0);
121              
122             Toggle the "safe_copy" field mentioned above.
123              
124             =cut
125              
126             sub SafeCopy {
127 0     0 1   my $Self = shift;
128 0 0         if (@_) {
129 0           $Self->{safe_copy} = shift;
130             }
131 0           return $Self->{safe_copy};
132             }
133              
134             =head2 InPos
135              
136             =head2 OutPos
137              
138             Read only accessors for the internal position trackers for
139             the two tar streams.
140              
141             =cut
142              
143             sub InPos {
144 0     0 1   my $Self = shift;
145 0           return $Self->{inpos};
146             }
147              
148             sub OutPos {
149 0     0 1   my $Self = shift;
150 0           return $Self->{outpos};
151             }
152              
153             =head2 AddFile
154              
155             Adds a file to the output filehandle, adding sensible
156             defaults for all the extra header fields.
157              
158             Requires: outfh
159              
160             my $header = $ts->AddFile($name, $size, $fh, %extra);
161              
162             See TARHEADER for documentation of the header fields.
163              
164             You must provide 'size' due to the non-seeking nature of
165             this library, but "-s $fh" is usually fine.
166              
167             Returns the complete header that was written.
168              
169             =cut
170              
171             sub AddFile {
172 0     0 1   my $Self = shift;
173 0           my $name = shift;
174 0           my $size = shift;
175 0           my $fh = shift;
176              
177 0           my $header = $Self->BlankHeader(@_, name => $name, size => $size);
178              
179 0 0         return $size ? $Self->WriteFromFh($fh, $header) : $Self->WriteHeader($header);
180             }
181              
182             =head2 AddLink
183              
184             my $header = $ts->AddLink($name, $linkname, %extra);
185              
186             Adds a symlink to the output filehandle.
187              
188             See TARHEADER for documentation of the header fields.
189              
190             Returns the complete header that was written.
191              
192             =cut
193              
194             sub AddLink {
195 0     0 1   my $Self = shift;
196 0           my $name = shift;
197 0           my $linkname = shift;
198              
199 0           my $header = $Self->BlankHeader(typeflag => 2, @_, name => $name, linkname => $linkname);
200              
201 0           return $Self->WriteHeader($header);
202             }
203              
204             =head2 StreamCopy
205              
206             Streams all records from the input filehandle and provides
207             an easy way to write them to the output filehandle.
208              
209             Requires: infh
210             Optional: outfh - required if you return 'KEEP'
211              
212             $ts->StreamCopy(sub {
213             my ($header, $outpos, $fh) = @_;
214             # ...
215             return 'KEEP';
216             });
217              
218             The chooser function can either return a single 'action' or
219             a tuple of action and a new header.
220              
221             The action can be:
222             KEEP - copy this file as is (possibly changed header) to output tar
223             EDIT - re-call $Chooser with filehandle
224             SKIP - skip over the file and call $Chooser on the next one
225             EXIT - skip and also stop further processing
226              
227             EDIT mode:
228              
229             the file will be copied to a temporary file and the filehandle passed to
230             $Chooser. It can truncate, rewrite, edit - whatever. So long as it updates
231             $header->{size} and returns it as $newheader it's all good.
232              
233             you don't have to change the file of course, it's also good just as a way to
234             view the contents of some files as you stream them.
235              
236             A standard usage pattern looks like this:
237              
238             $ts->StreamCopy(sub {
239             my ($header, $outpos, $fs) = @_;
240              
241             # simple checks
242             return 'KEEP' if do_want($header);
243             return 'SKIP' if dont_want($header);
244              
245             return 'EDIT' unless $fh;
246              
247             # checks that require a filehandle
248             });
249              
250             =cut
251              
252             sub StreamCopy {
253 0     0 1   my $Self = shift;
254 0           my $Chooser = shift;
255              
256 0           while (my $header = $Self->ReadHeader()) {
257 0           my $pos = $header->{_pos};
258 0 0         if ($Chooser) {
259 0           my ($rc, $newheader) = $Chooser->($header, $Self->{outpos}, undef);
260              
261 0           my $TempFile;
262             my $Edited;
263              
264             # positive code means read the file
265 0 0         if ($rc eq 'EDIT') {
266 0           $Edited = 1;
267 0           $TempFile = $Self->CopyToTempFile($header->{size});
268             # call chooser again with the contents
269 0   0       ($rc, $newheader) = $Chooser->($newheader || $header, $Self->{outpos}, $TempFile);
270 0           seek($TempFile, 0, 0);
271             }
272              
273             # short circuit exit code
274 0 0         return if $rc eq 'EXIT';
275              
276             # NOTE: even the size could have been changed if it's an edit!
277 0 0         $header = $newheader if $newheader;
278              
279 0 0         if ($rc eq 'KEEP') {
    0          
280 0 0         print "KEEP $header->{name} $pos/$Self->{outpos}\n" if $VERBOSE;
281 0 0 0       if ($TempFile) {
    0          
282 0           $Self->WriteFromFh($TempFile, $header);
283             }
284             # guarantee safety by getting everything into a temporary file first
285             elsif ($Self->{safe_copy} and $header->{size}) {
286 0           $TempFile = $Self->CopyToTempFile($header->{size});
287 0           $Self->WriteFromFh($TempFile, $header);
288             }
289             else {
290 0           $Self->WriteCopy($header);
291             }
292             }
293              
294             # anything else means discard it
295             elsif ($rc eq 'SKIP') {
296 0 0         if ($TempFile) {
297 0 0         print "LATE REJECT $header->{name} $pos/$Self->{outpos}\n" if $VERBOSE;
298             # $TempFile already contains the bytes
299             }
300             else {
301 0 0         print "DISCARD $header->{name} $pos/$Self->{outpos}\n" if $VERBOSE;
302 0           $Self->DumpBytes($header->{size});
303             }
304             }
305              
306             else {
307 0           die "Bogus response $rc from callback\n";
308             }
309             }
310             else {
311 0 0         print "PASSTHROUGH $header->{name} $Self->{outpos}\n" if $VERBOSE;
312             # XXX - faster but less safe
313             #$Self->WriteCopy($header);
314              
315             # slow safe option :)
316 0           my $TempFile = $Self->CopyToTempFile($header->{size});
317 0           $Self->WriteFromFh($TempFile, $header);
318             }
319             }
320             }
321              
322             =head2 ReadBlocks
323              
324             Requires: infh
325              
326             my $raw = $ts->ReadBlocks($nblocks);
327              
328             Reads 'n' blocks of 512 bytes from the input filehandle
329             and returns them as single scalar.
330              
331             Returns undef at EOF on the input filehandle. Any further
332             calls after undef is returned will die. This is to avoid
333             naive programmers creating infinite loops.
334              
335             nblocks is optional, and defaults to 1.
336              
337             =cut
338              
339             sub ReadBlocks {
340 0     0 1   my $Self = shift;
341 0   0       my $nblocks = shift || 1;
342 0 0         unless ($Self->{infh}) {
343 0           die "Attempt to read without input filehandle\n";
344             }
345 0           my $bytes = BLOCKSIZE * $nblocks;
346 0           my $buf = '';
347 0           my $pos = 0;
348 0           while ($pos < $bytes) {
349 0           my $chunk = min($bytes - $pos, BUFSIZE);
350 0           my $n = sysread($Self->{infh}, $buf, $chunk, $pos);
351 0 0         unless ($n) {
352 0           delete $Self->{infh};
353 0 0         return unless $pos; # nothing at EOF is fine
354 0           die "Failed to read full block at $Self->{inpos}\n";
355             }
356 0           $pos += $n;
357 0           $Self->{inpos} += $n;
358             }
359 0           return $buf;
360             }
361              
362             =head2 WriteBlocks
363              
364             Requires: outfh
365              
366             my $pos = $ts->WriteBlocks($buffer, $nblocks);
367              
368             Write blocks to the output filehandle. If the buffer is too
369             short, it will be padded with zero bytes. If it's too long,
370             it will be truncated.
371              
372             nblocks is optional, and defaults to 1.
373              
374             Returns the position of the header in the output stream.
375              
376             =cut
377              
378             sub WriteBlocks {
379 0     0 1   my $Self = shift;
380 0           my $buf = shift;
381 0   0       my $nblocks = shift || 1;
382              
383 0           my $bytes = BLOCKSIZE * $nblocks;
384              
385 0 0         unless ($Self->{outfh}) {
386 0           die "Attempt to write without output filehandle\n";
387             }
388 0           my $pos = $Self->{outpos};
389              
390             # make sure we've got $nblocks times BLOCKSIZE bytes to write
391 0 0         if (length($buf) < $bytes) {
392 0           $buf .= "\0" x ($bytes - length($buf));
393             }
394              
395 0           my $bufpos = 0;
396 0           while ($bufpos < $bytes) {
397 0           my $n = syswrite($Self->{outfh}, $buf, $bytes - $bufpos, $bufpos);
398 0 0         unless ($n) {
399 0           delete $Self->{outfh};
400 0           die "Failed to write full block at $Self->{outpos}\n";
401             }
402 0           $bufpos += $n;
403 0           $Self->{outpos} += $n;
404             }
405              
406 0           return $pos;
407             }
408              
409             =head2 ReadHeader
410              
411             Requires: infh
412              
413             my $header = $ts->ReadHeader(%Opts);
414              
415             Read a single 512 byte header off the input filehandle and
416             convert it to a TARHEADER format hashref. Returns undef
417             at the end of the file.
418              
419             If the option (SkipInvalid => 1) is passed, it will skip
420             over blocks which fail to pass the checksum test.
421              
422             =cut
423              
424             sub ReadHeader {
425 0     0 1   my $Self = shift;
426 0           my %Opts = @_;
427              
428 0           my ($pos, $header, $skipped) = (0, undef, 0);
429              
430 0           my $initialpos = $Self->{inpos};
431 0           while (not $header) {
432 0           $pos = $Self->{inpos};
433 0           my $block = $Self->ReadBlocks();
434 0 0         last unless $block;
435 0           $header = $Self->ParseHeader($block);
436 0 0         last if $header;
437 0 0         last unless $Opts{SkipInvalid};
438 0           $skipped++;
439             }
440              
441 0 0         return unless $header;
442              
443 0 0         if ($skipped) {
444 0           warn "Skipped $skipped blocks - invalid headers at $initialpos\n";
445             }
446              
447 0           $header->{_pos} = $pos;
448 0           $Self->{last_header} = $header;
449              
450 0           return $header;
451             }
452              
453             =head2 WriteHeader
454              
455             Requires: outfh
456              
457             my $newheader = $ts->WriteHeader($header);
458              
459             Read a single 512 byte header off the input filehandle.
460              
461             If the option (SkipInvalid => 1) is passed, it will skip
462             over blocks which fail to pass the checksum test.
463              
464             Returns a copy of the header with _pos set to the position
465             in the output file.
466              
467             =cut
468              
469             sub WriteHeader {
470 0     0 1   my $Self = shift;
471 0           my $header = shift;
472              
473 0           my $block = $Self->CreateHeader($header);
474 0           my $pos = $Self->WriteBlocks($block);
475 0           return( {%$header, _pos => $pos} );
476             }
477              
478             =head2 ParseHeader
479              
480             my $header = $ts->ParseHeader($block);
481              
482             Parse a single block of raw bytes into a TARHEADER
483             format header. $block must be exactly 512 bytes.
484              
485             Returns undef if the block fails the checksum test.
486              
487             =cut
488              
489             sub ParseHeader {
490 0     0 1   my $Self = shift;
491 0           my $block = shift;
492              
493             # enforce length
494 0 0         return unless(512 == length($block));
495              
496             # skip empty blocks
497 0 0         return if substr($block, 0, 1) eq "\0";
498              
499             # unpack exactly 15 items from the block
500 0           my @items = unpack("a100a8a8a8a12a12a8a1a100a8a32a32a8a8a155", $block);
501 0 0         return unless (15 == @items);
502              
503 0           for (@items) {
504 0           s/\0.*//; # strip from first null
505             }
506              
507 0           my $chksum = oct($items[6]);
508             # do checksum
509 0           substr($block, 148, 8) = " ";
510 0 0         unless (unpack("%16C*", $block) == $chksum) {
511 0           return;
512             }
513              
514 0           my %header = (
515             name => $items[0],
516             mode => oct($items[1]),
517             uid => oct($items[2]),
518             gid => oct($items[3]),
519             size => oct($items[4]),
520             mtime => oct($items[5]),
521             # checksum
522             typeflag => $items[7],
523             linkname => $items[8],
524             # magic
525             uname => $items[10],
526             gname => $items[11],
527             devmajor => oct($items[12]),
528             devminor => oct($items[13]),
529             prefix => $items[14],
530             );
531              
532 0           return \%header;
533             }
534              
535             =head2 BlankHeader
536              
537             my $header = $ts->BlankHeader(%extra);
538              
539             Create a header with sensible defaults. That means
540             time() for mtime, 0777 for mode, etc.
541              
542             It then applies any 'extra' fields from %extra to
543             generate a final header. Also validates the keys
544             in %extra to make sure they're all known keys.
545              
546             =cut
547              
548             sub BlankHeader {
549 0     0 1   my $Self = shift;
550 0           my %hash = (
551             name => '',
552             mode => 0777,
553             uid => 0,
554             gid => 0,
555             size => 0,
556             mtime => time(),
557             typeflag => '0', # this is actually the STANDARD plain file format, phooey. Not 'f' like Tar writes
558             linkname => '',
559             uname => '',
560             gname => '',
561             devmajor => 0,
562             devminor => 0,
563             prefix => '',
564             );
565 0           my %overrides = @_;
566 0           foreach my $key (keys %overrides) {
567 0 0         if (exists $hash{$key}) {
568 0           $hash{$key} = $overrides{$key};
569             }
570             else {
571 0           warn "invalid key $key for tar header\n";
572             }
573             }
574 0           return \%hash;
575             }
576              
577             =head2 CreateHeader
578              
579             my $block = $ts->CreateHeader($header);
580              
581             Creates a 512 byte block from the TARHEADER format header.
582              
583             =cut
584              
585             sub CreateHeader {
586 0     0 1   my $Self = shift;
587 0           my $header = shift;
588              
589             my $block = pack("a100a8a8a8a12a12a8a1a100a8a32a32a8a8a155",
590             $header->{name},
591             sprintf("%07o", $header->{mode}),
592             sprintf("%07o", $header->{uid}),
593             sprintf("%07o", $header->{gid}),
594             sprintf("%011o", $header->{size}),
595             sprintf("%011o", $header->{mtime}),
596             " ", # chksum
597             $header->{typeflag},
598             $header->{linkname},
599             "ustar \0", # magic
600             $header->{uname},
601             $header->{gname},
602             sprintf("%07o", $header->{devmajor}),
603             sprintf("%07o", $header->{devminor}),
604             $header->{prefix},
605 0           );
606              
607             # calculate checksum
608 0           my $checksum = sprintf("%06o", unpack("%16C*", $block));
609 0           substr($block, 148, 8) = $checksum . "\0 ";
610              
611             # pad out to BLOCKSIZE characters
612 0 0         if (length($block) < BLOCKSIZE) {
    0          
613 0           $block .= "\0" x (BLOCKSIZE - length($block));
614             }
615             elsif (length($block) > BLOCKSIZE) {
616 0           $block = substr($block, 0, BLOCKSIZE);
617             }
618              
619 0           return $block;
620             }
621              
622             =head2 CopyBytes
623              
624             $ts->CopyBytes($bytes);
625              
626             Copies bytes from input to output filehandle, rounded up to
627             block size, so only whole blocks are actually copied.
628              
629             =cut
630              
631             sub CopyBytes {
632 0     0 1   my $Self = shift;
633 0           my $bytes = shift;
634 0           my $buf;
635 0           while ($bytes > 0) {
636 0           my $n = int($bytes / BLOCKSIZE);
637 0 0         $n = 16 if $n > 16;
638 0           my $dump = $Self->ReadBlocks($n);
639 0           $Self->WriteBlocks($dump, $n);
640 0           $bytes -= length($dump);
641             }
642             }
643              
644             =head2 DumpBytes
645              
646             $ts->DumpBytes($bytes);
647              
648             Just like CopyBytes, but it doesn't write anywhere.
649             Reads full blocks off the input filehandle, rounding
650             up to block size.
651              
652             =cut
653              
654             sub DumpBytes {
655 0     0 1   my $Self = shift;
656 0           my $bytes = shift;
657 0           while ($bytes > 0) {
658 0           my $n = min(1 + int(($bytes-1) / BLOCKSIZE), BLOCKCOUNT);
659 0           my $dump = $Self->ReadBlocks($n);
660 0           $bytes -= length($dump);
661             }
662             }
663              
664             =head2 FinishTar
665              
666             $ts->FinishTar();
667              
668             Writes 5 blocks of zero bytes to the output file, which makes
669             gnu tar happy that it's found the end of the file.
670              
671             Don't use this if you're planning on concatenating multiple
672             files together.
673              
674             =cut
675              
676             sub FinishTar {
677 0     0 1   my $Self = shift;
678             # add 5 blocks of all zero - this seems to be expected by gnutar and it will complain
679             # if they're not there
680 0           $Self->WriteBlocks("", 5);
681             }
682              
683             =head2 CopyToTempFile
684              
685             my $fh = $ts->CopyToTempFile($header->{size});
686              
687             Creates a temporary file (with File::Temp) and fills it with
688             the contents of the file on the input stream. It reads
689             entire blocks, and discards the padding.
690              
691             =cut
692              
693             sub CopyToTempFile {
694 0     0 1   my $Self = shift;
695 0           my $bytes = shift;
696              
697 0           my $TempFile = File::Temp->new();
698 0           while ($bytes > 0) {
699 0           my $n = min(1 + int(($bytes - 1) / BLOCKSIZE), BLOCKCOUNT);
700 0           my $dump = $Self->ReadBlocks($n);
701 0 0         die "Failed to read $n blocks for $bytes at $Self->{inpos}\n" unless defined $dump;
702 0 0         $dump = substr($dump, 0, $bytes) if length($dump) > $bytes;
703 0           $TempFile->print($dump);
704 0           $bytes -= length($dump);
705             }
706 0           seek($TempFile, 0, 0);
707              
708 0           return $TempFile;
709             }
710              
711             =head2 CopyFromFh
712              
713             $ts->CopyFromFh($fh, $header->{size});
714              
715             Copies the contents of the filehandle to the output stream,
716             padding out to block size.
717              
718             =cut
719              
720             sub CopyFromFh {
721 0     0 1   my $Self = shift;
722 0           my $Fh = shift;
723 0           my $bytes = shift;
724 0   0       my $buf = shift // '';
725 0   0       my $pos = shift // 0;
726              
727 0           my $tocopy = $bytes + $pos;
728              
729 0           while ($tocopy) {
730 0           my $chunk = min($tocopy - $pos, BUFSIZE);
731 0 0         if ($chunk) {
732 0           my $n = sysread($Fh, $buf, $chunk, $pos);
733 0 0         unless ($n) {
734 0           die "Failed to read $chunk bytes from input fh at at $pos\n";
735             }
736 0           $pos += $n;
737             }
738              
739             # if we're done, write including padding
740 0 0         if ($pos == $tocopy) {
741 0           my $nblocks = 1 + int(($pos-1) / BLOCKSIZE);
742 0           $Self->WriteBlocks($buf, $nblocks);
743 0           return;
744             }
745              
746             # if we have any full blocks, write them out
747 0           my $nblocks = int($pos / BLOCKSIZE);
748 0 0         if ($nblocks) {
749 0           $Self->WriteBlocks($buf, $nblocks);
750             # keep any partial blocks
751 0           my $written = $nblocks * BLOCKSIZE;
752 0           $buf = substr($buf, $written);
753 0           $pos -= $written;
754 0           $tocopy -= $written;
755             }
756             }
757              
758 0           die "Finished copying without writing everything\n";
759             }
760              
761             =head2 WriteFromFh
762              
763             $ts->WriteFromFh($fh, $header);
764              
765             Adds the header and then calls CopyFromFh with the data from the
766             filehandle
767              
768             =cut
769              
770             sub WriteFromFh {
771 0     0 1   my $Self = shift;
772 0           my $Fh = shift;
773 0           my $header = shift;
774              
775 0           my $pos = $Self->{outpos};
776              
777 0           my $block = $Self->CreateHeader($header);
778 0           $Self->CopyFromFh($Fh, $header->{size}, $block, BLOCKSIZE);
779              
780 0           return( {%$header, _pos => $pos} );
781             }
782              
783             =head2 WriteCopy
784              
785             $ts->WriteCopy($header);
786              
787             Streams the record which matches the given header directly from the input
788             stream to the output stream.
789              
790             =cut
791              
792             sub WriteCopy {
793 0     0 1   my $Self = shift;
794 0           my $header = shift;
795              
796 0           my $pos = $Self->{outpos};
797              
798 0           my $toread = $header->{size};
799              
800 0           my $blocks = $Self->CreateHeader($header);
801 0           my $count = 1;
802              
803 0   0       while ($count || $toread > 0) {
804 0 0         if ($toread) {
805 0           my $n = min(1 + int(($toread-1) / BLOCKSIZE), BLOCKCOUNT-$count);
806 0           my $dump = $Self->ReadBlocks($n);
807 0 0         die "Failed to read $n blocks for $toread at $Self->{inpos}\n" unless defined $dump;
808 0           $blocks .= $dump;
809 0           $count += $n;
810 0           $toread -= length($dump);
811             }
812 0           $Self->WriteBlocks($blocks, $count);
813 0           $blocks = '';
814 0           $count = 0;
815             }
816              
817 0           return( {%$header, _pos => $pos} );
818             }
819              
820             =head1 TARHEADER format
821              
822             This is the "BlankHeader" output, which includes all the fields
823             in a standard tar header:
824              
825             my %hash = (
826             name => '',
827             mode => 0777,
828             uid => 0,
829             gid => 0,
830             size => 0,
831             mtime => time(),
832             typeflag => '0', # this is actually the STANDARD plain file format, phooey. Not 'f' like Tar writes
833             linkname => '',
834             uname => '',
835             gname => '',
836             devmajor => 0,
837             devminor => 0,
838             prefix => '',
839             );
840              
841             You can read more about the tar header format produced by this
842             module on wikipedia:
843             L
844             or here: L
845              
846             Type flags:
847              
848             '0' Normal file
849             (ASCII NUL) Normal file (now obsolete)
850             '1' Hard link
851             '2' Symbolic link
852             '3' Character special
853             '4' Block special
854             '5' Directory
855             '6' FIFO
856             '7' Contiguous file
857              
858             Obviously some module wrote 'f' as the type - I must have found
859             that during original testing. That's bogus though.
860              
861             =head1 AUTHOR
862              
863             Bron Gondwana, C<< >>
864              
865             =head1 BUGS
866              
867             Please report any bugs or feature requests to C
868             at rt.cpan.org>, or through the web interface at
869             L.
870             I will be notified, and then you'll automatically be notified of progress
871             on your bug as I make changes.
872              
873              
874             =head1 SUPPORT
875              
876             You can find documentation for this module with the perldoc command.
877              
878             perldoc Archive::Tar::Stream
879              
880              
881             You can also look for information at:
882              
883             =over 4
884              
885             =item * RT: CPAN's request tracker (report bugs here)
886              
887             L
888              
889             =item * AnnoCPAN: Annotated CPAN documentation
890              
891             L
892              
893             =item * CPAN Ratings
894              
895             L
896              
897             =item * Search CPAN
898              
899             L
900              
901             =back
902              
903              
904             =head1 LATEST COPY
905              
906             The latest copy of this code, including development branches,
907             can be found at
908              
909             http://github.com/brong/Archive-Tar-Stream/
910              
911              
912             =head1 LICENSE AND COPYRIGHT
913              
914             Copyright 2011 Opera Software Australia Pty Limited
915              
916             This program is free software; you can redistribute it and/or modify it
917             under the terms of either: the GNU General Public License as published
918             by the Free Software Foundation; or the Artistic License.
919              
920             See http://dev.perl.org/licenses/ for more information.
921              
922              
923             =cut
924              
925             1; # End of Archive::Tar::Stream