--- 42.pl 2009/07/18 17:31:53 1 +++ 42.pl 2009/07/19 14:31:08 6 @@ -6,65 +6,151 @@ 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 $unit = 'MB'; + +my ( $verbose, $skip, $init, $debug ) = ( 0, 1 ); GetOptions( - 'rollback!' => \$rollback, - 'commit!' => \$commit, + 'verbose!' => \$verbose, + 'skip!' => \$skip, + 'init!' => \$init, + 'debug!' => \$debug, ) or die "unknown options: $!"; -map { mkdir $_ unless -e $_ } map { "$mnt/$_" } ( 'log', 'ps' ); -my $path = shift @ARGV; +sub size { + my $size = shift; + $size =~ s{$unit}{}; + $size; +} -my ($dir,$file) = ($1,$2) if $path =~ m{^(?:(.+)/)?([^/]+)}; +my @part; -my $seq; -my $opos = 0; +sub partitions { + @part = (); + open(my $parted, '-|', "parted -s $dev unit $unit print free") || die "parted: $!"; + while(<$parted>) { + chomp; + s{^\s+}{}; + next unless $_; + my @p = map { size($_) } split(/\s+/, $_ ); + warn "## $_ ",dump( @p ) if $debug; + if ( $p[3] && $p[3] eq 'Free' ) { + $part[0] = [ @p ]; + } elsif ( $p[0] =~ m{^\d+$} ) { + my $nr = shift @p; + $part[$nr] = [ @p ]; + } else { + warn "SKIP ",dump( @p ) if $debug; + } + } + warn "# part = ",dump( @part ) if $verbose; + return @part; +} -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 = scalar(glob("$mnt/log/*")); - warn "# $mnt/last not found, recover seq: $seq\n"; +sub mount_42 { + my $node = $dev . '1'; + if ( ! -e $node ) { + print STDERR "wait for $node"; + sleep 1; + while ( ! -e $node ) { + print STDERR "."; + sleep 1; + } + print STDERR " ready\n"; + } + warn "+ mount $node $mnt\n"; + system("mount $node $mnt") == 0 or die "can't mount: $!"; } +my $remount_on = qr/(mkpart|rm)/; -my $log = "$mnt/log/$seq"; - -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"; +sub parted { + my $command = shift; + my @before = partitions(); + + system "umount $mnt" if $command =~ $remount_on; + + warn "+ $command\n"; + system("parted -s $dev unit $unit $command") == 0 or die "parted: $?"; + if ( $command =~ $remount_on ) { + my @part = partitions(); + while ( $#before == $#part ) { + warn "re-read partition table\n"; + sleep 1; + @part = partitions(); + } + mount_42 if $part[1]; } } -my $cmd = "dd_rescue -l $log -S $opos $path $dev && ln -s $log $mnt/last"; -print "+ $cmd\n"; -system($cmd) == 0 or die $?; +partitions(); + +if ( $init ) { + #parted("rm $_") foreach grep { defined $part[$_] } map { $#part - $_ + 1 } ( 1 .. $#part ); + system("umount $mnt"); + parted("mklabel msdos"); + + parted("mkpartfs primary ext2 0M 42MB"); + parted("mkpart extended 42MB " . $part[0]->[1] . $unit); + + my $cmd = read_file($0); + my $path = "$mnt/42.pl"; + write_file( $path, $cmd ); + chmod 0755, $path; + print "created $path ",-s $path, " bytes\n"; + exit; +} + +map { mkdir $_ unless -e $_ } map { "$mnt/$_" } ( 'log', 'fs', 'stat' ); + +my $path = shift @ARGV || die "usage: $0 /path/to/file\n"; + +my ($dir,$file) = ($1,$2) if $path =~ m{^(?:(.+)/)?([^/]+)}; my $fs = "$mnt/fs/$dir"; -mkdir $fs unless -e $fs; +mkpath $fs unless -e $fs; $fs .= '/' . $file; -symlink $fs,$log || die "can't create $fs: $!"; +if ( -e $fs ) { + if ( $skip ) { + warn "SKIP $fs\n"; + exit 0; + } else { + die "$fs exists! re-run with --skip\n"; + } +} + +my @stat = stat($path); +die "can't stat $path: $!" unless @stat; +warn "# $path ",$stat[7],$/; + +my $size = int( ( $stat[7] + 1024 ) / 1024 / 1024 ); +$size += $size % 64; # 16k allocation cluster +my ( $free_start, undef, $free_size ) = @{$part[0]}; + +my $part_end = $free_start + $size; + +parted("mkpart logical $free_start$unit $part_end$unit "); +my $part_size = $part[$#part]->[2] || die "can't get size of new partition"; +die "not enough space on $dev $size > $part_size" if $size > $part_size; + +my $part_nr = $#part; + +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;