| 1105 |
dpurdie |
1 |
########################################################################
|
|
|
2 |
# Copyright (C) 2008 ERG Limited, All rights reserved
|
|
|
3 |
#
|
|
|
4 |
# Module name : CreateSdImage.pm
|
|
|
5 |
# Module type : Makefile system
|
|
|
6 |
# Compiler(s) : Perl
|
|
|
7 |
# Environment(s): jats
|
|
|
8 |
#
|
|
|
9 |
# Description : Create a File System Image from a directory
|
|
|
10 |
# tree.
|
|
|
11 |
# Create the various file partitions and images
|
|
|
12 |
# 0) Determine the partition sizes
|
|
|
13 |
# 1) Create an empty image of the entire SD card
|
|
|
14 |
# 2) Partition it up
|
|
|
15 |
# 3) Determine the real size of each patition
|
|
|
16 |
# 4) Create ext3 file system images
|
|
|
17 |
# 5) Transfer the file system images into the SD image
|
|
|
18 |
# 6) Install grub - if required
|
|
|
19 |
#
|
|
|
20 |
#......................................................................#
|
|
|
21 |
|
|
|
22 |
require 5.008_002;
|
|
|
23 |
use strict;
|
|
|
24 |
use warnings;
|
|
|
25 |
|
|
|
26 |
package CreateSdImage;
|
|
|
27 |
|
|
|
28 |
use JatsError;
|
|
|
29 |
use JatsSystem;
|
|
|
30 |
use FileUtils;
|
|
|
31 |
use File::Path;
|
|
|
32 |
|
|
|
33 |
|
|
|
34 |
# automatically export what we need into namespace of caller.
|
|
|
35 |
use Exporter();
|
|
|
36 |
our (@ISA, @EXPORT);
|
|
|
37 |
@ISA = qw(Exporter);
|
|
|
38 |
@EXPORT = qw(
|
|
|
39 |
CreateSdImage
|
|
|
40 |
);
|
|
|
41 |
|
|
|
42 |
#
|
|
|
43 |
# Data structure to describe the ouput partition(s)
|
|
|
44 |
# Defined early so that it can be modified by the platform specific hook
|
|
|
45 |
# Structure is a hash of hashed elements
|
|
|
46 |
# Key: Partition number
|
|
|
47 |
# Partition-0 is used to hold a complete (test) image
|
|
|
48 |
#
|
|
|
49 |
# Value: A hash of other entries
|
|
|
50 |
# These are
|
|
|
51 |
# Key: name - Name of the partition
|
|
|
52 |
# root - Partition mount point
|
|
|
53 |
# size - Size in Megabytes of the partition
|
|
|
54 |
# 'rest' is auto calculated
|
|
|
55 |
# type - Optional. If 'extended', then an extended
|
|
|
56 |
# partition will be created and all following
|
|
|
57 |
# partitions will be created in that extended
|
|
|
58 |
# partition
|
|
|
59 |
# noprune - Don't prune after partition
|
|
|
60 |
# Set internally.
|
|
|
61 |
#
|
|
|
62 |
our %partitions = (
|
|
|
63 |
|
|
|
64 |
|
|
|
65 |
1 => { name=>"rootfs", root=>"/" , size=>30, },
|
|
|
66 |
2 => { name=>"logfs" , root=>"/var/log" , size=>20, },
|
|
|
67 |
3 => { name=>"afcfs", root=>"/afc" , size=>150, },
|
|
|
68 |
4 => { name=>"varfs", root=>"/var" , size=>"rest" },
|
|
|
69 |
|
|
|
70 |
# 4 => { type=>"extended" },
|
|
|
71 |
);
|
|
|
72 |
|
|
|
73 |
#-------------------------------------------------------------------------------
|
|
|
74 |
# Function : CreateSdImage
|
|
|
75 |
#
|
|
|
76 |
# Description : Create an ETX3 File System Image froma directory
|
|
|
77 |
#
|
|
|
78 |
# Inputs : $outdir - Target directory
|
|
|
79 |
# $image_name - Root name of the output image
|
|
|
80 |
# $WORK - Root of file system image
|
|
|
81 |
# $BUILD - Scratch Work Area
|
|
|
82 |
# $disk_blocks - Size of target disk
|
| 1107 |
dpurdie |
83 |
# $fullfs - Size of test full fs
|
| 1105 |
dpurdie |
84 |
#
|
|
|
85 |
# Returns :
|
|
|
86 |
#
|
|
|
87 |
sub CreateSdImage
|
|
|
88 |
{
|
| 1107 |
dpurdie |
89 |
my ($outdir,$image_name,$WORK, $BUILD, $disk_blocks, $fullfs) = @_;
|
|
|
90 |
my $install_grub = ( -f "$WORK/boot/grub/grub" );
|
| 1105 |
dpurdie |
91 |
|
|
|
92 |
#
|
| 1107 |
dpurdie |
93 |
# Ensure that the required utility programs can be found and that they
|
|
|
94 |
# are being consumed from packages and not from the users path
|
| 1105 |
dpurdie |
95 |
#
|
| 1107 |
dpurdie |
96 |
Error ( "Required utility program not found in package: e2fsimage" )
|
|
|
97 |
unless ( LocateProgInPath ('e2fsimage' ) );
|
|
|
98 |
|
|
|
99 |
Error ( "Required utility program not found in package: grub" )
|
|
|
100 |
unless ( ! $install_grub || LocateProgInPath ('grub' ) );
|
|
|
101 |
|
|
|
102 |
#
|
|
|
103 |
# Insert size of the FullFS
|
|
|
104 |
# May be zero
|
|
|
105 |
#
|
|
|
106 |
$partitions{0}{size} = $fullfs;
|
|
|
107 |
|
|
|
108 |
#
|
|
|
109 |
#
|
| 1105 |
dpurdie |
110 |
# Magic: The size of the SD card
|
|
|
111 |
# This was determined by 'dd' ing an SD card and using the number of
|
|
|
112 |
# blocks that could be extracted.
|
|
|
113 |
#
|
|
|
114 |
my $disk_sectors = $disk_blocks * 2;
|
|
|
115 |
$image_name .= '.raw';
|
|
|
116 |
my $disk_image = "$BUILD/$image_name";
|
|
|
117 |
my $pr_size = 20 * 2; # Sectors for a partition table
|
|
|
118 |
|
|
|
119 |
#
|
|
|
120 |
# Determine partition start and end sectors
|
|
|
121 |
# Work in sectors
|
|
|
122 |
#
|
|
|
123 |
my $pstart = $pr_size;
|
|
|
124 |
my $in_extended = 0;
|
|
|
125 |
|
|
|
126 |
Message ("Calculate partition sizes");
|
|
|
127 |
foreach my $partition ( sort keys %partitions )
|
|
|
128 |
{
|
|
|
129 |
my $pdata = $partitions{$partition};
|
|
|
130 |
my $rest = 0;
|
|
|
131 |
|
|
|
132 |
unless ($partition )
|
|
|
133 |
{
|
|
|
134 |
$pdata->{size} *= 2;
|
|
|
135 |
next;
|
|
|
136 |
}
|
|
|
137 |
|
|
|
138 |
if ( exists $pdata->{type} && $pdata->{type} eq 'extended' )
|
|
|
139 |
{
|
|
|
140 |
$pdata->{start} = $pstart;
|
|
|
141 |
$pdata->{end} = $disk_sectors;
|
|
|
142 |
$pdata->{size} = ($disk_sectors - $pstart);
|
|
|
143 |
$pdata->{id} = 5;
|
|
|
144 |
$in_extended = 1;
|
|
|
145 |
}
|
|
|
146 |
else
|
|
|
147 |
{
|
|
|
148 |
Error ("Partition Table setup. The 'rest' keyword must only be used on the last partition") if ( $rest );
|
|
|
149 |
$pdata->{type} = 'Linux';
|
|
|
150 |
$pdata->{id} = 83;
|
|
|
151 |
$pstart += $pr_size if ( $in_extended );
|
|
|
152 |
|
|
|
153 |
if ( $pdata->{size} eq 'rest' )
|
|
|
154 |
{
|
|
|
155 |
Error ("Partition Table setup. The 'rest' keyword can only be used once") if ( $rest );
|
|
|
156 |
$pdata->{size} = ($disk_sectors - $pstart) / 2048;
|
|
|
157 |
$rest = 1;
|
|
|
158 |
}
|
|
|
159 |
|
|
|
160 |
$pdata->{start} = $pstart;
|
|
|
161 |
$pdata->{end} = $pstart + ( $pdata->{size} * 1024 * 2);
|
|
|
162 |
$pdata->{size} = $pdata->{end} - $pdata->{start};
|
|
|
163 |
$pstart = $pdata->{end};
|
|
|
164 |
}
|
|
|
165 |
}
|
|
|
166 |
|
|
|
167 |
#
|
|
|
168 |
# Display calculated partition sizes
|
|
|
169 |
#
|
|
|
170 |
foreach my $partition ( sort keys %partitions )
|
|
|
171 |
{
|
|
|
172 |
next unless ( $partition );
|
|
|
173 |
my $pdata = $partitions{$partition};
|
|
|
174 |
my $name = $pdata->{name} || "";
|
|
|
175 |
|
|
|
176 |
printf "Partition-%d (%10s): Sector Start=%10d, End=%10d, Sector Size=%10d(~%5dM), Id=%s\n",
|
|
|
177 |
$partition,
|
|
|
178 |
$name,
|
|
|
179 |
$pdata->{start},
|
|
|
180 |
$pdata->{end},
|
|
|
181 |
$pdata->{size},
|
|
|
182 |
$pdata->{size}/2/1024,
|
|
|
183 |
$pdata->{id};
|
|
|
184 |
}
|
|
|
185 |
|
|
|
186 |
Message ("Creating Empty SD Image");
|
|
|
187 |
System ('dd', 'if=/dev/zero', "of=$disk_image", 'bs=1024', "count=$disk_blocks" );
|
|
|
188 |
|
|
|
189 |
Message ("Creating Partition Table");
|
|
|
190 |
open (SFDISK, "| /sbin/sfdisk -f -L $disk_image" ) || Error ("Cannot invoke sfdisk");
|
|
|
191 |
print SFDISK "unit: sectors\n\n";
|
|
|
192 |
foreach my $partition ( sort keys %partitions )
|
|
|
193 |
{
|
|
|
194 |
next unless ( $partition );
|
|
|
195 |
my $pdata = $partitions{$partition};
|
|
|
196 |
printf SFDISK "Partition-%d: start=%10d, size=%10d, Id=%s\n",
|
|
|
197 |
$partition,
|
|
|
198 |
$pdata->{start},
|
|
|
199 |
$pdata->{size},
|
|
|
200 |
$pdata->{id};
|
|
|
201 |
}
|
|
|
202 |
close (SFDISK);
|
|
|
203 |
|
|
|
204 |
################################################################################
|
|
|
205 |
#
|
|
|
206 |
# Split the complete file system image into required partitions
|
|
|
207 |
# Once the partition has been created it will be removed from the image
|
|
|
208 |
# What is left is the root file system - the final partition
|
|
|
209 |
#
|
|
|
210 |
# Note: Size values are taken after the SD card has been partitioned
|
|
|
211 |
# The partioning process wants to place partitions on cylinder
|
|
|
212 |
# boundaries. This is why the number are not nice powers of two or 10
|
|
|
213 |
#
|
|
|
214 |
# Partition logic:
|
|
|
215 |
# P1 root - 30Meg
|
|
|
216 |
# P2 log - 20Meg Mounted as /var/log)
|
|
|
217 |
# P3 afc - 150Meg
|
|
|
218 |
# P4 var - Rest (~290 Meg)
|
|
|
219 |
#
|
|
|
220 |
#
|
|
|
221 |
# Note: Order is important, as dir tree will be pruned
|
|
|
222 |
# Be smart and determine order to create_fs
|
|
|
223 |
#
|
|
|
224 |
foreach my $partition ( CalcPartitionOrder() )
|
|
|
225 |
{
|
|
|
226 |
create_fs ($WORK, $BUILD, $partition);
|
|
|
227 |
}
|
|
|
228 |
|
|
|
229 |
#
|
|
|
230 |
# Insert the images into the SD image
|
|
|
231 |
#
|
|
|
232 |
foreach my $partition ( sort keys %partitions )
|
|
|
233 |
{
|
|
|
234 |
next unless ( $partition );
|
|
|
235 |
my $pdata = $partitions{$partition};
|
|
|
236 |
next unless ( defined $pdata->{fsname} );
|
|
|
237 |
|
|
|
238 |
Message ("Insert filesystem $pdata->{name} into SD Image");
|
|
|
239 |
my $cmd = "dd if=$pdata->{fsname} of=$disk_image bs=512 conv=notrunc seek=$pdata->{start} count=$pdata->{size}";
|
|
|
240 |
System ($cmd);
|
|
|
241 |
}
|
|
|
242 |
|
|
|
243 |
################################################################################
|
|
|
244 |
#
|
|
|
245 |
# Install grub into the MBR of the file system [ optional]
|
|
|
246 |
# This is only required for an x86 target and will detected by the
|
|
|
247 |
# presence of a key grub file
|
|
|
248 |
# The grub files will have been copied into the filesystem image earlier
|
|
|
249 |
#
|
| 1107 |
dpurdie |
250 |
if ( $install_grub )
|
| 1105 |
dpurdie |
251 |
{
|
|
|
252 |
Message ("Installing Grub into the MBR");
|
| 1107 |
dpurdie |
253 |
|
| 1105 |
dpurdie |
254 |
#
|
|
|
255 |
# Need to run a copy of grab (built for the build machine) on the image
|
|
|
256 |
#
|
|
|
257 |
# Need a command file to feed into grub
|
|
|
258 |
# Prevent grub from scanning bios
|
|
|
259 |
#
|
|
|
260 |
my $map_file = "$BUILD/device_map";
|
|
|
261 |
open (GRUB, ">$map_file" ) || Error ("Cannot create grub device map file: $map_file");
|
|
|
262 |
print GRUB "(hd0) $disk_image\n";
|
|
|
263 |
close (GRUB);
|
|
|
264 |
|
|
|
265 |
#
|
|
|
266 |
# Run grub and pipe in commands
|
|
|
267 |
#
|
|
|
268 |
open (GRUB, "| grub --device-map=$map_file --batch" ) || Error ("Cannot invoke GRUB");
|
|
|
269 |
print GRUB "device (hd0) $disk_image\n";
|
|
|
270 |
print GRUB "root (hd0,0)\n";
|
|
|
271 |
print GRUB "setup (hd0)\n";
|
|
|
272 |
close (GRUB);
|
|
|
273 |
|
|
|
274 |
#
|
|
|
275 |
# Cleanup temp files
|
|
|
276 |
#
|
|
|
277 |
unlink $map_file;
|
|
|
278 |
}
|
|
|
279 |
|
|
|
280 |
################################################################################
|
|
|
281 |
#
|
|
|
282 |
# Package up the file system images
|
|
|
283 |
# Transfer the required build artifacts directly into the package
|
|
|
284 |
#
|
|
|
285 |
# This images are large. They are stored in a gzip form to compress them
|
|
|
286 |
#
|
|
|
287 |
# Output to disk as:
|
|
|
288 |
# gunzip -c rootfs.ext3.gz | dd of=/dev/sdb1 bs=10K
|
|
|
289 |
#
|
|
|
290 |
mkpath( $outdir, 0, 0775);
|
|
|
291 |
Message ("Package up the (compressed) filesystem images");
|
|
|
292 |
my %package_list;
|
|
|
293 |
|
|
|
294 |
#
|
|
|
295 |
# Complete image
|
|
|
296 |
#
|
|
|
297 |
my $ofile = "$outdir/${image_name}.gz";
|
|
|
298 |
$package_list{$ofile}{type} = 'image';
|
|
|
299 |
System ("gzip -c $disk_image > $ofile");
|
|
|
300 |
|
|
|
301 |
# #
|
|
|
302 |
# # Create test images of the internal partitions
|
|
|
303 |
# #
|
|
|
304 |
# mkpath( "$outdir/test", 0, 0775);
|
|
|
305 |
# foreach my $partition ( sort keys %partitions )
|
|
|
306 |
# {
|
|
|
307 |
# my $pdata = $partitions{$partition};
|
|
|
308 |
# next unless ( defined $pdata->{fsname} );
|
|
|
309 |
#
|
|
|
310 |
# my $file = $pdata->{fsname} ;
|
|
|
311 |
# my $ofile = StripDir($file) ;
|
|
|
312 |
# $ofile = "$outdir/test/${ofile}.gz";
|
|
|
313 |
# $package_list{$ofile}{type} = 'ext3';
|
|
|
314 |
# $package_list{$ofile}{partition} = $partition;
|
|
|
315 |
# System ("gzip -c $file > $ofile");
|
|
|
316 |
# }
|
|
|
317 |
#
|
|
|
318 |
#
|
|
|
319 |
# Useful information for testing
|
|
|
320 |
#
|
|
|
321 |
Message ("Useful load commands for testing");
|
|
|
322 |
foreach my $entry ( sort keys %package_list )
|
|
|
323 |
{
|
|
|
324 |
next unless ( defined $package_list{$entry}{partition} );
|
|
|
325 |
|
|
|
326 |
my $ifname = AbsPath( $entry );
|
|
|
327 |
my $ofname = "/dev/sdb" . $package_list{$entry}{partition} ;
|
|
|
328 |
|
|
|
329 |
my $cmd = "gunzip -c $ifname | dd bs=1M of=$ofname";
|
|
|
330 |
Message $cmd;
|
|
|
331 |
}
|
|
|
332 |
|
|
|
333 |
foreach my $entry ( sort keys %package_list )
|
|
|
334 |
{
|
|
|
335 |
next if ( defined $package_list{$entry}{partition} );
|
|
|
336 |
|
|
|
337 |
my $ifname = AbsPath( $entry );
|
|
|
338 |
my $ofname = "/dev/sdb";
|
|
|
339 |
|
|
|
340 |
my $cmd = "gunzip -c $ifname | dd bs=1M of=$ofname";
|
|
|
341 |
Message $cmd;
|
|
|
342 |
}
|
|
|
343 |
}
|
|
|
344 |
|
|
|
345 |
#-------------------------------------------------------------------------------
|
|
|
346 |
# Function : create_fs
|
|
|
347 |
#
|
|
|
348 |
# Description : Create the ext3 image of a given partition
|
|
|
349 |
#
|
|
|
350 |
# Inputs : $WORK - Root of data
|
|
|
351 |
# $BUILD - Scratch Area
|
|
|
352 |
# $FS_PART - Partition number
|
|
|
353 |
#
|
|
|
354 |
# Returns :
|
|
|
355 |
#
|
|
|
356 |
sub create_fs
|
|
|
357 |
{
|
|
|
358 |
my ($WORK, $BUILD, $FS_PART) = @_;
|
|
|
359 |
my $pdata = $partitions{$FS_PART} || Error ("Unknown partition number: $FS_PART");
|
|
|
360 |
my $FS_IMAGE = $pdata->{name};
|
|
|
361 |
my $FS_BASE = $pdata->{root};
|
|
|
362 |
my $FS_SIZE = $pdata->{size} / 2;
|
|
|
363 |
|
| 1107 |
dpurdie |
364 |
#
|
|
|
365 |
# The 'full' image may not be required
|
|
|
366 |
# It will have a zero size
|
|
|
367 |
#
|
|
|
368 |
return unless ( $FS_SIZE );
|
|
|
369 |
|
| 1105 |
dpurdie |
370 |
Message "Creating EXT3 file system: ${FS_IMAGE}, Partition: ${FS_PART}, Size: ${FS_SIZE} blocks";
|
|
|
371 |
|
|
|
372 |
$FS_IMAGE = "${BUILD}/${FS_IMAGE}.ext3";
|
|
|
373 |
$pdata->{fsname} = $FS_IMAGE;
|
|
|
374 |
unlink ${FS_IMAGE};
|
|
|
375 |
|
|
|
376 |
my $cmd = "e2fsimage -f ${FS_IMAGE} -d ${WORK}${FS_BASE} -u 0 -g 0 -s ${FS_SIZE}";
|
|
|
377 |
$cmd .= " -v " if ( IsVerbose(1) );
|
|
|
378 |
Verbose $cmd;
|
|
|
379 |
open (E2FSIMAGE, "$cmd |" ) || Error ("Can't invoke e2fsimage");
|
|
|
380 |
while ( <E2FSIMAGE> )
|
|
|
381 |
{
|
|
|
382 |
chomp;
|
|
|
383 |
Verbose (" $_");
|
|
|
384 |
}
|
|
|
385 |
close (E2FSIMAGE);
|
|
|
386 |
|
|
|
387 |
$cmd = "/sbin/tune2fs -j -c 10 -i 5d ${FS_IMAGE}";
|
|
|
388 |
Verbose $cmd;
|
|
|
389 |
open (E2FSIMAGE, "$cmd |" ) || Error ("Can't invoke tunefs");
|
|
|
390 |
while ( <E2FSIMAGE> )
|
|
|
391 |
{
|
|
|
392 |
chomp;
|
|
|
393 |
Verbose (" $_");
|
|
|
394 |
}
|
|
|
395 |
close (E2FSIMAGE);
|
|
|
396 |
|
|
|
397 |
#
|
|
|
398 |
# Clean the subtree that has been encapsulated in the partition
|
|
|
399 |
# Leave the mountpoint
|
|
|
400 |
#
|
|
|
401 |
unless ( $pdata->{noprune} )
|
|
|
402 |
{
|
|
|
403 |
Verbose ("Cleaning partition data - leave mount point", $pdata->{root});
|
|
|
404 |
foreach my $file ( glob("${WORK}/${FS_BASE}/*") )
|
|
|
405 |
{
|
|
|
406 |
if ( -d $file )
|
|
|
407 |
{
|
|
|
408 |
rmtree( $file );
|
|
|
409 |
}
|
|
|
410 |
else
|
|
|
411 |
{
|
|
|
412 |
unlink ($file);
|
|
|
413 |
}
|
|
|
414 |
}
|
|
|
415 |
}
|
|
|
416 |
}
|
|
|
417 |
|
|
|
418 |
#-------------------------------------------------------------------------------
|
|
|
419 |
# Function : CalcPartitionOrder
|
|
|
420 |
#
|
|
|
421 |
# Description : Determine the order in which we need to create the partitions
|
|
|
422 |
# Simple: if 'A' is a subset of 'B' then 'A' must be done first
|
|
|
423 |
#
|
|
|
424 |
# Inputs :
|
|
|
425 |
#
|
|
|
426 |
# Returns :
|
|
|
427 |
#
|
|
|
428 |
sub CalcPartitionOrder
|
|
|
429 |
{
|
|
|
430 |
my %pdata;
|
|
|
431 |
my @porder;
|
|
|
432 |
|
|
|
433 |
#
|
|
|
434 |
# Examine all partitions
|
|
|
435 |
# Partition-0: process first, don't prune
|
|
|
436 |
# Ignore extended partions. The only affect the partition table
|
|
|
437 |
#
|
|
|
438 |
foreach my $partition ( sort keys %partitions )
|
|
|
439 |
{
|
|
|
440 |
if ( $partition == 0 )
|
|
|
441 |
{
|
|
|
442 |
push @porder, $partition;
|
|
|
443 |
$partitions{$partition}{noprune} = 1;
|
|
|
444 |
next;
|
|
|
445 |
}
|
|
|
446 |
next if ( ($partitions{$partition}{type} || '') eq 'extended' );
|
|
|
447 |
$pdata{$partitions{$partition}{root}} = $partition;
|
|
|
448 |
}
|
|
|
449 |
|
|
|
450 |
#
|
|
|
451 |
# Now we have the names of the partions
|
|
|
452 |
# Reverse Order
|
|
|
453 |
#
|
|
|
454 |
foreach my $name ( reverse sort keys %pdata )
|
|
|
455 |
{
|
|
|
456 |
push @porder, $pdata{$name};
|
|
|
457 |
}
|
|
|
458 |
|
|
|
459 |
#
|
|
|
460 |
# Mark the last partition as non-prune
|
|
|
461 |
#
|
|
|
462 |
my $last = $porder[$#porder];
|
|
|
463 |
$partitions{$last}{noprune} = 1;
|
|
|
464 |
|
|
|
465 |
#
|
|
|
466 |
# Return the order in which partitions MUST be pruned
|
|
|
467 |
#
|
|
|
468 |
return @porder;
|
|
|
469 |
}
|
|
|
470 |
|