my $VERBOSE = 0; # Set to 1 to enable debugging messages
############################ CHECKSUM VERIFICATION ############################
-# A very simple later for verifying checksums. Checksums may be used on object
+# A very simple layer for verifying checksums. Checksums may be used on object
# references directly, and can also be used to verify entire reconstructed
# files.
#
# iterate_objects is a helper function used to iterate over the set of object
# references that contain the file data for a regular file.
+sub parse_int {
+ my $str = shift;
+ if ($str =~ /^0/) {
+ return oct($str);
+ } else {
+ return $str + 0;
+ }
+}
+
sub uri_decode {
my $str = shift;
$str =~ s/%([0-9a-f]{2})/chr(hex($1))/ge;
next if $obj eq "";
if ($obj =~ /^@(\S+)$/) {
my $indirect = load_ref($1);
- iterate_objects($callback, $arg, $1, $recursion_level + 1);
+ iterate_objects($callback, $arg, $indirect, $recursion_level + 1);
} else {
&$callback($arg, $obj);
}
die "File $name is missing checksum or size";
}
+ $info{size} = parse_int($info{size});
+
# Open the file to be recreated. The data will be written out by the call
# to iterate_objects.
open FILE, ">", "$DEST_DIR/$name"
my $gid = -1;
if (defined $info{user}) {
my @items = split /\s/, $info{user};
- $uid = $items[0] + 0 if exists $items[0];
+ $uid = parse_int($items[0]) if exists $items[0];
}
if (defined $info{group}) {
my @items = split /\s/, $info{group};
- $gid = $items[0] + 0 if exists $items[0];
+ $gid = parse_int($items[0]) if exists $items[0];
}
chown $uid, $gid, $dest
or warn "Unable to change ownership for $dest";
if (defined $info{mode}) {
- my $mode = $info{mode};
+ my $mode = parse_int($info{mode});
chmod $mode, $dest
or warn "Unable to change permissions for $dest";
}
# Try to parse the data as "key: value" pairs of file metadata. Also
# handle continuation lines, which start with whitespace and continue
# the previous "key: value" pair.
- if ($line =~ m/^(\w+):\s+(.*)\s*$/) {
+ if ($line =~ m/^(\w+):\s*(.*)$/) {
$info{$1} = $2;
$last_key = $1;
} elsif ($line =~/^\s/ && defined $last_key) {
$OBJECT_DIR = dirname($descriptor);
print "Source directory: $OBJECT_DIR\n" if $VERBOSE;
-# Read the snapshot descriptor to find the root object.
+# Read the snapshot descriptor to find the root object. Parse it to get a set
+# of key/value pairs.
open DESCRIPTOR, "<", $descriptor
or die "Cannot open backup descriptor file $descriptor: $!";
-my $line = <DESCRIPTOR>;
-if ($line !~ m/^Root: (\S+)$/) {
+my %descriptor = ();
+my ($line, $last_key);
+while (defined($line = <DESCRIPTOR>)) {
+ # Any lines of the form "key: value" should be inserted into the
+ # %descriptor dictionary. Any continuation line (a line starting with
+ # whitespace) will append text to the previous key's value. Ignore other
+ # lines.
+ chomp $line;
+
+ if ($line =~ m/^(\w+):\s*(.*)$/) {
+ $descriptor{$1} = $2;
+ $last_key = $1;
+ } elsif ($line =~/^\s/ && defined $last_key) {
+ $descriptor{$last_key} .= $line;
+ } else {
+ undef $last_key;
+ print STDERR "Ignoring line in backup descriptor: $line\n";
+ }
+}
+
+# A valid backup descriptor should at the very least specify the root metadata
+# object.
+if (!exists $descriptor{Root}) {
die "Expected 'Root:' specification in backup descriptor file";
}
-my $root = $1;
+my $root = $descriptor{Root};
close DESCRIPTOR;
# Set the umask to something restrictive. As we unpack files, we'll originally