Rev 1107 | Blame | Compare with Previous | Last modification | View Log | RSS feed
######################################################################### Copyright (C) 2008 ERG Limited, All rights reserved## Module name : CreateSdImage.pm# Module type : Makefile system# Compiler(s) : Perl# Environment(s): jats## Description : Create a File System Image from a directory# tree.# Create the various file partitions and images# 0) Determine the partition sizes# 1) Create an empty image of the entire SD card# 2) Partition it up# 3) Determine the real size of each patition# 4) Create ext3 file system images# 5) Transfer the file system images into the SD image# 6) Install grub - if required##......................................................................#require 5.008_002;use strict;use warnings;package CreateSdImage;use JatsError;use JatsSystem;use FileUtils;use File::Path;# automatically export what we need into namespace of caller.use Exporter();our (@ISA, @EXPORT);@ISA = qw(Exporter);@EXPORT = qw(CreateSdImage);## Data structure to describe the ouput partition(s)# Defined early so that it can be modified by the platform specific hook# Structure is a hash of hashed elements# Key: Partition number# Partition-0 is used to hold a complete (test) image## Value: A hash of other entries# These are# Key: name - Name of the partition# root - Partition mount point# size - Size in Megabytes of the partition# Set internally.# 'rest' is auto calculated, must be last# type - Optional. If 'extended', then an extended# partition will be created and all following# partitions will be created in that extended# partition# noprune - Don't prune after partition# Set internally.#our %partitions = (0 => { name=>"fullfs", root=>"/" }, # Just for Test1 => { name=>"rootfs", root=>"/" },2 => { name=>"logfs" , root=>"/var/log" },3 => { name=>"afcfs", root=>"/afc" },4 => { name=>"varfs", root=>"/var" },# 4 => { type=>"extended" },);#-------------------------------------------------------------------------------# Function : CreateSdImage## Description : Create an ETX3 File System Image froma directory## Inputs : $outdir - Target directory# $image_name - Root name of the output image# $WORK - Root of file system image# $BUILD - Scratch Work Area# $diskGeometry - Reference to disk Geometry hash# Keys are:# fullfs# size# rootfs# logfs# afcfs# Returns : varfs#sub CreateSdImage{my ($outdir,$image_name,$WORK, $BUILD, $diskGeometry ) = @_;my $install_grub = ( -f "$WORK/boot/grub/grub" );## Ensure that the required utility programs can be found and that they# are being consumed from packages and not from the users path#Error ( "Required utility program not found in package: e2fsimage" )unless ( LocateProgInPath ('e2fsimage' ) );Error ( "Required utility program not found in package: grub" )unless ( ! $install_grub || LocateProgInPath ('grub' ) );Error ("CreateSdImage:disk-blocks must be specified")unless ( $diskGeometry->{'size'} );my $disk_blocks = $diskGeometry->{'size'};### Magic: The size of the SD card# This was determined by 'dd' ing an SD card and using the number of# blocks that could be extracted.#my $disk_sectors = $disk_blocks * 2;$image_name .= '.raw';my $disk_image = "$BUILD/$image_name";my $pr_size = 20 * 2; # Sectors for a partition table## Determine partition start and end sectors# Work in sectors#my $pstart = $pr_size;my $in_extended = 0;Message ("Calculate partition sizes");foreach my $partition ( sort keys %partitions ){my $pdata = $partitions{$partition};my $rest = 0;## Determine configured size#if ( $pdata->{name} ){Error ("CreateSdImage: Partition size not specified: $pdata->{name} ")unless ( exists($diskGeometry->{$pdata->{name}}) );$pdata->{size} = $diskGeometry->{$pdata->{name}} || 0;}unless ($partition ){$pdata->{size} *= 2;next;}## Validate the current start#if ( $pstart >= $disk_sectors ){my @data;foreach my $partition ( sort keys %partitions ){next unless ( $partition );my $pdata = $partitions{$partition};my $name = $pdata->{name};next unless ( $name );push @data, sprintf( "Partition-%d (%10s): Sector Size=~%5dM\n",$partition,$name,($diskGeometry->{$pdata->{name}} || 'Remainder') );}Error ("Invalid Disk Configuration. Partition Sizes exceed Disk size","Disk Size : $disk_blocks (~" . $disk_blocks / 1024 . ")M",@data);}if ( exists $pdata->{type} && $pdata->{type} eq 'extended' ){$pdata->{start} = $pstart;$pdata->{end} = $disk_sectors;$pdata->{size} = ($disk_sectors - $pstart);$pdata->{id} = 5;$in_extended = 1;}else{Error ("Partition Table setup. The 'rest' keyword must only be used on the last partition") if ( $rest );$pdata->{type} = 'Linux';$pdata->{id} = 83;$pstart += $pr_size if ( $in_extended );if ( $pdata->{size} eq 'rest' ){Error ("Partition Table setup. The 'rest' keyword can only be used once") if ( $rest );$pdata->{size} = ($disk_sectors - $pstart) / 2048;$rest = 1;}$pdata->{start} = $pstart;$pdata->{end} = $pstart + ( $pdata->{size} * 1024 * 2);$pdata->{size} = $pdata->{end} - $pdata->{start};$pstart = $pdata->{end};}}## Display calculated partition sizes#foreach my $partition ( sort keys %partitions ){next unless ( $partition );my $pdata = $partitions{$partition};my $name = $pdata->{name} || "";printf "Partition-%d (%10s): Sector Start=%10d, End=%10d, Sector Size=%10d(~%5dM), Id=%s\n",$partition,$name,$pdata->{start},$pdata->{end},$pdata->{size},$pdata->{size}/2/1024,$pdata->{id};}Message ("Creating Empty SD Image");System ('dd', 'if=/dev/zero', "of=$disk_image", 'bs=1024', "count=$disk_blocks" );Message ("Creating Partition Table");open (SFDISK, "| /sbin/sfdisk -f -L $disk_image" ) || Error ("Cannot invoke sfdisk");print SFDISK "unit: sectors\n\n";foreach my $partition ( sort keys %partitions ){next unless ( $partition );my $pdata = $partitions{$partition};printf SFDISK "Partition-%d: start=%10d, size=%10d, Id=%s\n",$partition,$pdata->{start},$pdata->{size},$pdata->{id};}close (SFDISK);################################################################################## Split the complete file system image into required partitions# Once the partition has been created it will be removed from the image# What is left is the root file system - the final partition## Note: Size values are taken after the SD card has been partitioned# The partioning process wants to place partitions on cylinder# boundaries. This is why the number are not nice powers of two or 10## Partition logic:# P1 root - 30Meg# P2 log - 20Meg Mounted as /var/log)# P3 afc - 150Meg# P4 var - Rest (~290 Meg)### Note: Order is important, as dir tree will be pruned# Be smart and determine order to create_fs#foreach my $partition ( CalcPartitionOrder() ){create_fs ($WORK, $BUILD, $partition);}## Insert the images into the SD image#foreach my $partition ( sort keys %partitions ){next unless ( $partition );my $pdata = $partitions{$partition};next unless ( defined $pdata->{fsname} );Message ("Insert filesystem $pdata->{name} into SD Image");my $cmd = "dd if=$pdata->{fsname} of=$disk_image bs=512 conv=notrunc seek=$pdata->{start} count=$pdata->{size}";System ($cmd);}################################################################################## Install grub into the MBR of the file system [ optional]# This is only required for an x86 target and will detected by the# presence of a key grub file# The grub files will have been copied into the filesystem image earlier#if ( $install_grub ){Message ("Installing Grub into the MBR");## Need to run a copy of grab (built for the build machine) on the image## Need a command file to feed into grub# Prevent grub from scanning bios#my $map_file = "$BUILD/device_map";open (GRUB, ">$map_file" ) || Error ("Cannot create grub device map file: $map_file");print GRUB "(hd0) $disk_image\n";close (GRUB);## Run grub and pipe in commands#open (GRUB, "| grub --device-map=$map_file --batch" ) || Error ("Cannot invoke GRUB");print GRUB "device (hd0) $disk_image\n";print GRUB "root (hd0,0)\n";print GRUB "setup (hd0)\n";close (GRUB);## Cleanup temp files#unlink $map_file;}################################################################################## Package up the file system images# Transfer the required build artifacts directly into the package## This images are large. They are stored in a gzip form to compress them## Output to disk as:# gunzip -c rootfs.ext3.gz | dd of=/dev/sdb1 bs=10K#mkpath( $outdir, 0, 0775);Message ("Package up the (compressed) filesystem images");my %package_list;## Complete image#my $ofile = "$outdir/${image_name}.gz";$package_list{$ofile}{type} = 'image';System ("gzip -c $disk_image > $ofile");# ## # Create test images of the internal partitions# ## mkpath( "$outdir/test", 0, 0775);# foreach my $partition ( sort keys %partitions )# {# my $pdata = $partitions{$partition};# next unless ( defined $pdata->{fsname} );## my $file = $pdata->{fsname} ;# my $ofile = StripDir($file) ;# $ofile = "$outdir/test/${ofile}.gz";# $package_list{$ofile}{type} = 'ext3';# $package_list{$ofile}{partition} = $partition;# System ("gzip -c $file > $ofile");# }### Useful information for testing#Message ("Useful load commands for testing");foreach my $entry ( sort keys %package_list ){next unless ( defined $package_list{$entry}{partition} );my $ifname = AbsPath( $entry );my $ofname = "/dev/sdb" . $package_list{$entry}{partition} ;my $cmd = "gunzip -c $ifname | dd bs=1M of=$ofname";Message $cmd;}foreach my $entry ( sort keys %package_list ){next if ( defined $package_list{$entry}{partition} );my $ifname = AbsPath( $entry );my $ofname = "/dev/sdb";my $cmd = "gunzip -c $ifname | dd bs=1M of=$ofname";Message $cmd;}}#-------------------------------------------------------------------------------# Function : create_fs## Description : Create the ext3 image of a given partition## Inputs : $WORK - Root of data# $BUILD - Scratch Area# $FS_PART - Partition number## Returns :#sub create_fs{my ($WORK, $BUILD, $FS_PART) = @_;my $pdata = $partitions{$FS_PART} || Error ("Unknown partition number: $FS_PART");my $FS_IMAGE = $pdata->{name};my $FS_BASE = $pdata->{root};my $FS_SIZE = $pdata->{size} / 2;## The 'full' image may not be required# It will have a zero size#return unless ( $FS_SIZE );Message "Creating EXT3 file system: ${FS_IMAGE}, Partition: ${FS_PART}, Size: ${FS_SIZE} blocks";$FS_IMAGE = "${BUILD}/${FS_IMAGE}.ext3";$pdata->{fsname} = $FS_IMAGE;unlink ${FS_IMAGE};my $cmd = "e2fsimage -f ${FS_IMAGE} -d ${WORK}${FS_BASE} -u 0 -g 0 -s ${FS_SIZE}";$cmd .= " -v " if ( IsVerbose(1) );Verbose $cmd;open (E2FSIMAGE, "$cmd |" ) || Error ("Can't invoke e2fsimage");while ( <E2FSIMAGE> ){chomp;Verbose (" $_");}close (E2FSIMAGE);$cmd = "/sbin/tune2fs -j -c 10 -i 5d ${FS_IMAGE}";Verbose $cmd;open (E2FSIMAGE, "$cmd |" ) || Error ("Can't invoke tunefs");while ( <E2FSIMAGE> ){chomp;Verbose (" $_");}close (E2FSIMAGE);## Clean the subtree that has been encapsulated in the partition# Leave the mountpoint#unless ( $pdata->{noprune} ){Verbose ("Cleaning partition data - leave mount point", $pdata->{root});foreach my $file ( glob("${WORK}/${FS_BASE}/*") ){if ( -d $file ){rmtree( $file );}else{unlink ($file);}}}}#-------------------------------------------------------------------------------# Function : CalcPartitionOrder## Description : Determine the order in which we need to create the partitions# Simple: if 'A' is a subset of 'B' then 'A' must be done first## Inputs :## Returns :#sub CalcPartitionOrder{my %pdata;my @porder;## Examine all partitions# Partition-0: process first, don't prune# Ignore extended partions. The only affect the partition table#foreach my $partition ( sort keys %partitions ){if ( $partition == 0 ){push @porder, $partition;$partitions{$partition}{noprune} = 1;next;}next if ( ($partitions{$partition}{type} || '') eq 'extended' );$pdata{$partitions{$partition}{root}} = $partition;}## Now we have the names of the partions# Reverse Order#foreach my $name ( reverse sort keys %pdata ){push @porder, $pdata{$name};}## Mark the last partition as non-prune#my $last = $porder[$#porder];$partitions{$last}{noprune} = 1;## Return the order in which partitions MUST be pruned#return @porder;}