#!/usr/bin/perl -w use strict; # buffer size for reading my $bufsize = 4*1024*1024; my ($opt_skip, $opt_disk, $opt_input, $opt_verbose); while (@ARGV) { if ($ARGV[0] eq '--skip') { shift @ARGV; $opt_skip = shift @ARGV; next; } if ($ARGV[0] eq '--disk') { shift @ARGV; $opt_disk = shift @ARGV; next; } if ($ARGV[0] eq '--input') { shift @ARGV; $opt_input = shift @ARGV; next; } if ($ARGV[0] eq '--verbose' || $ARGV[0] eq '-v') { shift @ARGV; $opt_verbose = 1; next; } last; } die "need to specify disk image\n" unless $opt_disk; open(F, '<', $opt_disk) || die "$opt_disk: $!\n"; if($opt_input) { open(S, '<', $opt_input) || die "$opt_input: $!\n"; } else { open(S, '<&STDIN') || die "can't dup stdin: $!\n"; } # skip build status if($opt_skip) { seek(S, $opt_skip, 0) || die "$!\n"; } while() { chomp; last unless length $_; my ($file, $filesize, $blksize, @blocks) = split(/ /); if($#blocks == -1 && $filesize) { die "invalid input '$_'\n"; } $filesize = int($filesize); $blksize = int($blksize); die "invalid block size" unless ($blksize > 0 && $blksize <= $bufsize); my $maxblocks = int($bufsize/$blksize); $file =~ s/.*\///; # ensure basename, also stops directory traversal $file =~ s/[^[:print:]]/_/g; # no binary junk in file names print "$file\n" if $opt_verbose; open (O, '>', $file) or die "$file: $!"; for my $block (@blocks) { my ($block, $end) = split(/-/, $block); $block = int($block); if($block == 0) { # a hole! seek(O, $blksize, 1); $filesize -= $blksize; next; } $end = $block unless $end; $end = int($end); seek(F, $block*$blksize, 0) || die "$file: seek: $!\n"; while($block <= $end && $filesize) { my $size; if($end == $block) { $size = $blksize; ++$block; } elsif($maxblocks >= $end-$block) { $size = ($end-$block)*$blksize; $block += $end-$block; } else { $size = $maxblocks*$blksize; $block += $maxblocks; } $size = $filesize if $size > $filesize; my $buf; if((sysread(F, $buf, $size) || 0) != $size) { die "$file: read: $!\n"; } $filesize -= $size; print O $buf; } } close O; # sanity check die "$file: invalid file size ($filesize byes left)\n" if $filesize != 0; }