Respect umask when creating backup files.
[cumulus.git] / restore.pl
index 9ba3a06..309fb73 100755 (executable)
@@ -25,8 +25,10 @@ my $OBJECT_DIR;                 # Where are the unpacked objects available?
 my $DEST_DIR = ".";             # Where should restored files should be placed?
 my $RECURSION_LIMIT = 3;        # Bound on recursive object references
 
+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.
 #
@@ -69,7 +71,7 @@ sub verifier_check {
     my $digester = $verifier->{DIGESTER};
 
     my $newhash = $digester->hexdigest();
-    if ($verifier->{HASH} ne $newhash) {
+    if ($VERBOSE && $verifier->{HASH} ne $newhash) {
         print STDERR "Verification failure: ",
             $newhash, " != ", $verifier->{HASH}, "\n";
     }
@@ -164,7 +166,7 @@ sub iterate_objects {
         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);
         }
@@ -225,7 +227,7 @@ sub process_file {
     my $type = $info{type};
 
     my $filename = uri_decode($info{name});
-    print "process_file: $filename\n";
+    print "$filename\n" if $VERBOSE;
 
     # Restore the specified file.  How to do so depends upon the file type, so
     # dispatch based on that.
@@ -264,13 +266,20 @@ sub process_file {
     # Restore mode, ownership, and any other metadata for the file.  This is
     # split out from the code above since the code is the same regardless of
     # file type.
-    my $atime = $info{atime} || time();
     my $mtime = $info{mtime} || time();
-    utime $atime, $mtime, $dest
-        or warn "Unable to update atime/mtime for $dest";
-
-    my $uid = $info{user} || -1;
-    my $gid = $info{group} || -1;
+    utime time(), $mtime, $dest
+        or warn "Unable to update mtime for $dest";
+
+    my $uid = -1;
+    my $gid = -1;
+    if (defined $info{user}) {
+        my @items = split /\s/, $info{user};
+        $uid = $items[0] + 0 if exists $items[0];
+    }
+    if (defined $info{group}) {
+        my @items = split /\s/, $info{group};
+        $gid = $items[0] + 0 if exists $items[0];
+    }
     chown $uid, $gid, $dest
         or warn "Unable to change ownership for $dest";
 
@@ -319,7 +328,7 @@ sub process_metadata {
 
         # Recursively handle indirect metadata blocks.
         if ($line =~ m/^@(\S+)$/) {
-            print "Indirect: $1\n";
+            print "Indirect: $1\n" if $VERBOSE;
             my $indirect = load_ref($1);
             process_metadata($indirect, $recursion_level + 1);
             next;
@@ -328,7 +337,7 @@ sub process_metadata {
         # 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) {
@@ -363,16 +372,38 @@ if (defined($ARGV[1])) {
 }
 
 $OBJECT_DIR = dirname($descriptor);
-print "Source directory: $OBJECT_DIR\n";
+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+)$/) {
-    die "Expected 'root:' specification in backup descriptor file";
+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
@@ -382,6 +413,6 @@ close DESCRIPTOR;
 umask 077;
 
 # Start processing metadata stored in the root to recreate the files.
-print "Root object: $root\n";
+print "Root object: $root\n" if $VERBOSE;
 my $contents = load_ref($root);
 process_metadata($contents);