summaryrefslogtreecommitdiff
path: root/extractbuild
blob: f46ffc14ab614899b4ab5b399416e90f506887e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/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(<S>) {
  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;
}