Changeset 153
- Timestamp:
- 05/23/07 21:28:38 (3 years ago)
- Location:
- trunk
- Files:
-
- 6 modified
-
Changes (modified) (1 diff)
-
TODO (modified) (1 diff)
-
brackup-restore (modified) (3 diffs)
-
lib/Brackup/Manual/Overview.pod (modified) (2 diffs)
-
lib/Brackup/Restore.pm (modified) (14 diffs)
-
lib/Brackup/Util.pm (modified) (4 diffs)
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 1 7 - brackup-target tool, to list/get brackup files from a target, 2 8 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 1 7 -- only use tempfiles in a 0700 directory under /tmp/brackup-$USER/ or 2 8 wherever. or option, when using encryption, to only doing 3 9 in-memory tempfiles, to avoid hitting disk? 4 5 -- (a future version of brackup-restore will let you grab the metafile from6 the target, but that's still a TODO item)7 10 8 11 -- <lj user=grahams>: if a file which existed when Brackup was -
trunk/brackup-restore
r140 r153 21 21 =item --from=NAME 22 22 23 Required. The metafile index. Probably named like "source-YYYYMMDD.brackup" 23 Required. The backup metafile, describing the tree you want to 24 restore. Probably named like "source-YYYYMMDD.brackup". If you lost 25 it, it's also stored on your backup target, and you can fetch it with 26 L<brackup-target>. 24 27 25 28 =item --to=NAME … … 65 68 66 69 use Brackup; 70 use Brackup::Util qw(tempfile); 67 71 68 72 my ($opt_verbose, $meta_file, $opt_help, $restore_dir, $opt_all, $prefix); … … 100 104 prefix => $prefix, 101 105 file => $meta_file, 106 verbose => $opt_verbose, 102 107 ); 103 108 -
trunk/lib/Brackup/Manual/Overview.pod
r133 r153 40 40 "myhome-yyyymmdd.brackup" in your current directory. Go look at it. 41 41 It 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) 42 just backed up. You might want to keep this file. Although, if you 43 don't, it's also stored on the target (in this case, Amazon), so it's 44 not critical. (You can always re-download lost .brackup files with 45 L<brackup-target>) 48 46 49 47 You might also notice two SQLite files at: … … 119 117 =head1 Restores 120 118 121 To do a restore, you'll currently need your *.brackup file handy122 (pulling the *.brackup file from the server isn't yet supported). Then 123 run:119 To do a restore, you'll need your *.brackup file handy. If you lost 120 it, you can re-download it from your backup target with 121 L<brackup-target>. Then run: 124 122 125 123 brackup-restore --from=foo.brackup --to=<dir> --all -
trunk/lib/Brackup/Restore.pm
r151 r153 4 4 use Carp qw(croak); 5 5 use Digest::SHA1; 6 use Brackup::Util qw(tempfile );6 use Brackup::Util qw(tempfile slurp valid_params); 7 7 8 8 sub new { … … 10 10 my $self = bless {}, $class; 11 11 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}; 15 16 16 17 $self->{_stats_to_run} = []; # stack (push/pop) of subrefs to reset stat info on … … 19 20 die "Backup file doesn't exist" unless $self->{file} && -f $self->{file}; 20 21 croak("Unknown options: " . join(', ', keys %opts)) if %opts; 22 23 $self->decrypt_file_if_needed; 24 21 25 return $self; 22 26 } … … 33 37 } 34 38 39 sub 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 35 53 sub restore { 36 54 my ($self) = @_; … … 60 78 $it->{Mode} ||= $meta->{DefaultDirMode} if $type eq "d"; 61 79 80 warn " * restoring $it->{Path} to $full\n" if $self->{verbose}; 62 81 $self->_restore_link ($full, $it) if $type eq "l"; 63 82 $self->_restore_directory($full, $it) if $type eq "d"; 64 83 $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}; 68 87 $self->_exec_statinfo_updates; 69 88 warn " * done\n" if $self->{verbose}; 70 89 return 1; 71 90 } … … 82 101 83 102 sub _decrypt_data { 84 my ($self, $dataref) = @_; 103 my $self = shift; 104 my $dataref = shift; 105 my %opts = valid_params(['no_batch', 'want_tempfile'], @_); 85 106 86 107 # find which key we're decrypting it using, else return chunk 87 108 # 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 91 114 unless ($ENV{'GPG_AGENT_INFO'} || 92 115 @Brackup::GPG_ARGS || … … 103 126 } 104 127 105 my $output_temp = $self->_output_temp_filename; 128 my $output_temp = $opts{want_tempfile} ? 129 (tempfile())[1] 130 : $self->_output_temp_filename; 106 131 my $enc_temp = $self->_encrypted_temp_filename; 107 132 … … 110 135 my @list = ("gpg", @Brackup::GPG_ARGS, 111 136 "--use-agent", 112 "--batch",137 !$opts{no_batch} ? ("--batch") : (), 113 138 "--trust-model=always", 114 139 "--output", $output_temp, … … 118 143 and die "Failed to decrypt with gpg: $!\n"; 119 144 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; 123 148 } 124 149 … … 176 201 my ($self, $full, $it) = @_; 177 202 178 if (-e $full ) {203 if (-e $full && -s $full) { 179 204 # TODO: add --conflict={skip,overwrite} option, defaulting to nothing: which dies 180 205 die "File $full ($it->{Path}) already exists. Aborting."; … … 239 264 my $self = shift; 240 265 open (my $fh, $self->{file}) or die "Failed to open metafile: $!"; 266 my $linenum = 0; 241 267 return sub { 242 268 my $ret = {}; … … 244 270 my $line; # 245 271 while (defined ($line = <$fh>)) { 272 $linenum++; 246 273 if ($line =~ /^([\w\-]+):\s*(.+)/) { 247 274 $ret->{$1} = $2; … … 257 284 next; 258 285 } 259 die "Unexpected line in metafile: $line"; 286 287 $line =~ s/[:^print:]/?/g; 288 die "Unexpected line in metafile $self->{file}, line $linenum: $line"; 260 289 } 261 290 return undef; … … 272 301 } 273 302 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; 303 sub DESTROY { 304 my $self = shift; 305 unlink(grep { $_ } ($self->{_output_temp_filename}, 306 $self->{_encrypted_temp_filename})); 279 307 } 280 308 -
trunk/lib/Brackup/Util.pm
r152 r153 6 6 use vars qw(@ISA @EXPORT_OK); 7 7 @ISA = ('Exporter'); 8 @EXPORT_OK = ('tempfile', 'tempdir');8 @EXPORT_OK = qw(tempfile tempdir slurp valid_params); 9 9 10 10 my $mainpid = $$; … … 13 13 END { 14 14 # 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 } 18 20 } 19 21 } … … 23 25 my (@ret) = File::Temp::tempfile(); 24 26 my $from = join(" ", (caller())[0..2]); 25 27 push @TEMP_FILES, [$ret[1], $from]; 26 28 return wantarray ? @ret : $ret[0]; 27 29 } … … 31 33 } 32 34 35 sub slurp { 36 my $file = shift; 37 open(my $fh, $file) or die "Failed to open $file: $!\n"; 38 return do { local $/; <$fh>; } 39 } 40 41 sub 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 33 49 1;
