#!/usr/bin/perl -w ################################################################ # # Copyright (c) 1995-2014 SUSE Linux Products GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or 3 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program (see the file COPYING); if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # ################################################################ use strict; # buffer size for reading my $bufsize = 4*1024*1024; my ($opt_skip, $opt_disk, $opt_input, $opt_verbose); $opt_verbose = 0; 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++; 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 "seek: $!\n"; } my %done; while () { chomp; last unless length $_; my ($filetype, $file, $filesize, $blksize, @blocks) = split(/ /); die("invalid input '$_'\n") unless defined($file); $file =~ s/%([a-fA-F0-9]{2})/chr(hex($1))/ge; die("bad file '$file'\n") if "/$file/" =~ /\/\.{0,2}\//s; if ($file =~ /^(.*)\//s) { die("file without directory: $file\n") unless $done{$1} && $done{$1} eq 'd'; } if ($filetype eq 'd') { # dir print "$file\n" if $opt_verbose && $opt_verbose > 1; mkdir($file) || die("mkdir $file: $!\n"); $done{$file} = 'd'; next; } if ($filetype eq 'l') { # symlink my $target = $filesize; die("symlink without target\n") unless defined $target; $target =~ s/%([a-fA-F0-9]{2})/chr(hex($1))/ge; die("bad symlink: $target\n") if "/$target/" =~ /\/\.?\//s; if ("/$target/" =~ /^(\/\.\.)+\/(.*?)$/s) { my ($head, $tail) = ($1, $2); die("bad upref in symlink: $target\n") if "/$tail/" =~ /\/\.\.\//s; die("bad upref in symlink: $target\n") if ($head =~ y!/!!) > ($file =~ y!/!!); } else { die("bad upref in symlink: $target\n") if "/$target/" =~ /\/\.\.\//s; } print "$file\n" if $opt_verbose && !($opt_verbose == 1 && $file =~ /^KIWI\/.*\//); symlink($target, $file) || die("symlink $target $file: $!\n"); $done{$file} = 'l'; next; } die("illegal file type: $filetype\n") unless $filetype eq 'f'; die "invalid input '$_'\n" if !@blocks && $filesize; $done{$file} = 'f'; $filesize = int($filesize); if ($filesize == 0) { print "$file\n" if $opt_verbose && !($opt_verbose == 1 && $file =~ /^KIWI\/.*\//); open (O, '>', $file) or die "$file: $!\n"; close O; next; } $blksize = int($blksize); die "$file: invalid block size $blksize\n" unless $blksize > 0 && $blksize <= $bufsize; my $maxblocks = int($bufsize/$blksize); print "$file\n" if $opt_verbose && !($opt_verbose == 1 && $file =~ /^KIWI\/.*\//); open (O, '>', $file) or die "$file: $!\n"; for my $block (@blocks) { my $end; ($block, $end) = split(/-/, $block); $block = int($block); if ($block == 0) { # a hole! $end = (($end || 0) + 1) * $blksize; $end = $filesize if $end > $filesize; seek(O, $end, 1); $filesize -= $end; 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; (sysread(F, $buf, $size) || 0) == $size || die("$file: read: $!\n"); $filesize -= $size; (syswrite(O, $buf) || 0) == length($buf) || die("$file: write error\n"); } } close(O) || die("$file: close error: $!\n"); # sanity check die "$file: invalid file size ($filesize byes left)\n" if $filesize != 0; }