--- 42.pl 2009/07/18 18:05:37 2 +++ 42.pl 2009/07/19 11:55:06 3 @@ -6,71 +6,107 @@ use File::Slurp; use File::Path; use Getopt::Long; +use Data::Dump qw/dump/; -my $dev = '/dev/sdb2'; +my $dev = '/dev/sdb'; my $mnt = '/mnt/42'; -my ( $rollback, $commit ); +my ( $init, $debug ); GetOptions( - 'rollback!' => \$rollback, - 'commit!' => \$commit, + 'init!' => \$init, + 'debug!' => \$debug, ) or die "unknown options: $!"; -map { mkdir $_ unless -e $_ } map { "$mnt/$_" } ( 'log', 'fs' ); -my $path = shift @ARGV || die "usage: $0 /path/to/file\n"; +my @part; -my ($dir,$file) = ($1,$2) if $path =~ m{^(?:(.+)/)?([^/]+)}; +sub partitions { + open(my $parted, '-|', "parted -s $dev unit mb print free") || die "parted: $!"; + while(<$parted>) { + warn "## $_" if $debug; + if ( m{^\s+([\d\.]+)MB\s+([\d\.]+)MB\s+([\d\.]+)MB\s+Free Space} ) { + $part[0] = [ $1, $2, $3, 'free' ]; + } + next unless m{^\s+\d+\s+}; + s{^\s+}{}; + s{([\d\.]+)MB}{$1}g; + my ( $nr, $start, $end, $size, undef ) = split(/\s+/, $_, 5); + $part[$nr] = [ $start, $end, $size ]; + } + warn "## part = ",dump( @part ) if $debug; + return @part; +} + +sub parted { + my $command = shift; + warn "+ $command\n"; + system("parted -s $dev unit mb $command") == 0 or die "parted: $!"; +} -my $seq; -my $opos = 0; -if ( -e "$mnt/last" ) { - my $log = read_file("$mnt/last"); - $seq = readlink("$mnt/last"); - $seq =~ s{^.+/(\d+)$}{$1} || die "can't parse sequence: $seq"; - $seq++; - $opos = $1 if $log =~ m{opos:\s+(\d+\.\d[k])}; - die "can't decode opos" unless $opos; - warn "# seq: $seq start: $opos\n"; - -} else { - $seq = $#{ glob("$mnt/log/*") } + 1; - warn "# $mnt/last not found, recover seq: $seq\n"; +if ( $init ) { + system("umount $mnt"); + partitions(); + parted("rm $_") foreach grep { defined $part[$_] } ( 1 .. 4 ); + parted("mkpartfs primary ext2 0 42MB"); + partitions(); + parted("mkpart extended 42MB " . $part[0]->[1] . 'MB'); + system "sync;sync"; + partitions(); + system("mount ${dev}1 $mnt") == 0 or die "can't mount: $!"; + my $cmd = read_file($0); + write_file( "$mnt/42.pl", $cmd ); + chmod 0755, "$mnt/42.pl"; + exit; } +map { mkdir $_ unless -e $_ } map { "$mnt/$_" } ( 'log', 'fs', 'stat' ); -my $log = "$mnt/log/$seq"; +my $path = shift @ARGV || die "usage: $0 /path/to/file\n"; -if ( -e $log ) { - print "ERROR $log exists!\n",read_file($log); - if ( $rollback ) { - unlink $log || die "can't rollback $log: $!"; - die "RECOVERY removed $log\n"; - } elsif ( $commit ) { - unlink "$mnt/last" || die "can't remove $mnt/last: $!"; - symlink $log,"$mnt/last" || die "can't commit $log: $!"; - die "ERROR: can't update $mnt/last -> $log" unless $log eq readlink "$mnt/last"; - die "RECOVERY updated $mnt/last -> $log\n"; - } else { - die "$log exists, re-run with --commit or --rollback"; - } -} +my ($dir,$file) = ($1,$2) if $path =~ m{^(?:(.+)/)?([^/]+)}; my $fs = "$mnt/fs/$dir"; mkpath $fs unless -e $fs; $fs .= '/' . $file; - die "$fs exists!" if -e $fs; -my $cmd = "dd_rescue -l $log -S $opos $path $dev"; -print "+ $cmd\n"; -system($cmd) == 0 or warn "# exit code: $?"; +my @stat = stat($path); +die "can't stat $path: $!" unless @stat; +warn "# $path ",$stat[7],$/; -unlink("$mnt/last") && symlink($log,"$mnt/last") || die "can't commit log $log -> $mnt/last"; +partitions(); -symlink($log,$fs) || die "can't create $fs: $!"; +my $size_mb = int( $stat[7] / 1000 / 1000 ) + 1; +my ( $free_start, undef, $free_size ) = @{$part[0]}; -print "OK $fs -> $log\n"; +my $part_end = $free_start + $size_mb; + +my $last_part = $#part; + +system("umount $mnt"); +parted("mkpart logical ${free_start}MB ${part_end}MB"); +partitions(); +system("mount ${dev}1 $mnt") == 0 or die "can't mount $mnt: $!"; + +my $part_size = $part[$#part]->[2] || die "can't get size of new partition"; + +die "not enough space on $dev $size_mb > $part_size" if $size_mb > $part_size; + +my $part_nr = $#part; + +die "can't create partition $last_part == $part_nr" if $last_part == $part_nr; + +write_file( "$mnt/stat/$part_nr", join("\n",@stat) ); + +my $log = "$mnt/log/$part_nr"; + +my $to_dev = $dev . $part_nr; + +symlink( $to_dev ,$fs) || die "can't create $fs: $!"; + +my $cmd = "dd_rescue -w -l $log $path $to_dev"; +print "+ $cmd\n"; +exec $cmd;