Subversion Repositories DevTools

Rev

Rev 1105 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
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