+ close FILE;
+
+ # Verify that the reconstructed object matches the size/checksum we were
+ # given.
+ if (!verifier_check($state{VERIFIER}) || $state{BYTES} != $info{size}) {
+ die "File reconstruction failed for $name: size or checksum differs";
+ }
+}
+
+sub process_file {
+ my %info = @_;
+
+ if (!defined($info{name})) {
+ die "Filename not specified in metadata block";
+ }
+
+ my $type = $info{type};
+
+ my $filename = uri_decode($info{name});
+ print "$filename\n" if $VERBOSE;
+
+ # Restore the specified file. How to do so depends upon the file type, so
+ # dispatch based on that.
+ my $dest = "$DEST_DIR/$filename";
+ if ($type eq '-') {
+ # Regular file
+ unpack_file($filename, %info);
+ } elsif ($type eq 'd') {
+ # Directory
+ if ($filename ne '.') {
+ mkdir $dest or die "Cannot create directory $filename: $!";
+ }
+ } elsif ($type eq 'l') {
+ # Symlink
+ if (!defined($info{contents})) {
+ die "Symlink $filename has no value specified";
+ }
+ my $contents = uri_decode($info{contents});
+ symlink $contents, $dest
+ or die "Cannot create symlink $filename: $!";
+
+ # TODO: We can't properly restore all metadata for symbolic links
+ # (attempts to do so below will change metadata for the pointed-to
+ # file). This should be later fixed, but for now we simply return
+ # before getting to the restore metadata step below.
+ return;
+ } elsif ($type eq 'p' || $type eq 's' || $type eq 'c' || $type eq 'b') {
+ # Pipe, socket, character device, block device.
+ # TODO: Handle these cases.
+ print STDERR "Ignoring special file $filename of type $type\n";
+ return;
+ } else {
+ die "Unknown file type '$type' for file $filename";
+ }
+
+ # 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 $mtime = $info{mtime} || time();
+ utime time(), $mtime, $dest
+ or warn "Unable to update mtime for $dest";
+
+ my $uid = -1;
+ my $gid = -1;
+ $uid = $info{user} + 0 if defined $info{user};
+ $gid = $info{group} + 0 if defined $info{group};
+ chown $uid, $gid, $dest
+ or warn "Unable to change ownership for $dest";
+
+ if (defined $info{mode}) {
+ my $mode = $info{mode};
+ chmod $mode, $dest
+ or warn "Unable to change permissions for $dest";