8 use Compress
::Zlib
qw(gzopen);
9 use Compress
::Bzip2
qw(bzopen);
11 use File
::Temp
qw(tempdir);
14 use POSIX
":sys_wait_h";
15 use Time
::HiRes
qw(usleep ualarm gettimeofday tv_interval);
16 use Archive
::Zip
qw(:CONSTANTS :ERROR_CODES);
26 'application/x-tar' => [ 'tar', \
&unpack_tar
, 1],
27 #'application/x-tar' => [ 'tar', \&generic_unpack ],
28 #'application/x-tar' => [ '7z', \&generic_unpack ],
29 'application/x-compressed-tar' => [ 'tar', \
&unpack_tar
, 1],
32 'application/x-cpio' => [ 'cpio', \
&unpack_tar
, 1],
33 #'application/x-cpio' => [ '7z', \&generic_unpack ],
36 #'application/zip' => [ 'zip', \&unpack_tar, 1],
37 'application/zip' => [ '7z', \
&generic_unpack
],
40 'application/x-7z-compressed' => [ '7z', \
&generic_unpack
],
43 'application/vnd.rar' => [ '7z', \
&generic_unpack
],
46 'application/x-arj' => [ '7z', \
&generic_unpack
],
49 'application/x-rpm' => [ '7z', \
&generic_unpack
],
52 'application/vnd.debian.binary-package' => [ 'ar', \
&unpack_tar
, 1],
55 'application/vnd.ms-cab-compressed' => [ '7z', \
&generic_unpack
],
58 'application/x-lha' => [ '7z', \
&generic_unpack
],
61 'application/vnd.ms-tnef' => [ 'tnef', \
&generic_unpack
],
64 'message/rfc822' => [ 'mime', \
&unpack_mime
],
66 ## CHM, Nsis - supported by 7z, but we currently do not
68 ##'application/x-zoo' - old format - no support
69 ##'application/x-ms-dos-executable' - exe should be blocked anyways - no support
70 ## application/x-arc - old format - no support
74 'application/gzip' => [ 'guzip', \
&uncompress_file
],
75 'application/x-compress' => [ 'uncompress', \
&uncompress_file
],
76 # 'application/x-compressed-tar' => [ 'guzip', \&uncompress_file ], # unpack_tar is faster
77 'application/x-tarz' => [ 'uncompress', \
&uncompress_file
],
78 'application/x-bzip' => [ 'bunzip2', \
&uncompress_file
],
79 'application/x-bzip-compressed-tar' => [ 'bunzip2', \
&uncompress_file
],
83 ## some helper methods
86 return ( $_[0] < $_[1]) ?
$_[0] : $_[1];
90 return ( $_[0] > $_[1]) ?
$_[0] : $_[1];
93 # STDERR is redirected to STDOUT by default
94 sub helper_pipe_open
{
95 my ($fh, $inputfilename, $errorfilename, @cmd) = @_;
97 my $pid = $fh->open ('-|');
99 die "unable to fork helper process: $!" if !defined $pid;
101 return $pid if ($pid != 0); # parent process simply returns
103 $inputfilename = '/dev/null' if !$inputfilename;
105 # same algorithm as used inside SA
107 my $fd = fileno (STDIN
);
109 POSIX
::close(0) if $fd != 0;
111 if (!open (STDIN
, "<$inputfilename")) {
116 $errorfilename = '&STDOUT' if !$errorfilename;
118 $fd = fileno(STDERR
);
120 POSIX
::close(2) if $fd != 2;
122 if (!open (STDERR
, ">$errorfilename")) {
133 die; # else -w complains
136 sub helper_pipe_consume
{
137 my ($cfh, $pid, $timeout, $bufsize, $callback) = @_;
140 run_with_timeout
($timeout, sub {
145 while (($count = $cfh->sysread ($buf, $bufsize)) > 0) {
146 &$callback ($buf, $count);
148 die "pipe read failed" if ($count < 0);
151 while (my $line = <$cfh>) {
160 # send TERM first if process still exits
162 kill (15, $pid) if kill (0, $pid);
164 # read remaining data, if any
166 while (($count = $cfh->sysread ($buf, $bufsize)) > 0) {
173 close ($cfh) || ($closeerr = $!);
177 if (kill (0, $pid)) {
179 kill (9, $pid); # terminate process
180 die "child '$pid' termination problems\n";
185 die "child '$pid' close failed - $closeerr\n" if $closeerr;
187 die "child '$pid' failed: $childstat\n" if $childstat;
190 sub run_with_timeout
{
191 my ($timeout, $code, @param) = @_;
193 die "got timeout\n" if $timeout <= 0;
202 local $SIG{ALRM
} = sub { $sigcount++; die "got timeout\n"; };
203 local $SIG{PIPE
} = sub { $sigcount++; die "broken pipe\n" };
204 local $SIG{__DIE__
}; # see SA bug 4631
206 $prev_alarm = alarm ($timeout);
208 $res = &$code (@param);
210 alarm 0; # avoid race conditions
215 alarm ($prev_alarm) if defined ($prev_alarm);
217 die "unknown error" if $sigcount && !$err; # seems to happen sometimes
224 # the unpacker object constructor
227 my ($type, %param) = @_;
232 $self->{tmpdir
} = $param{tmpdir
} || tempdir
(CLEANUP
=> 1);
233 $self->{starttime
} = [gettimeofday
];
234 $self->{timeout
} = $param{timeout
} || 3600*24;
236 # maxfiles: 0 = disabled
237 $self->{maxfiles
} = defined ($param{maxfiles
}) ?
$param{maxfiles
} : 1000;
239 $param{maxrec
} = 0 if !defined ($param{maxrec
});
240 if ($param{maxrec
} < 0) {
241 $param{maxrec
} = - $param{maxrec
};
242 $self->{maxrec_soft
} = 1; # do not die when limit reached
245 $self->{maxrec
} = $param{maxrec
} || 8; # 0 = disabled
246 $self->{maxratio
} = $param{maxratio
} || 0; # 0 = disabled
248 $self->{maxquota
} = $param{quota
} || 250*1024*1024; # 250 MB
250 $self->{ctonly
} = $param{ctonly
}; # only detect contained content types
254 $self->{ratioquota
} = 0;
259 $self->{debug
} = $param{debug
} || 0;
262 $self->{filenames
} = {};
264 $self->{ufid
} = 0; # counter to create unique file names
265 $self->{udid
} = 0; # counter to create unique dir names
266 $self->{ulid
} = 0; # counter to create unique link names
276 if ($self->{debug
}) {
277 system ("find '$self->{tmpdir}'");
280 rmtree
($self->{tmpdir
});
286 rmtree
($self->{tmpdir
});
290 sub uncompress_file
{
291 my ($self, $app, $filename, $newname, $csize, $filesize) = @_;
293 my $timeout = $self->check_timeout();
295 my $maxsize = $self->{quota
} - $self->{size
};
297 if ($self->{maxratio
}) {
298 $maxsize = min2
($maxsize, $filesize * $self->{maxratio
});
301 $self->add_glob_mime_type ($newname);
310 if ($app eq 'guzip' || $app eq 'bunzip2') {
316 # bzip provides a gz compatible interface
317 if ($app eq 'bunzip2') {
318 $self->{mime
}->{'application/x-bzip'} = 1;
319 $cfh = bzopen
("$filename", 'r');
320 die "bzopen '$filename' failed" if !$cfh;
322 $self->{mime
}->{'application/gzip'} = 1;
323 $cfh = gzopen
("$filename", 'rb');
324 die "gzopen '$filename' failed" if !$cfh;
327 run_with_timeout
($timeout, sub {
330 while (($count = $cfh->gzread ($buf, 128*1024)) > 0) {
333 $ct = xdg_mime_get_mime_type_for_data
($buf, $count);
336 $self->{mime
}->{$ct} = 1;
338 if (!is_archive
($ct)) {
341 # warning: this can lead to wrong size/quota test
342 last if $self->{ctonly
};
348 $self->check_comp_ratio ($filesize, $usize);
350 $self->check_quota (1, $usize, $csize);
353 $outfd = IO
::File-
>new;
355 if (!$outfd->open ($newname, O_CREAT
|O_EXCL
|O_WRONLY
, 0640)) {
356 die "unable to create file $newname: $!";
360 if (!$outfd->print ($buf)) {
361 die "unable to write '$newname' - $!";
374 } elsif ($app eq 'uncompress') {
376 $self->{mime
}->{'application/x-compress'} = 1;
379 my @cmd = ('/bin/gunzip', '-c', $filename);
380 my $cfh = IO
::File-
>new();
381 my $pid = helper_pipe_open
($cfh, '/dev/null', '/dev/null', @cmd);
383 helper_pipe_consume
($cfh, $pid, $timeout, 128*1024, sub {
384 my ($buf, $count) = @_;
386 $ct = xdg_mime_get_mime_type_for_data
($buf, $count) if (!$usize);
390 $self->check_comp_ratio ($filesize, $usize);
392 $self->check_quota (1, $usize, $csize);
395 $outfd = IO
::File-
>new;
397 if (!$outfd->open ($newname, O_CREAT
|O_EXCL
|O_WRONLY
, 0640)) {
398 die "unable to create file $newname: $!";
402 if (!$outfd->print ($buf)) {
403 die "unable to write '$newname' - $!";
412 $outfd->close () if $outfd;
419 $self->check_quota (1, $usize, $csize, 1);
421 $self->todo_list_add ($newname, $ct, $usize);
426 # calculate real filesystem space (needed by ext3 to store files/dirs)
428 my ($size, $isdir) = @_;
430 my $bs = 4096; # ext3 block size
432 $size = max2
($size, $bs) if $isdir; # dirs needs at least one block
434 return int (($size + $bs - 1) / $bs) * $bs; # round up to block size
438 my ($self, $filename, $ct, $size) = @_;
441 $self->{mime
}->{$ct} = 1;
442 if (is_archive
($ct)) {
443 push @{$self->{todo
}}, [$filename, $ct, $size];
451 my $elapsed = int (tv_interval
($self->{starttime
}));
452 my $timeout = $self->{timeout
} - $elapsed;
454 die "got timeout\n" if $timeout <= 0;
459 sub check_comp_ratio
{
460 my ($self, $compsize, $usize) = @_;
462 return if !$compsize || !$self->{maxratio
};
464 my $ratio = $usize/$compsize;
466 die "compression ratio too large (> $self->{maxratio})"
467 if $ratio > $self->{maxratio
};
471 my ($self, $files, $size, $csize, $commit) = @_;
473 my $sizediff = $csize ?
$size - $csize : $size;
475 die "compression ratio too large (> $self->{maxratio})"
476 if $self->{maxratio
} && (($self->{size
} + $sizediff) > $self->{ratioquota
});
478 die "archive too large (> $self->{quota})"
479 if ($self->{size
} + $sizediff) > $self->{quota
};
481 die "unexpected number of files '$files'" if $files <= 0;
483 $files-- if ($csize);
485 die "too many files in archive (> $self->{maxfiles})"
486 if $self->{maxfiles
} && (($self->{files
} + $files) > $self->{maxfiles
});
489 $self->{files
} += $files;
490 $self->{size
} += $sizediff;
495 sub add_glob_mime_type
{
496 my ($self, $filename) = @_;
498 my $basename = basename
($filename);
499 $self->{filenames
}->{$basename} = 1;
501 if (my $ct = xdg_mime_get_mime_type_from_file_name
($basename)) {
502 $self->{mime
}->{$ct} = 1 if $ct ne 'application/octet-stream';
507 my ($self, $app, $filename, $tmpdir, $csize, $filesize) = @_;
512 my $timeout = $self->check_timeout();
515 run_with_timeout
($timeout, sub {
517 # Create a new MIME parser:
519 if ($self->{maxfiles
}) {
520 $max = $self->{maxfiles
} - $self->{files
};
523 my $parser = PMG
::MIMEUtils
::new_mime_parser
({
527 extract_uuencode
=> 1,
528 ignore_filename
=> 1,
532 my $entity = $parser->parse_open ($filename);
534 PMG
::MIMEUtils
::traverse_mime_parts
($entity, sub {
536 my $ct = $part->head->mime_attr('content-type');
537 $self->{mime
}->{$ct} = 1 if $ct && length($ct) < 256;
539 if (my $body = $part->bodyhandle) {
540 my $path = $body->path;
552 $self->check_quota ($files, $size, $csize, 1); # commit sizes
559 my ($self, $app, $filename, $tmpdir, $csize, $filesize) = @_;
564 my $timeout = $self->check_timeout();
568 my $zip = Archive
::Zip-
>new ();
570 Archive
::Zip
::setErrorHandler
(sub { die @_ });
572 run_with_timeout
($timeout, sub {
574 my $status = $zip->read ($filename);
575 die "unable to open zip file '$filename'" if $status != AZ_OK
;
578 foreach my $mem ($zip->members) {
582 my $cm = $mem->compressionMethod();
583 die "unsupported zip compression method '$cm'\n"
584 if !(($cm == COMPRESSION_DEFLATED
||
585 $cm == COMPRESSION_STORED
));
587 die "encrypted archive detected\n"
588 if $mem->isEncrypted();
590 my $us = $mem->uncompressedSize();
592 next if $us <= 0; # skip zero size files
594 if ($mem->isDirectory) {
595 $size += realsize
($us, 1);
597 $size += realsize
($us);
600 $self->check_comp_ratio ($filesize, $size);
602 $self->check_quota ($files, $size, $csize);
604 next if $mem->isDirectory; # skip dirs
606 my $name = basename
($mem->fileName());
607 $name =~ s
|[^A-Za-z0-9\
.]|-|g
;
608 my $newfn = sprintf "$tmpdir/Z%08d_$name", $tid++;
610 $self->add_glob_mime_type ($name);
612 my $outfd = IO
::File-
>new;
613 if (!$outfd->open ($newfn, O_CREAT
|O_EXCL
|O_WRONLY
, 0640)) {
614 die "unable to create file $newfn: $!";
621 $mem->desiredCompressionMethod (COMPRESSION_STORED
);
623 $status = $mem->rewindData();
625 die "unable to rewind zip stream" if $status != AZ_OK
;
629 while ($status == AZ_OK
) {
630 ($outRef, $status) = $mem->readChunk();
631 die "unable to read zip member"
632 if ($status != AZ_OK
&& $status != AZ_STREAM_END
);
634 my $len = length ($$outRef);
636 $ct = xdg_mime_get_mime_type_for_data
($$outRef, $len) if (!$bytes);
637 $outfd->print ($$outRef) || die "write error during zip copy";
641 last if $status == AZ_STREAM_END
;
646 $self->todo_list_add ($newfn, $ct, $bytes);
666 $self->check_quota ($files, $size, $csize, 1); # commit sizes
672 my ($self, $app, $filename, $tmpdir, $csize, $filesize) = @_;
677 my $timeout = $self->check_timeout();
679 my $a = LibArchive
::archive_read_new
();
681 die "unable to create LibArchive object" if !$a;
683 LibArchive
::archive_read_support_format_all
($a);
684 LibArchive
::archive_read_support_filter_all
($a);
687 run_with_timeout
($timeout, sub {
689 if ((my $r = LibArchive
::archive_read_open_filename
($a, $filename, 10240))) {
690 die "LibArchive error: %s", LibArchive
::archive_error_string
($a);
695 my $r = LibArchive
::archive_read_next_header
($a, $entry);
697 last if ($r == LibArchive
::ARCHIVE_EOF
);
699 if ($r != LibArchive
::ARCHIVE_OK
) {
700 die "LibArchive error: %s", LibArchive
::archive_error_string
($a);
703 my $us = LibArchive
::archive_entry_size
($entry);
704 my $mode = LibArchive
::archive_entry_mode
($entry);
707 if (POSIX
::S_ISREG
($mode)) {
708 $rs = realsize
($us);
710 $rs = POSIX
::S_ISDIR
($mode) ? realsize
($us, 1) : 256;
715 $self->check_comp_ratio ($filesize, $size);
717 $self->check_quota ($files, $size, $csize);
719 next if POSIX
::S_ISDIR
($mode);
720 next if !POSIX
::S_ISREG
($mode);
722 my $name = basename
(LibArchive
::archive_entry_pathname
($entry));
723 $name =~ s
|[^A-Za-z0-9\
.]|-|g
;
724 my $newfn = sprintf "$tmpdir/A%08d_$name", $tid++;
726 $self->add_glob_mime_type ($name);
738 while (($len = LibArchive
::archive_read_data
($a, $buf, 128*1024)) > 0) {
741 if ($ct = xdg_mime_get_mime_type_for_data
($buf, $len)) {
742 $self->{mime
}->{$ct} = 1;
744 if (!is_archive
($ct)) {
746 last if $self->{ctonly
};
753 if (!$outfd) { # create only when needed
754 $outfd = IO
::File-
>new;
756 if (!$outfd->open ($newfn, O_CREAT
|O_EXCL
|O_WRONLY
, 0640)) {
757 die "unable to create file $newfn: $!";
761 if (!$outfd->print ($buf)) {
762 die "unable to write '$newfn' - $!";
766 die ("error reading archive (encrypted)\n")
770 $self->todo_list_add ($newfn, $ct, $bytes) if $todo;
775 $outfd->close () if $outfd;
787 LibArchive
::archive_read_close
($a);
788 LibArchive
::archive_read_free
($a);
792 $self->check_quota ($files, $size, $csize, 1); # commit sizes
798 my ($self, $app, $filename, $tmpdir, $csize, $filesize) = @_;
803 my $timeout = $self->check_timeout();
806 my @restorecmd = ('/bin/false');
811 @listcmd = ('/bin/tar', '-tvf', $filename);
812 @restorecmd = ('/bin/tar', '-x', '--backup=number', "--transform='s,[^A-Za-z0-9\./],-,g'", '-o',
813 '-m', '-C', $tmpdir, '-f', $filename);
816 if ($line =~ m/^(\S)\S+\s+\S+\s+([\d,\.]+)\s+\S+/) {
817 my ($type, $bytes) = ($1, $2);
818 $bytes =~ s/[,\.]//g;
821 $bytes = realsize
($bytes, 1);
822 } elsif ($type eq '-') {
823 $bytes = realsize
($bytes);
825 $bytes = 256; # simple assumption
831 $self->check_comp_ratio ($filesize, $size);
832 $self->check_quota ($files, $size, $csize);
835 die "can't parse tar output: $line\n";
838 } elsif ($app eq '7z' || $app eq '7zsimple') {
839 # Note: set password to 'none' with '-pnone', to avoid reading from /dev/tty
840 @restorecmd = ('/usr/bin/7z', 'e', '-pnone', '-bd', '-y', '-aou', "-w$self->{tmpdir}", "-o$tmpdir", $filename);
842 @listcmd = ('/usr/bin/7z', 'l', '-slt', $filename);
844 my ($path, $folder, $bytes);
850 if ($line =~ m/^\s*\z/) {
851 if (defined ($path) && defined ($bytes)) {
852 $bytes = realsize
($bytes, $folder);
856 $self->check_comp_ratio ($filesize, $size);
857 $self->check_quota ($files, $size, $csize);
863 } elsif ($line =~ m/^Path = (.*)\z/s) {
865 } elsif ($line =~ m/^Size = (\d+)\z/s) {
867 } elsif ($line =~ m/^Folder = (\d+)\z/s) {
869 } elsif ($line =~ m/^Attributes = ([D\.][R\.][H\.][S\.][A\.])\z/s) {
870 $folder = 1 if $1 && substr ($1, 0, 1) eq 'D';
874 } elsif ($app eq 'tnef') {
875 @listcmd = ('/usr/bin/tnef', '-tv', '-f', $filename);
876 @restorecmd = ('/usr/bin/tnef', '-C', $tmpdir, '--number-backups', '-f', $filename);
882 if ($line =~ m!^\s*(\d+)\s*|\s*\d{4}/\d{1,2}/\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2}\s*|!) {
885 $bytes = realsize
($bytes);
889 $self->check_comp_ratio ($filesize, $size);
890 $self->check_quota ($files, $size, $csize);
892 die "can't parse tnef output\n";
898 die "unknown application '$app'";
903 my $cfh = IO
::File-
>new();
904 my $pid = helper_pipe_open
($cfh, '/dev/null', '/dev/null', @listcmd);
906 helper_pipe_consume
($cfh, $pid, $timeout, 0, $filter);
913 return if !$files; # empty archive
915 $self->check_quota ($files, $size, $csize, 1);
917 $timeout = $self->check_timeout();
919 my $cfh = IO
::File-
>new();
920 my $pid = helper_pipe_open
($cfh, '/dev/null', undef, @restorecmd);
921 helper_pipe_consume
($cfh, $pid, $timeout, 0, sub {
923 print "$app: $line" if $self->{debug
};
930 my ($self, $dirname, $level) = @_;
934 print "unpack dir '$dirname'\n" if $self->{debug
};
936 opendir(DIR
, $dirname) || die "can't opendir $dirname: $!";
940 while (defined ($name = readdir (DIR
))) {
941 my $path = "$dirname/$name";
942 my $st = lstat ($path);
945 die "no such file '$path' - $!";
946 } elsif (POSIX
::S_ISDIR
($st->mode)) {
947 next if ($name eq '.' || $name eq '..');
948 $self->unpack_dir ($path, $level);
949 } elsif (POSIX
::S_ISREG
($st->mode)) {
950 my $size = $st->size;
951 $self->__unpack_archive ($path, $level + 1, $size);
959 my ($self, $level) = @_;
961 my $ta = $self->{todo
};
964 foreach my $todo (@$ta) {
965 $self->__unpack_archive ($todo->[0], $level, $todo->[2], $todo->[1]);
969 sub __unpack_archive
{
970 my ($self, $filename, $level, $size, $ct) = @_;
972 $level = 0 if !$level;
974 $self->{levels
} = max2
($self->{levels
}, $level);
976 if ($self->{maxrec
} && ($level >= $self->{maxrec
})) {
977 return if $self->{maxrec_soft
};
978 die "max recursion limit reached\n";
981 die "undefined file size" if !defined ($size);
983 return if !$size; # nothing to do
986 $ct = PMG
::Utils
::magic_mime_type_for_file
($filename);
987 $self->add_glob_mime_type($filename);
991 $self->{mime
}->{$ct} = 1;
993 if (defined($decompressors->{$ct})) {
995 my ($app, $code) = @{$decompressors->{$ct}};
999 # we try to keep extension correctly
1000 my $tmp = basename
($filename);
1001 ($ct eq 'application/gzip') &&
1003 ($ct eq 'application/x-bzip') &&
1004 $tmp =~ s/\.bz2?\z//;
1005 ($ct eq 'application/x-compress') &&
1007 ($ct eq 'application/x-compressed-tar') &&
1008 $tmp =~ s/\.gz\z// || $tmp =~ s/\.tgz\z/.tar/;
1009 ($ct eq 'application/x-bzip-compressed-tar') &&
1010 $tmp =~ s/\.bz2?\z// || $tmp =~ s/\.tbz\z/.tar/;
1011 ($ct eq 'application/x-tarz') &&
1014 my $newname = sprintf "%s/DC_%08d_%s", $self->{tmpdir
}, ++$self->{ufid
}, $tmp;
1016 print "Decomp: $filename\n\t($ct) with $app to $newname\n"
1019 if (my $res = &$code($self, $app, $filename, $newname, $level ?
$size : 0, $size)) {
1020 unlink $filename if $level;
1021 $self->unpack_todo ($level + 1);
1024 } elsif (defined ($unpackers->{$ct})) {
1026 my ($app, $code, $ctdetect) = @{$unpackers->{$ct}};
1030 my $tmpdir = sprintf "%s/DIR_%08d", $self->{tmpdir
}, ++$self->{udid
};
1033 print "Unpack: $filename\n\t($ct) with $app to $tmpdir\n"
1036 if (my $res = &$code ($self, $app, $filename, $tmpdir, $level ?
$size : 0, $size)) {
1037 unlink $filename if $level;
1040 $self->unpack_todo ($level + 1);
1042 $self->unpack_dir ($tmpdir, $level);
1053 return defined($decompressors->{$ct}) || defined($unpackers->{$ct});
1058 # Description: unpacks an archive and records containing
1059 # content types (detected by magic numbers and file extension)
1060 # Extracted files are stored inside 'tempdir'.
1062 # returns: true if file is archive, undef otherwise
1064 sub unpack_archive
{
1065 my ($self, $filename, $ct) = @_;
1067 my $st = lstat($filename);
1071 die "no such file '$filename' - $!";
1072 } elsif (POSIX
::S_ISREG
($st->mode)) {
1075 return if !$size; # do nothing
1077 $self->{quota
} = $self->{maxquota
} - $self->{size
};
1079 $self->{ratioquota
} = $size * $self->{maxratio
} if $self->{maxratio
};
1082 return; # do nothing
1085 $ct = PMG
::Utils
::magic_mime_type_for_file
($filename) if !$ct;
1087 return if (!$ct || !is_archive
($ct)); # not an archive
1090 $self->__unpack_archive($filename, 0, $st->size, $ct);
1095 printf "ELAPSED: %.2f ms $filename\n",
1096 int(tv_interval
($self->{starttime
}) * 1000)
1100 $self->{mime
}->{'proxmox/unreadable-archive'} = 1;