def test_write_value_at_offset(self): image = Image(self.img, MiB(2)) image.write_value_at_offset(801, 130031) # Now open the path independently, seek to the given offset, and read # 4 bytes, then interpret it as a little-endian 32-bit integer. with open(image.path, 'rb') as fp: fp.seek(130031) # Unpack always returns a tuple, but there's only one item there. value, *ignore = unpack('<I', fp.read(4)) self.assertEqual(value, 801)
def test_write_value_at_offset(self): image = Image(self.img, MiB(2)) image.write_value_at_offset(801, 130031) # Now open the path independently, seek to the given offset, and read # 4 bytes, then interpret it as a little-endian 32-bit integer. with open(image.path, 'rb') as fp: fp.seek(130031) # Unpack always returns a tuple, but there's only one item there. value, *ignore = unpack('<I', fp.read(4)) self.assertEqual(value, 801)
def test_write_value_at_offsets_near_end(self): image = Image(self.img, 10000) # Attempt to write a bunch of values near the end of the file. Since # the value will always be a 32-bit value, any positions farther out # than 4 bytes before the end will fail. results = set() for pos in range(9995, 10002): with suppress(ValueError): image.write_value_at_offset(801, pos) self.assertEqual(os.path.getsize(self.img), 10000) results.add(pos) self.assertEqual(results, {9995, 9996})
def test_write_value_at_offsets_near_end(self): image = Image(self.img, 10000) # Attempt to write a bunch of values near the end of the file. Since # the value will always be a 32-bit value, any positions farther out # than 4 bytes before the end will fail. results = set() for pos in range(9995, 10002): with suppress(ValueError): image.write_value_at_offset(801, pos) self.assertEqual(os.path.getsize(self.img), 10000) results.add(pos) self.assertEqual(results, {9995, 9996})
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 _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)