1 |
#!/usr/bin/perl |
2 |
|
3 |
use warnings; |
4 |
use strict; |
5 |
|
6 |
use File::Slurp; |
7 |
use File::Path; |
8 |
use Getopt::Long; |
9 |
|
10 |
my $dev = '/dev/sdb2'; |
11 |
my $mnt = '/mnt/42'; |
12 |
|
13 |
my ( $rollback, $commit ); |
14 |
|
15 |
GetOptions( |
16 |
'rollback!' => \$rollback, |
17 |
'commit!' => \$commit, |
18 |
) or die "unknown options: $!"; |
19 |
|
20 |
map { mkdir $_ unless -e $_ } map { "$mnt/$_" } ( 'log', 'ps' ); |
21 |
|
22 |
my $path = shift @ARGV; |
23 |
|
24 |
my ($dir,$file) = ($1,$2) if $path =~ m{^(?:(.+)/)?([^/]+)}; |
25 |
|
26 |
my $seq; |
27 |
my $opos = 0; |
28 |
|
29 |
if ( -e "$mnt/last" ) { |
30 |
my $log = read_file("$mnt/last"); |
31 |
$seq = readlink("$mnt/last"); |
32 |
$seq =~ s{^.+/(\d+)$}{$1} || die "can't parse sequence: $seq"; |
33 |
$seq++; |
34 |
$opos = $1 if $log =~ m{opos:\s+(\d+\.\d[k])}; |
35 |
die "can't decode opos" unless $opos; |
36 |
warn "# seq: $seq start: $opos\n"; |
37 |
|
38 |
} else { |
39 |
$seq = scalar(glob("$mnt/log/*")); |
40 |
warn "# $mnt/last not found, recover seq: $seq\n"; |
41 |
} |
42 |
|
43 |
|
44 |
my $log = "$mnt/log/$seq"; |
45 |
|
46 |
if ( -e $log ) { |
47 |
print "ERROR $log exists!\n",read_file($log); |
48 |
if ( $rollback ) { |
49 |
unlink $log || die "can't rollback $log: $!"; |
50 |
die "RECOVERY removed $log\n"; |
51 |
} elsif ( $commit ) { |
52 |
unlink "$mnt/last" || die "can't remove $mnt/last: $!"; |
53 |
symlink $log,"$mnt/last" || die "can't commit $log: $!"; |
54 |
die "ERROR: can't update $mnt/last -> $log" unless $log eq readlink "$mnt/last"; |
55 |
die "RECOVERY updated $mnt/last -> $log\n"; |
56 |
} else { |
57 |
die "$log exists, re-run with --commit or --rollback"; |
58 |
} |
59 |
} |
60 |
|
61 |
my $cmd = "dd_rescue -l $log -S $opos $path $dev && ln -s $log $mnt/last"; |
62 |
print "+ $cmd\n"; |
63 |
system($cmd) == 0 or die $?; |
64 |
|
65 |
my $fs = "$mnt/fs/$dir"; |
66 |
mkdir $fs unless -e $fs; |
67 |
|
68 |
$fs .= '/' . $file; |
69 |
symlink $fs,$log || die "can't create $fs: $!"; |
70 |
|