| Category: | Utility |
| Author/Contact Info | Dmitri Tikhonov, dtikhonov@yahoo.com |
| Description: | This filter converts tar files produced by smbclient's
built-in tar. The problem with smb's tar is that it always
creates entries in tar files using full pathnames.
For instance, if I want a tar file of //box/share/windows/desktop, no mater what I do I will get pathnames of the form ./windows/desktop instead of ./desktop. Tar Converter to the rescue! Use it like this: <nobr> smbclient //'box'/'share' -D windows -T cq - desktop 2> /dev/null | perl tar-conv '//box/share/windows/desktop' > tarball.tar </nobr> Now, for tar format, look here. This filter does not support ustar format, only the good old tar format. Try it, you might find it useful (especially if you use SAMBA). All feedback is greatly appreciated. |
#
# this program converts file names in tar files.
#
# author: Dmitri
#
# $Id: tar-conv,v 1.1 2001/03/12 11:57:02 dmitri Exp $
#
# $Log: tar-conv,v $
# Revision 1.1 2001/03/12 11:57:02 dmitri
# This is a filter that modifies pathnames in tar files produced by
# smbclient's evil tar command.
#
use integer;
use strict;
my $block;
my $blocksize = 512;
my $blknum = 0;
my $url = $ARGV[0];
$url =~ s|^/*.+?/+.+?/+||;
$url =~ s|/*[^/]+/*$||;
sub checksum {
my ($block) = @_;
my $chksum = 0;
substr $block, 148, 8, ' ';
for (split '', $block) { $chksum += ord }
$chksum = sprintf("%o%c ", $chksum, 0);
$chksum = '0'x(8 - length($chksum)) . $chksum;
substr $block, 148, 8, $chksum;
return $block;
}
sub make_short_block {
my ($retval) = @_;
my ($block, $filename, $diff);
read(STDIN, $block, $blocksize);
($filename = $block) =~ s|^\./($url/)|\./|i;
$filename =~ s/\000*$//;
$filename = $filename . sprintf("%c"x(100 - length $filename)
+, 0);
read(STDIN, $block, $blocksize);
substr $block, 0, 100, $filename;
$retval = checksum $block;
my $size = substr $retval, 124, 10;
$blknum += $size/$blocksize + 1 + ($size%$blocksize ? 1 : 0);
return $retval;
}
sub make_long_block {
my ($retval) = @_;
my ($block, $filename, $diff);
read(STDIN, $block, $blocksize);
($filename = $block) =~ s|^\./($url/)|\./|i;
$filename = $filename . sprintf("%c"x(512 - length $filename)
+, 0);
$retval = $block . $filename;
read(STDIN, $block, $blocksize);
substr $block, 0, 100, substr $filename, 0, 100;
$block = checksum $block;
my $size = substr $block, 124, 10;
$blknum += $size/$blocksize + 1 + ($size%$blocksize ? 1 : 0);
$retval = $retval . $block;
return $retval;
}
sub parse_block {
my ($block) = @_;
my ($filename, $chksum, $size, $diff, $link);
# let's see if it's a header block
++$blknum && return $block
unless
$block =~ /^(.{100}) # filename
[0-7 ]{6}.{2} # mode
[0-7 ]{6}.{2} # uid
[0-7 ]{6}.{2} # gid
([0-7]{11}). # size
[0-7]{11}. # mtime
([0-7]{6}).{2} # checksum
(.) # indicatior for links
/x;
$filename = $1;
$size = oct $2;
$chksum = oct $3;
$link = $4;
# check for long file name
if ($filename =~ m|^\./\./\@LongLink| && $link eq "L") {
if ($size - length $url > 100) {
return make_long_block $block;
} else {
return make_short_block $block;
}
}
++$blknum && return '' if $block =~ m|^\./$url/\000|i;
# calculate the next block that contains next header.
$blknum += $size/$blocksize + 1 + ($size%$blocksize ? 1 : 0);
$filename =~ s/\000+$//;
$filename =~ s|^\./($url/)|\./|i;
$diff = $1;
# pad with NUL's to 100 bytes.
$filename = $filename . sprintf("%c"x(100 - length $filename)
+, 0);
# update checksum.
for (split '', $diff) { $chksum -= ord }
$chksum = sprintf("%o", $chksum);
$chksum = '0'x(6 - length($chksum)) . $chksum;
$block =~ s/^.{100}
(
[0-7]{6}.{2}
[0-7]{6}.{2}
[0-7]{6}.{2}
[0-7]{11}.
[0-7]{11}.
)
[0-7]{6}
/$filename$1$chksum/x;
return $block;
}
for (my $i = 0; read(STDIN, $block, $blocksize); ++$i) {
if ($blknum == $i) {
# if header block, convert.
print parse_block $block;
} else {
print $block;
}
}
|
|
|
|---|