def test_set_partition_type_gpt(self): image = Image(self.img, MiB(6), VolumeSchema.gpt) image.partition(offset=MiB(1), size=MiB(1)) self.assertEqual(len(image.disk.partitions), 1) image.set_parition_type(1, '21686148-6449-6E6F-744E-656564454649') disk_info = image.diagnostics() self.assertEqual(disk_info['partitiontable']['partitions'][0]['type'], '21686148-6449-6E6F-744E-656564454649') image.set_parition_type(1, '00000000-0000-0000-0000-0000DEADBEEF') disk_info = image.diagnostics() self.assertEqual(disk_info['partitiontable']['partitions'][0]['type'], '00000000-0000-0000-0000-0000DEADBEEF')
def test_set_partition_type_mbr(self): image = Image(self.img, MiB(6), VolumeSchema.mbr) image.partition(offset=MiB(1), size=MiB(1)) self.assertEqual(len(image.disk.partitions), 1) image.set_parition_type(1, '83') disk_info = image.diagnostics() self.assertEqual(disk_info['partitiontable']['partitions'][0]['type'], '83') image.set_parition_type(1, 'da') disk_info = image.diagnostics() self.assertEqual(disk_info['partitiontable']['partitions'][0]['type'], 'da')
def test_small_partition_size_and_offset(self): # LP: #1630709 - structure parts with size and offset < 1MB. image = Image(self.img, MiB(2), VolumeSchema.mbr) image.partition(offset=256, size=512) disk_info = image.diagnostics() # Even though the offset and size are set at 256 bytes and 512 bytes # respectively, the minimum granularity is one sector (i.e. 512 # bytes). The start and size returned by diagnostics() are in sector # units. self.assertEqual(disk_info['partitiontable']['partitions'][0]['start'], 1) self.assertEqual(disk_info['partitiontable']['partitions'][0]['size'], 1)
def _make_one_disk(self, imgfile, name, volume): part_id = 1 # Create the image object for the selected volume schema image = Image(imgfile, volume.image_size, volume.schema) offset_writes = [] part_offsets = {} # We first create all the needed partitions. # For regular core16 and core18 builds, this means creating all of the # defined partitions. For core20 (the so called 'seeded images'), we # only create all the role-less partitions and mbr + system-seed. # The rest is created dynamically by snapd on first boot. for part in volume.structures: if part.name is not None: part_offsets[part.name] = part.offset if part.offset_write is not None: offset_writes.append((part.offset, part.offset_write)) if (part.role is StructureRole.mbr or part.type == 'bare' or self._should_skip_partition(part)): continue activate = False if (volume.schema is VolumeSchema.mbr and part.role is StructureRole.system_boot): activate = True elif (volume.schema is VolumeSchema.gpt and part.role is StructureRole.system_data and part.name is None): part.name = 'writable' image.partition(part.offset, part.size, part.name, activate) # Now since we're done, we need to do a second pass to copy the data # and set all the partition types. This needs to be done like this as # libparted's commit() operation resets type GUIDs to defaults and # clobbers things like hybrid MBR partitions. part_id = 1 for i, part in enumerate(volume.structures): if self._should_skip_partition(part): continue image.copy_blob(volume.part_images[i], bs=image.sector_size, seek=part.offset // image.sector_size, count=ceil(part.size / image.sector_size), conv='notrunc') if part.role is StructureRole.mbr or part.type == 'bare': continue image.set_parition_type(part_id, part.type) part_id += 1 for value, dest in offset_writes: # Decipher non-numeric offset_write values. if isinstance(dest, tuple): dest = part_offsets[dest[0]] + dest[1] image.write_value_at_offset(value // image.sector_size, dest)
def test_small_partition_size_and_offset(self): # LP: #1630709 - structure parts with size and offset < 1MB. image = Image(self.img, MiB(2), VolumeSchema.mbr) image.partition(offset=256, size=512) disk_info = image.diagnostics() # Even though the offset and size are set at 256 bytes and 512 bytes # respectively, the minimum granularity is one sector (i.e. 512 # bytes). The start and size returned by diagnostics() are in sector # units. self.assertEqual( disk_info['partitiontable']['partitions'][0]['start'], 1) self.assertEqual( disk_info['partitiontable']['partitions'][0]['size'], 1)
def test_mbr_image_partitions(self): image = Image(self.img, MiB(2), VolumeSchema.mbr) # Create the first partition. image.partition(offset=image.sector(33), size=image.sector(3000), is_bootable=True) self.assertEqual(len(image.disk.partitions), 1) # Append the next one. image.partition(offset=image.sector(3033), size=image.sector(1000)) self.assertEqual(len(image.disk.partitions), 2) image.set_parition_type(1, '83') image.set_parition_type(2, 'dd') disk_info = image.diagnostics() partitions = disk_info['partitiontable'] # The device id is unpredictable. partitions.pop('id') # XXX: In later versions of pyparted the partitiontable structure # added a 'grain' entry that we're not really interested in. # Remove it so we can have the tests working for all series. if 'grain' in partitions: partitions.pop('grain') # Newer sfdisk displays an additional field of 'sectorsize' that # we're not really interested in. partitions.pop('sectorsize', None) self.assertEqual( partitions, { 'label': 'dos', 'device': self.img, 'unit': 'sectors', 'partitions': [{ 'node': '{}1'.format(self.img), 'start': 33, 'size': 3000, 'type': '83', 'bootable': True, }, { 'node': '{}2'.format(self.img), 'start': 3033, 'size': 1000, 'type': 'dd', }], })
def test_gpt_image_partitions(self): image = Image(self.img, MiB(10), VolumeSchema.gpt) image.partition(offset=MiB(4), size=MiB(1), name='grub') self.assertEqual(len(image.disk.partitions), 1) image.partition(offset=MiB(5), size=MiB(4)) self.assertEqual(len(image.disk.partitions), 2) image.set_parition_type(1, '21686148-6449-6E6F-744E-656564454649') image.set_parition_type(2, '0FC63DAF-8483-4772-8E79-3D69D8477DE4') # Use an external tool for checking the partition table to be sure # that it's indeed correct as suspected. disk_info = image.diagnostics() partitions = disk_info['partitiontable'] # The device id is unpredictable. partitions.pop('id') # Newer sfdisk displays an additional field of 'sectorsize' that # we're not really interested in. partitions.pop('sectorsize', None) # The partition uuids as well. [p.pop('uuid') for p in partitions['partitions']] self.maxDiff = None self.assertEqual( partitions, { 'label': 'gpt', 'device': self.img, 'unit': 'sectors', 'firstlba': 34, 'lastlba': 20446, 'partitions': [{ 'node': '{}1'.format(self.img), 'start': 8192, 'size': 2048, 'type': '21686148-6449-6E6F-744E-656564454649', 'name': 'grub', }, { 'node': '{}2'.format(self.img), 'start': 10240, 'size': 8192, 'type': '0FC63DAF-8483-4772-8E79-3D69D8477DE4', }], })
def _make_one_disk(self, imgfile, name, volume): part_id = 1 # Create the image object for the selected volume schema image = Image(imgfile, volume.image_size, volume.schema) offset_writes = [] part_offsets = {} # We first create all the partitions. for part in volume.structures: if part.name is not None: part_offsets[part.name] = part.offset if part.offset_write is not None: offset_writes.append((part.offset, part.offset_write)) if part.role is StructureRole.mbr or part.type == 'bare': continue activate = False if (volume.schema is VolumeSchema.mbr and part.role is StructureRole.system_boot): activate = True elif (volume.schema is VolumeSchema.gpt and part.role is StructureRole.system_data and part.name is None): part.name = 'writable' image.partition(part.offset, part.size, part.name, activate) # Now since we're done, we need to do a second pass to copy the data # and set all the partition types. This needs to be done like this as # libparted's commit() operation resets type GUIDs to defaults and # clobbers things like hybrid MBR partitions. part_id = 1 for i, part in enumerate(volume.structures): image.copy_blob(volume.part_images[i], bs=image.sector_size, seek=part.offset // image.sector_size, count=ceil(part.size / image.sector_size), conv='notrunc') if part.role is StructureRole.mbr or part.type == 'bare': continue image.set_parition_type(part_id, part.type) part_id += 1 for value, dest in offset_writes: # Decipher non-numeric offset_write values. if isinstance(dest, tuple): dest = part_offsets[dest[0]] + dest[1] image.write_value_at_offset(value // image.sector_size, dest)
def test_mbr_image_partitions(self): image = Image(self.img, MiB(2), VolumeSchema.mbr) # Create the first partition. image.partition(offset=image.sector(33), size=image.sector(3000), is_bootable=True) self.assertEqual(len(image.disk.partitions), 1) # Append the next one. image.partition(offset=image.sector(3033), size=image.sector(1000)) self.assertEqual(len(image.disk.partitions), 2) image.set_parition_type(1, '83') image.set_parition_type(2, 'dd') disk_info = image.diagnostics() partitions = disk_info['partitiontable'] # The device id is unpredictable. partitions.pop('id') # XXX: In later versions of pyparted the partitiontable structure # added a 'grain' entry that we're not really interested in. # Remove it so we can have the tests working for all series. if 'grain' in partitions: partitions.pop('grain') self.assertEqual(partitions, { 'label': 'dos', 'device': self.img, 'unit': 'sectors', 'partitions': [{ 'node': '{}1'.format(self.img), 'start': 33, 'size': 3000, 'type': '83', 'bootable': True, }, { 'node': '{}2'.format(self.img), 'start': 3033, 'size': 1000, 'type': 'dd', }], })
def test_mbr_image_partitions(self): image = Image(self.img, MiB(2), VolumeSchema.mbr) # Create the first partition. image.partition(offset=image.sector(33), size=image.sector(3000), is_bootable=True) self.assertEqual(len(image.disk.partitions), 1) # Append the next one. image.partition(offset=image.sector(3033), size=image.sector(1000)) self.assertEqual(len(image.disk.partitions), 2) image.set_parition_type(1, '83') image.set_parition_type(2, 'dd') disk_info = image.diagnostics() partitions = disk_info['partitiontable'] # The device id is unpredictable. partitions.pop('id') self.assertEqual( partitions, { 'label': 'dos', 'device': self.img, 'unit': 'sectors', 'partitions': [{ 'node': '{}1'.format(self.img), 'start': 33, 'size': 3000, 'type': '83', 'bootable': True, }, { 'node': '{}2'.format(self.img), 'start': 3033, 'size': 1000, 'type': 'dd', }], })
def test_gpt_image_partitions(self): image = Image(self.img, MiB(10), VolumeSchema.gpt) image.partition(offset=MiB(4), size=MiB(1), name='grub') self.assertEqual(len(image.disk.partitions), 1) image.partition(offset=MiB(5), size=MiB(4)) self.assertEqual(len(image.disk.partitions), 2) image.set_parition_type(1, '21686148-6449-6E6F-744E-656564454649') image.set_parition_type(2, '0FC63DAF-8483-4772-8E79-3D69D8477DE4') # Use an external tool for checking the partition table to be sure # that it's indeed correct as suspected. disk_info = image.diagnostics() partitions = disk_info['partitiontable'] # The device id is unpredictable. partitions.pop('id') # The partition uuids as well. [p.pop('uuid') for p in partitions['partitions']] self.maxDiff = None self.assertEqual(partitions, { 'label': 'gpt', 'device': self.img, 'unit': 'sectors', 'firstlba': 34, 'lastlba': 20446, 'partitions': [{ 'node': '{}1'.format(self.img), 'start': 8192, 'size': 2048, 'type': '21686148-6449-6E6F-744E-656564454649', 'name': 'grub', }, { 'node': '{}2'.format(self.img), 'start': 10240, 'size': 8192, 'type': '0FC63DAF-8483-4772-8E79-3D69D8477DE4', }], })
def test_partition(self): # Create BIOS boot partition # # The partition is 1MiB in size, as recommended by various # partitioning guides. The actual required size is much, much # smaller. image = Image(self.img, MiB(10)) image.partition(new='1:4MiB:+1MiB') image.partition(typecode='1:21686148-6449-6E6F-744E-656564454649') image.partition(change_name='1:grub') mbr = image.diagnostics(Diagnostics.mbr) # We should see that the disk size is 10MiB. self.assertRegex(mbr, '10.0 MiB') gpt = image.diagnostics(Diagnostics.gpt) # We should see that there is 1 partition named grub. self.assertRegex(gpt, 'grub')
image = Image('img', GiB(4)) # Install GRUB to MBR # TODO: this has to be represented in the image.yaml # NOTE: the boot.img has to be a part of the gadget snap itself # FIXME: embed a pointer to 2nd stage in bios-boot partition image.copy_blob('blogs/img.mbr', bs=446, count=1, conv='notrunc') # Create BIOS boot partition # # The partition is 1MiB in size, as recommended by various partitioning guides. # The actual required size is much, much smaller. # # https://www.gnu.org/software/grub/manual/html_node/BIOS-installation.html#BIOS-installation image.partition(new='1:4MiB:+1MiB') image.partition(typecode='1:21686148-6449-6E6F-744E-656564454649') image.partition(change_name='1:grub') image.copy_blob('blobs/img.bios-boot', bs='1MiB', seek=4, count=1, conv='notrunc') # Create EFI system partition # # TODO: switch to 512MiB as recommended by the standard image.partition(new='2:5MiB:+64MiB') image.partition(typecode='2:C12A7328-F81F-11D2-BA4B-00A0C93EC93B') image.partition(change_name='2:system-boot') image.copy_blob('blobs/img.system-boot',
def make_disk(self): self.disk_img = os.path.join(self.images, 'disk.img') image = Image(self.disk_img, GiB(4)) # Create BIOS boot partition # # The partition is 1MiB in size, as recommended by various # partitioning guides. The actual required size is much, much # smaller. # # https://www.gnu.org/software/grub/manual/html_node/BIOS-installation.html#BIOS-installation # image.partition(new='1:4MiB:+1MiB') # image.partition(typecode='1:21686148-6449-6E6F-744E-656564454649') # image.partition(change_name='1:grub') # image.copy_blob(self.boot_img, # bs='1MiB', seek=4, count=1, conv='notrunc') # Create EFI system partition # # TODO: switch to 512MiB as recommended by the standard image.partition(new='2:5MiB:+64MiB') image.partition(typecode='2:C12A7328-F81F-11D2-BA4B-00A0C93EC93B') image.partition(change_name='2:system-boot') image.copy_blob(self.boot_img, bs='1MB', seek=4, count=64, conv='notrunc') # Create main snappy writable partition image.partition(new='3:72MiB:+3646MiB') image.partition(typecode='3:0FC63DAF-8483-4772-8E79-3D69D8477DE4') image.partition(change_name='3:writable') image.copy_blob(self.root_img, bs='1MiB', seek=72, count=3646, conv='notrunc') self._next.append(self.finish)
image = Image('img', GiB(4)) # Install GRUB to MBR # TODO: this has to be represented in the image.yaml # NOTE: the boot.img has to be a part of the gadget snap itself # FIXME: embed a pointer to 2nd stage in bios-boot partition image.copy_blob('blogs/img.mbr', bs=446, count=1, conv='notrunc') # Create BIOS boot partition # # The partition is 1MiB in size, as recommended by various partitioning guides. # The actual required size is much, much smaller. # # https://www.gnu.org/software/grub/manual/html_node/BIOS-installation.html#BIOS-installation image.partition(new='1:4MiB:+1MiB') image.partition(typecode='1:21686148-6449-6E6F-744E-656564454649') image.partition(change_name='1:grub') image.copy_blob('blobs/img.bios-boot', bs='1MiB', seek=4, count=1, conv='notrunc') # Create EFI system partition # # TODO: switch to 512MiB as recommended by the standard image.partition(new='2:5MiB:+64MiB') image.partition(typecode='2:C12A7328-F81F-11D2-BA4B-00A0C93EC93B') image.partition(change_name='2:system-boot') image.copy_blob('blobs/img.system-boot', bs='1MB', seek=4, count=64, conv='notrunc') # Create main snappy writable partition