Changeset 153

Show
Ignore:
Timestamp:
05/23/07 21:28:38 (3 years ago)
Author:
bradfitz
Message:

brackup-restore's verbose flag is more verbose now, showing files
as they're restored.

brackup-restore can restore from an encrypted *.brackup file now,
firing up gpg for user to decrypt to a tempfile

Location:
trunk
Files:
6 modified

Legend:

Unmodified
Added
Removed
  • trunk/Changes

    r152 r153  
     1  - brackup-restore's verbose flag is more verbose now, showing files 
     2    as they're restored. 
     3 
     4  - brackup-restore can restore from an encrypted *.brackup file now, 
     5    firing up gpg for user to decrypt to a tempfile 
     6 
    17  - brackup-target tool, to list/get brackup files from a target, 
    28    and in the future do garbage collection on no-longer-referenced 
  • trunk/TODO

    r152 r153  
     1-- in Restore.pm:  
     2      # TODO: inefficient!  we don't want to download the chunk from the 
     3      # target multiple times.  better to cache it locally, or at least 
     4      # only fetch a region from the target (but that's still kinda inefficient 
     5      # and pushes complexity into the Target interface) 
     6 
    17-- only use tempfiles in a 0700 directory under /tmp/brackup-$USER/ or 
    28   wherever.  or option, when using encryption, to only doing 
    39   in-memory tempfiles, to avoid hitting disk? 
    4  
    5 -- (a future version of brackup-restore will let you grab the metafile from 
    6    the target, but that's still a TODO item) 
    710 
    811-- <lj user=grahams>: if a file which existed when Brackup was 
  • trunk/brackup-restore

    r140 r153  
    2121=item --from=NAME 
    2222 
    23 Required.  The metafile index.  Probably named like "source-YYYYMMDD.brackup" 
     23Required.  The backup metafile, describing the tree you want to 
     24restore.  Probably named like "source-YYYYMMDD.brackup".  If you lost 
     25it, it's also stored on your backup target, and you can fetch it with 
     26L<brackup-target>. 
    2427 
    2528=item --to=NAME 
     
    6568 
    6669use Brackup; 
     70use Brackup::Util qw(tempfile); 
    6771 
    6872my ($opt_verbose, $meta_file, $opt_help, $restore_dir, $opt_all, $prefix); 
     
    100104                                    prefix => $prefix, 
    101105                                    file   => $meta_file, 
     106                                    verbose => $opt_verbose, 
    102107                                    ); 
    103108 
  • trunk/lib/Brackup/Manual/Overview.pod

    r133 r153  
    4040"myhome-yyyymmdd.brackup" in your current directory.  Go look at it. 
    4141It describes the state of the tree (the "root", or "source") that you 
    42 just backed up.  B<You should keep this file>.  Although, if you 
    43 didn't, it was also stored on the target (in this case, Amazon), so 
    44 it's not critical, but at present you need the metafile (the .brackup 
    45 file), locally in able to do a restore with B<brackup-restore>.  (a 
    46 future version of brackup-restore will let you grab the metafile from 
    47 the target, but that's still a TODO item) 
     42just backed up.  You might want to keep this file.  Although, if you 
     43don't, it's also stored on the target (in this case, Amazon), so it's 
     44not critical.  (You can always re-download lost .brackup files with 
     45L<brackup-target>) 
    4846 
    4947You might also notice two SQLite files at: 
     
    119117=head1 Restores 
    120118 
    121 To do a restore, you'll currently need your *.brackup file handy 
    122 (pulling the *.brackup file from the server isn't yet supported).  Then 
    123 run: 
     119To do a restore, you'll need your *.brackup file handy.  If you lost 
     120it, you can re-download it from your backup target with 
     121L<brackup-target>.  Then run: 
    124122 
    125123   brackup-restore --from=foo.brackup --to=<dir> --all 
  • trunk/lib/Brackup/Restore.pm

    r151 r153  
    44use Carp qw(croak); 
    55use Digest::SHA1; 
    6 use Brackup::Util qw(tempfile); 
     6use Brackup::Util qw(tempfile slurp valid_params); 
    77 
    88sub new { 
     
    1010    my $self = bless {}, $class; 
    1111 
    12     $self->{to}     = delete $opts{to};      # directory we're restoring to 
    13     $self->{prefix} = delete $opts{prefix};  # directory/file filename prefix, or "" for all 
    14     $self->{file}   = delete $opts{file};    # filename we're restoring from 
     12    $self->{to}      = delete $opts{to};      # directory we're restoring to 
     13    $self->{prefix}  = delete $opts{prefix};  # directory/file filename prefix, or "" for all 
     14    $self->{file}    = delete $opts{file};    # filename we're restoring from 
     15    $self->{verbose} = delete $opts{verbose}; 
    1516 
    1617    $self->{_stats_to_run} = [];  # stack (push/pop) of subrefs to reset stat info on 
     
    1920    die "Backup file doesn't exist"           unless $self->{file} && -f $self->{file}; 
    2021    croak("Unknown options: " . join(', ', keys %opts)) if %opts; 
     22 
     23    $self->decrypt_file_if_needed; 
     24 
    2125    return $self; 
    2226} 
     
    3337} 
    3438 
     39sub decrypt_file_if_needed { 
     40    my $self = shift; 
     41    my $meta = slurp($self->{file}); 
     42    if ($meta =~ /[\x00-\x08]/) { # silly is-binary heuristic 
     43        my $new_file = $self->_decrypt_data(\$meta, 
     44                                            want_tempfile => 1, 
     45                                            no_batch => 1, 
     46                                            ); 
     47        warn "Decrypted metafile $self->{file} to $new_file; using that to restore from...\n"; 
     48        $self->{file} = $new_file; 
     49        scalar <STDIN>; 
     50    } 
     51} 
     52 
    3553sub restore { 
    3654    my ($self) = @_; 
     
    6078        $it->{Mode} ||= $meta->{DefaultDirMode}  if $type eq "d"; 
    6179 
     80        warn " * restoring $it->{Path} to $full\n" if $self->{verbose}; 
    6281        $self->_restore_link     ($full, $it) if $type eq "l"; 
    6382        $self->_restore_directory($full, $it) if $type eq "d"; 
    6483        $self->_restore_file     ($full, $it) if $type eq "f"; 
    65         #warn " * restored $it->{Path} to $full\n"; 
    66     } 
    67  
     84    } 
     85 
     86    warn " * fixing stat info\n" if $self->{verbose}; 
    6887    $self->_exec_statinfo_updates; 
    69  
     88    warn " * done\n" if $self->{verbose}; 
    7089    return 1; 
    7190} 
     
    82101 
    83102sub _decrypt_data { 
    84     my ($self, $dataref) = @_; 
     103    my $self = shift; 
     104    my $dataref = shift; 
     105    my %opts = valid_params(['no_batch', 'want_tempfile'], @_); 
    85106 
    86107    # find which key we're decrypting it using, else return chunk 
    87108    # unmodified in the not-encrypted case 
    88     my $rcpt = $self->{_meta}{"GPG-Recipient"} or 
    89         return $dataref; 
    90  
     109    if ($self->{_meta}) { 
     110        my $rcpt = $self->{_meta}{"GPG-Recipient"} or 
     111            return $dataref; 
     112    } 
     113         
    91114    unless ($ENV{'GPG_AGENT_INFO'} || 
    92115            @Brackup::GPG_ARGS || 
     
    103126    } 
    104127 
    105     my $output_temp = $self->_output_temp_filename; 
     128    my $output_temp = $opts{want_tempfile} ? 
     129        (tempfile())[1] 
     130        : $self->_output_temp_filename; 
    106131    my $enc_temp    = $self->_encrypted_temp_filename; 
    107132 
     
    110135    my @list = ("gpg", @Brackup::GPG_ARGS, 
    111136                "--use-agent", 
    112                 "--batch", 
     137                !$opts{no_batch} ? ("--batch") : (), 
    113138                "--trust-model=always", 
    114139                "--output",  $output_temp, 
     
    118143        and die "Failed to decrypt with gpg: $!\n"; 
    119144 
    120     my $ref = _read_file($output_temp); 
    121     unlink $output_temp, $enc_temp; 
    122     return $ref; 
     145    return $output_temp if $opts{want_tempfile}; 
     146    my $data = Brackup::Util::slurp($output_temp); 
     147    return \$data; 
    123148} 
    124149 
     
    176201    my ($self, $full, $it) = @_; 
    177202 
    178     if (-e $full) { 
     203    if (-e $full && -s $full) { 
    179204        # TODO: add --conflict={skip,overwrite} option, defaulting to nothing: which dies 
    180205        die "File $full ($it->{Path}) already exists.  Aborting."; 
     
    239264    my $self = shift; 
    240265    open (my $fh, $self->{file}) or die "Failed to open metafile: $!"; 
     266    my $linenum = 0; 
    241267    return sub { 
    242268        my $ret = {}; 
     
    244270        my $line;  # 
    245271        while (defined ($line = <$fh>)) { 
     272            $linenum++; 
    246273            if ($line =~ /^([\w\-]+):\s*(.+)/) { 
    247274                $ret->{$1} = $2; 
     
    257284                next; 
    258285            } 
    259             die "Unexpected line in metafile: $line"; 
     286 
     287            $line =~ s/[:^print:]/?/g; 
     288            die "Unexpected line in metafile $self->{file}, line $linenum: $line"; 
    260289        } 
    261290        return undef; 
     
    272301} 
    273302 
    274 sub _read_file { 
    275     my ($file) = @_; 
    276     open (my $fh, $file) or die "Failed to open $file for reading: $!\n"; 
    277     my $data = do { local $/; <$fh>; }; 
    278     return \$data; 
     303sub DESTROY { 
     304    my $self = shift; 
     305    unlink(grep { $_ } ($self->{_output_temp_filename}, 
     306                        $self->{_encrypted_temp_filename})); 
    279307} 
    280308 
  • trunk/lib/Brackup/Util.pm

    r152 r153  
    66use vars qw(@ISA @EXPORT_OK); 
    77@ISA = ('Exporter'); 
    8 @EXPORT_OK = ('tempfile', 'tempdir'); 
     8@EXPORT_OK = qw(tempfile tempdir slurp valid_params); 
    99 
    1010my $mainpid = $$; 
     
    1313END { 
    1414    # will happen after File::Temp's cleanup 
    15     foreach my $rec (@TEMP_FILES) { 
    16         next unless -e $rec->[0]; 
    17         unlink($rec->[0]); 
     15    if ($$ == $mainpid) { 
     16        foreach my $rec (@TEMP_FILES) { 
     17            next unless -e $rec->[0]; 
     18            unlink($rec->[0]); 
     19        } 
    1820    } 
    1921} 
     
    2325    my (@ret) = File::Temp::tempfile(); 
    2426    my $from = join(" ", (caller())[0..2]); 
    25  
     27    push @TEMP_FILES, [$ret[1], $from]; 
    2628    return wantarray ? @ret : $ret[0]; 
    2729} 
     
    3133} 
    3234 
     35sub slurp { 
     36    my $file = shift; 
     37    open(my $fh, $file) or die "Failed to open $file: $!\n"; 
     38    return do { local $/; <$fh>; } 
     39} 
     40 
     41sub valid_params { 
     42    my ($vlist, %uarg) = @_; 
     43    my %ret; 
     44    $ret{$_} = delete $uarg{$_} foreach @$vlist; 
     45    croak("Bogus options: " . join(', ', sort keys %uarg)) if %uarg; 
     46    return %ret; 
     47} 
     48 
    33491;