=== modified file 'linaro-android-media-create'
@@ -118,9 +118,8 @@
if media.is_block_device:
if not confirm_device_selection_and_ensure_it_is_ready(args.device):
sys.exit(1)
- elif not args.should_format_rootfs or not args.should_format_bootfs:
- print ("Do not use --no-boot or --no-part in conjunction with "
- "--image_file.")
+ elif not args.should_create_partitions:
+ print ("Do not use --no-part in conjunction with --image_file.")
sys.exit(1)
else:
# All good, move on.
@@ -137,8 +136,8 @@
# Create partitions
boot_partition, system_partition, cache_partition, \
data_partition, sdcard_partition = setup_android_partitions( \
- board_config, media, args.boot_label, args.should_create_partitions,
- args.should_align_boot_part)
+ board_config, media, args.image_size, args.boot_label,
+ args.should_create_partitions, args.should_align_boot_part)
populate_partition(BOOT_DIR + "/boot", BOOT_DISK, boot_partition)
board_config.populate_boot_script(boot_partition, BOOT_DISK, args.consoles)
=== modified file 'linaro_image_tools/media_create/__init__.py'
@@ -124,8 +124,16 @@
def get_android_args_parser():
"""Get the ArgumentParser for the arguments given on the command line."""
parser = argparse.ArgumentParser()
+ group = parser.add_mutually_exclusive_group(required=True)
+ group.add_argument(
+ '--mmc', dest='device', help='The storage device to use.')
+ group.add_argument(
+ '--image_file', dest='device',
+ help='File where we should write the image file.')
parser.add_argument(
- '--mmc', required=True, dest='device', help='The storage device to use.')
+ '--image_size', default='2G',
+ help=('The image size, specified in mega/giga bytes (e.g. 3000M or '
+ '3G); use with --image_file only'))
parser.add_argument(
'--dev', required=True, dest='board', choices=ANDROID_KNOWN_BOARDS,
help='Generate an SD card or image for the given board.')
=== modified file 'linaro_image_tools/media_create/partitions.py'
@@ -41,22 +41,39 @@
UDISKS = "org.freedesktop.UDisks"
-def setup_android_partitions(board_config, media, bootfs_label,
+def setup_android_partitions(board_config, media, image_size, bootfs_label,
should_create_partitions, should_align_boot_part=False):
cylinders = None
+ if not media.is_block_device:
+ image_size_in_bytes = convert_size_to_bytes(image_size)
+ cylinders = image_size_in_bytes / CYLINDER_SIZE
+ proc = cmd_runner.run(
+ ['dd', 'of=%s' % media.path,
+ 'bs=1', 'seek=%s' % image_size_in_bytes, 'count=0'],
+ stderr=open('/dev/null', 'w'))
+ proc.wait()
if should_create_partitions:
create_partitions(
board_config, media, HEADS, SECTORS, cylinders,
should_align_boot_part=should_align_boot_part)
- bootfs, system, cache, data, sdcard = \
- get_android_partitions_for_media (media, board_config)
- ensure_partition_is_not_mounted(bootfs)
- ensure_partition_is_not_mounted(system)
- ensure_partition_is_not_mounted(cache)
- ensure_partition_is_not_mounted(data)
- ensure_partition_is_not_mounted(sdcard)
+ if media.is_block_device:
+ bootfs, system, cache, data, sdcard = \
+ get_android_partitions_for_media (media, board_config)
+ ensure_partition_is_not_mounted(bootfs)
+ ensure_partition_is_not_mounted(system)
+ ensure_partition_is_not_mounted(cache)
+ ensure_partition_is_not_mounted(data)
+ ensure_partition_is_not_mounted(sdcard)
+ else:
+ partitions = get_android_loopback_devices(media.path)
+ bootfs = partitions[0]
+ system = partitions[1]
+ cache = partitions[2]
+ data = partitions[4]
+ sdcard = partitions[5]
+
print "\nFormating boot partition\n"
proc = cmd_runner.run(
@@ -195,6 +212,21 @@
return boot_device, root_device
+def get_android_loopback_devices(image_file):
+ """Return the loopback devices for the given image file.
+
+ Assumes a particular order of devices in the file.
+ Register the loopback devices as well.
+ """
+ devices = []
+ device_info = calculate_android_partition_size_and_offset(image_file)
+ for device_offset, device_size in device_info:
+ devices.append(register_loopback(image_file, device_offset,
+ device_size))
+
+ return devices
+
+
def register_loopback(image_file, offset, size):
"""Register a loopback device with an atexit handler to de-register it."""
def undo(device):
@@ -231,8 +263,8 @@
partition.type)
if 'boot' in partition.getFlagsAsString():
geometry = partition.geometry
- vfat_offset = geometry.start * 512
- vfat_size = geometry.length * 512
+ vfat_offset = geometry.start * SECTOR_SIZE
+ vfat_size = geometry.length * SECTOR_SIZE
vfat_partition = partition
elif vfat_partition is not None:
# next partition after boot partition is the root partition
@@ -241,8 +273,8 @@
# iterate disk.partitions which only returns
# parted.PARTITION_NORMAL partitions
geometry = partition.geometry
- linux_offset = geometry.start * 512
- linux_size = geometry.length * 512
+ linux_offset = geometry.start * SECTOR_SIZE
+ linux_size = geometry.length * SECTOR_SIZE
linux_partition = partition
break
@@ -252,6 +284,33 @@
"Couldn't find root partition on %s" % image_file)
return vfat_size, vfat_offset, linux_size, linux_offset
+
+def calculate_android_partition_size_and_offset(image_file):
+ """Return the size and offset of the android partitions.
+
+ Both the size and offset are in bytes.
+
+ :param image_file: A string containing the path to the image_file.
+ :return: A list of (offset, size) pairs.
+ """
+ # Here we can use parted.Device to read the partitions because we're
+ # reading from a regular file rather than a block device. If it was a
+ # block device we'd need root rights.
+ disk = Disk(Device(image_file))
+ partition_info = []
+ for partition in disk.partitions:
+ geometry = partition.geometry
+ partition_info.append((geometry.start * SECTOR_SIZE,
+ geometry.length * SECTOR_SIZE))
+ # NB: don't use vfat_partition.nextPartition() as that might return
+ # a partition of type PARTITION_FREESPACE; it's much easier to
+ # iterate disk.partitions which only returns
+ # parted.PARTITION_NORMAL partitions
+
+ assert len(partition_info) == 6
+ return partition_info
+
+
def get_android_partitions_for_media(media, board_config):
"""Return the device files for all the Android partitions of media.
@@ -276,6 +335,14 @@
assert boot_partition is not None, (
"Could not find boot partition for %s" % media.path)
+ assert system_partition is not None, (
+ "Could not find system partition for %s" % media.path)
+ assert cache_partition is not None, (
+ "Could not find cache partition for %s" % media.path)
+ assert data_partition is not None, (
+ "Could not find data partition for %s" % media.path)
+ assert sdcard_partition is not None, (
+ "Could not find sdcard partition for %s" % media.path)
return boot_partition, system_partition, cache_partition, \
data_partition, sdcard_partition
=== modified file 'linaro_image_tools/media_create/tests/test_media_create.py'
@@ -71,10 +71,12 @@
HEADS,
SECTORS,
calculate_partition_size_and_offset,
+ calculate_android_partition_size_and_offset,
convert_size_to_bytes,
create_partitions,
ensure_partition_is_not_mounted,
get_boot_and_root_loopback_devices,
+ get_android_loopback_devices,
get_boot_and_root_partitions_for_media,
Media,
run_sfdisk_commands,
@@ -878,6 +880,20 @@
# Stub time.sleep() as create_partitions() use that.
self.orig_sleep = time.sleep
time.sleep = lambda s: None
+ self.android_image_size = 256 * 1024**2
+ # Extended partition info takes 32 sectors from the first ext partition
+ ext_part_size = 32
+ self.android_offsets_and_sizes = [
+ (63 * SECTOR_SIZE, 32768 * SECTOR_SIZE),
+ (32831 * SECTOR_SIZE, 65536 * SECTOR_SIZE),
+ (98367 * SECTOR_SIZE, 65536 * SECTOR_SIZE),
+ (294975 * SECTOR_SIZE, (self.android_image_size -
+ 294975 * SECTOR_SIZE)),
+ ((294975 + ext_part_size) * SECTOR_SIZE,
+ (131072 - ext_part_size) * SECTOR_SIZE),
+ ((426047 + ext_part_size) * SECTOR_SIZE,
+ self.android_image_size - (426047 + ext_part_size) * SECTOR_SIZE)
+ ]
def tearDown(self):
super(TestPartitionSetup, self).tearDown()
@@ -886,7 +902,13 @@
def _create_tmpfile(self):
# boot part at +8 MiB, root part at +16 MiB
return self._create_qemu_img_with_partitions(
- '16384,15746,0x0C,*\n32768,,,-')
+ '16384,15746,0x0C,*\n32768,,,-', '30M')
+
+ def _create_android_tmpfile(self):
+ # boot, system, cache, (extended), userdata and sdcard partitions
+ return self._create_qemu_img_with_partitions(
+ '63,32768,0x0C,*\n32831,65536,L\n98367,65536,L\n294975,-,E\n' \
+ '294975,131072,L\n426047,,,-', '%s' % self.android_image_size)
def test_convert_size_no_suffix(self):
self.assertEqual(524288, convert_size_to_bytes('524288'))
@@ -908,10 +930,19 @@
[8061952L, 8388608L, 14680064L, 16777216L],
[vfat_size, vfat_offset, linux_size, linux_offset])
+ def test_calculate_android_partition_size_and_offset(self):
+ tmpfile = self._create_android_tmpfile()
+ device_info = calculate_android_partition_size_and_offset(tmpfile)
+ # We use map(None, ...) since it would catch if the lists are not of
+ # equal length and zip() would not in all cases.
+ for device_pair, expected_pair in map(None, device_info,
+ self.android_offsets_and_sizes):
+ self.assertEqual(device_pair, expected_pair)
+
def test_partition_numbering(self):
# another Linux partition at +24 MiB after the boot/root parts
tmpfile = self._create_qemu_img_with_partitions(
- '16384,15746,0x0C,*\n32768,15427,,-\n49152,,,-')
+ '16384,15746,0x0C,*\n32768,15427,,-\n49152,,,-', '30M')
vfat_size, vfat_offset, linux_size, linux_offset = (
calculate_partition_size_and_offset(tmpfile))
# check that the linux partition offset starts at +16 MiB so that it's
@@ -941,10 +972,10 @@
("%s%d" % (tmpfile, 2), "%s%d" % (tmpfile, 3)),
get_boot_and_root_partitions_for_media(media, boards.Mx5Config))
- def _create_qemu_img_with_partitions(self, sfdisk_commands):
+ def _create_qemu_img_with_partitions(self, sfdisk_commands, tempfile_size):
tmpfile = self.createTempFileAsFixture()
proc = cmd_runner.run(
- ['dd', 'of=%s' % tmpfile, 'bs=1', 'seek=30M', 'count=0'],
+ ['dd', 'of=%s' % tmpfile, 'bs=1', 'seek=%s' % tempfile_size, 'count=0'],
stderr=open('/dev/null', 'w'))
proc.communicate()
stdout, stderr = run_sfdisk_commands(
@@ -1000,6 +1031,38 @@
'%s losetup -d ' % sudo_args],
popen_fixture.mock.commands_executed)
+ def test_get_android_loopback_devices(self):
+ tmpfile = self._create_android_tmpfile()
+ atexit_fixture = self.useFixture(MockSomethingFixture(
+ atexit, 'register', AtExitRegister()))
+ popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
+ # We can't test the return value of get_boot_and_root_loopback_devices
+ # because it'd require running losetup as root, so we just make sure
+ # it calls losetup correctly.
+ get_android_loopback_devices(tmpfile)
+ self.assertEqual(
+ ['%s losetup -f --show %s --offset %s --sizelimit %s'
+ % (sudo_args, tmpfile, offset, size) for (offset, size) in
+ self.android_offsets_and_sizes],
+ popen_fixture.mock.commands_executed)
+
+ # get_boot_and_root_loopback_devices will also setup two exit handlers
+ # to de-register the loopback devices set up above.
+ self.assertEqual(6, len(atexit_fixture.mock.funcs))
+ popen_fixture.mock.calls = []
+ atexit_fixture.mock.run_funcs()
+ # We did not really run losetup above (as it requires root) so here we
+ # don't have a device to pass to 'losetup -d', but when a device is
+ # setup it is passed to the atexit handler.
+ self.assertEquals(
+ ['%s losetup -d ' % sudo_args,
+ '%s losetup -d ' % sudo_args,
+ '%s losetup -d ' % sudo_args,
+ '%s losetup -d ' % sudo_args,
+ '%s losetup -d ' % sudo_args,
+ '%s losetup -d ' % sudo_args],
+ popen_fixture.mock.commands_executed)
+
def test_setup_partitions_for_image_file(self):
# In practice we could pass an empty image file to setup_partitions,
# but here we mock Popen() and thanks to that the image is not setup