def create(self, name, mbsize, type_name, flags=None): """ Create DASD partition :param string name: partition name :param int mbsize: partition size :param string type_name: unused :param list flags: unused """ self.partition_id += 1 fdasd_input = NamedTemporaryFile() with open(fdasd_input.name, 'w') as partition: log.debug('%s: fdasd: n p cur_position +%sM w q', name, format(mbsize)) if mbsize == 'all_free': partition.write('n\np\n\n\nw\nq\n') else: partition.write('n\np\n\n+%dM\nw\nq\n' % mbsize) bash_command = ' '.join( ['cat', fdasd_input.name, '|', 'fdasd', '-f', self.disk_device]) try: Command.run(['bash', '-c', bash_command]) except Exception: # unfortunately fdasd reports that it can't read in the partition # table which I consider a bug in fdasd. However the table was # correctly created and therefore we continue. Problem is that we # are not able to detect real errors with the fdasd operation at # that point. log.debug('potential fdasd errors were ignored')
def create(self, name, mbsize, type_name, flags=None): """ Create DASD partition :param string name: partition name :param int mbsize: partition size :param string type_name: unused :param list flags: unused """ self.partition_id += 1 fdasd_input = NamedTemporaryFile() with open(fdasd_input.name, 'w') as partition: log.debug( '%s: fdasd: n p cur_position +%sM w q', name, format(mbsize) ) if mbsize == 'all_free': partition.write('n\np\n\n\nw\nq\n') else: partition.write('n\np\n\n+%dM\nw\nq\n' % mbsize) bash_command = ' '.join( ['cat', fdasd_input.name, '|', 'fdasd', '-f', self.disk_device] ) try: Command.run( ['bash', '-c', bash_command] ) except Exception: # unfortunately fdasd reports that it can't read in the partition # table which I consider a bug in fdasd. However the table was # correctly created and therefore we continue. Problem is that we # are not able to detect real errors with the fdasd operation at # that point. log.debug('potential fdasd errors were ignored')
def fix_boot_catalog(cls, isofile): """ Fixup inconsistencies in boot catalog Make sure all catalog entries are in correct order and provide complete metadata information e.g catalog name :param str isofile: path to the ISO file """ iso_metadata = Iso._read_iso_metadata(isofile) Iso._validate_iso_metadata(iso_metadata) boot_catalog = iso_metadata.boot_catalog first_catalog_entry = Iso._sub_string( data=boot_catalog, length=32, start=32 ) first_catalog_entry = Iso._embed_string_in_segment( data=first_catalog_entry, string=struct.pack('B19s', 1, bytes(b'Legacy (isolinux)')), length=20, start=12 ) boot_catalog = Iso._embed_string_in_segment( data=boot_catalog, string=first_catalog_entry, length=32, start=32 ) second_catalog_entry = Iso._sub_string( data=boot_catalog, length=32, start=64 ) second_catalog_entry = Iso._embed_string_in_segment( data=second_catalog_entry, string=struct.pack('B19s', 1, bytes(b'UEFI (grub)')), length=20, start=12 ) second_catalog_entry_sector = second_catalog_entry[0] if second_catalog_entry_sector == 0x88: boot_catalog = Iso._embed_string_in_segment( data=boot_catalog, string=second_catalog_entry, length=32, start=96 ) second_catalog_entry = struct.pack( 'BBH28s', 0x91, 0xef, 1, bytes(b'') ) boot_catalog = Iso._embed_string_in_segment( data=boot_catalog, string=second_catalog_entry, length=32, start=64 ) with open(isofile, 'rb+') as iso: Iso._write_iso_sector( iso_metadata.boot_catalog_sector, boot_catalog, iso ) log.debug('Fixed iso catalog contents')
def fix_boot_catalog(self, isofile): """ Fixup inconsistencies in boot catalog Make sure all catalog entries are in correct order and provide complete metadata information e.g catalog name :param str isofile: path to the ISO file """ iso_metadata = Iso._read_iso_metadata(isofile) Iso._validate_iso_metadata(iso_metadata) boot_catalog = iso_metadata.boot_catalog first_catalog_entry = Iso._sub_string( data=boot_catalog, length=32, start=32 ) first_catalog_entry = Iso._embed_string_in_segment( data=first_catalog_entry, string=struct.pack('B19s', 1, bytes(b'Legacy (isolinux)')), length=20, start=12 ) boot_catalog = Iso._embed_string_in_segment( data=boot_catalog, string=first_catalog_entry, length=32, start=32 ) second_catalog_entry = Iso._sub_string( data=boot_catalog, length=32, start=64 ) second_catalog_entry = Iso._embed_string_in_segment( data=second_catalog_entry, string=struct.pack('B19s', 1, bytes(b'UEFI (grub)')), length=20, start=12 ) second_catalog_entry_sector = second_catalog_entry[0] if second_catalog_entry_sector == 0x88: boot_catalog = Iso._embed_string_in_segment( data=boot_catalog, string=second_catalog_entry, length=32, start=96 ) second_catalog_entry = struct.pack( 'BBH28s', 0x91, 0xef, 1, bytes(b'') ) boot_catalog = Iso._embed_string_in_segment( data=boot_catalog, string=second_catalog_entry, length=32, start=64 ) with open(isofile, 'rb+') as iso: Iso._write_iso_sector( iso_metadata.boot_catalog_sector, boot_catalog, iso ) log.debug('Fixed iso catalog contents')
def relocate_boot_catalog(self, isofile): """ Move ISO boot catalog to the standardized place Check location of the boot catalog and move it to the place where all BIOS and firwmare implementations expects it :param str isofile: path to the ISO file """ iso_metadata = Iso._read_iso_metadata(isofile) Iso._validate_iso_metadata(iso_metadata) with open(isofile, 'rb+') as iso: new_boot_catalog_sector = iso_metadata.path_table_sector - 1 new_volume_descriptor = Iso._read_iso_sector( new_boot_catalog_sector - 1, iso ) new_volume_id = Iso._sub_string( data=new_volume_descriptor, length=7 ) if bytes(b'CD001') not in new_volume_id: new_boot_catalog_sector = None ref_sector = iso_metadata.boot_catalog_sector for sector in range(0x12, 0x40): new_volume_descriptor = Iso._read_iso_sector(sector, iso) new_volume_id = Iso._sub_string( data=new_volume_descriptor, length=7 ) if (bytes(b'TEA01') in new_volume_id or sector + 1 == ref_sector): new_boot_catalog_sector = sector + 1 break if ( new_boot_catalog_sector and iso_metadata.boot_catalog_sector != new_boot_catalog_sector ): new_boot_catalog = Iso._read_iso_sector( new_boot_catalog_sector, iso ) empty_catalog = bytes(b'\x00') * 0x800 if new_boot_catalog == empty_catalog: eltorito_descriptor = Iso._embed_string_in_segment( data=iso_metadata.eltorito_descriptor, string=struct.pack('<I', new_boot_catalog_sector), length=4, start=0x47 ) Iso._write_iso_sector( new_boot_catalog_sector, iso_metadata.boot_catalog, iso ) Iso._write_iso_sector( 0x11, eltorito_descriptor, iso ) log.debug( 'Relocated boot catalog from sector 0x%x to 0x%x', iso_metadata.boot_catalog_sector, new_boot_catalog_sector )
def relocate_boot_catalog(cls, isofile): """ Move ISO boot catalog to the standardized place Check location of the boot catalog and move it to the place where all BIOS and firwmare implementations expects it :param str isofile: path to the ISO file """ iso_metadata = Iso._read_iso_metadata(isofile) Iso._validate_iso_metadata(iso_metadata) with open(isofile, 'rb+') as iso: new_boot_catalog_sector = iso_metadata.path_table_sector - 1 new_volume_descriptor = Iso._read_iso_sector( new_boot_catalog_sector - 1, iso ) new_volume_id = Iso._sub_string( data=new_volume_descriptor, length=7 ) if bytes(b'CD001') not in new_volume_id: new_boot_catalog_sector = None ref_sector = iso_metadata.boot_catalog_sector for sector in range(0x12, 0x40): new_volume_descriptor = Iso._read_iso_sector(sector, iso) new_volume_id = Iso._sub_string( data=new_volume_descriptor, length=7 ) if bytes(b'TEA01') in new_volume_id or \ sector + 1 == ref_sector: new_boot_catalog_sector = sector + 1 break if new_boot_catalog_sector and \ iso_metadata.boot_catalog_sector != new_boot_catalog_sector: new_boot_catalog = Iso._read_iso_sector( new_boot_catalog_sector, iso ) empty_catalog = bytes(b'\x00') * 0x800 if new_boot_catalog == empty_catalog: eltorito_descriptor = Iso._embed_string_in_segment( data=iso_metadata.eltorito_descriptor, string=struct.pack('<I', new_boot_catalog_sector), length=4, start=0x47 ) Iso._write_iso_sector( new_boot_catalog_sector, iso_metadata.boot_catalog, iso ) Iso._write_iso_sector( 0x11, eltorito_descriptor, iso ) log.debug( 'Relocated boot catalog from sector 0x%x to 0x%x', iso_metadata.boot_catalog_sector, new_boot_catalog_sector )
def setup_media_loader_directory(lookup_path, media_path, boot_theme): loader_data = lookup_path + '/image/loader/' media_boot_path = os.sep.join( [media_path, Defaults.get_iso_boot_path(), 'loader']) Path.wipe(loader_data) Path.create(loader_data) grub_image_file_names = [ Defaults.get_isolinux_bios_grub_loader(), 'boot_hybrid.img' ] loader_files = [] for grub_image_file_name in grub_image_file_names: grub_file = Defaults.get_grub_path( lookup_path, 'i386-pc/{0}'.format(grub_image_file_name), raise_on_error=False) if grub_file and os.path.exists(grub_file): loader_files.append(grub_file) for syslinux_file_name in Defaults.get_syslinux_modules(): for syslinux_dir in Defaults.get_syslinux_search_paths(): syslinux_file = os.path.normpath( os.sep.join( [lookup_path, syslinux_dir, syslinux_file_name])) if os.path.exists(syslinux_file): loader_files.append(syslinux_file) log.debug('Copying loader files to {0}'.format(loader_data)) for loader_file in loader_files: log.debug('--> Copying {0}'.format(loader_file)) shutil.copy(loader_file, loader_data) bash_command = ' '.join( ['cp', lookup_path + '/boot/memtest*', loader_data + '/memtest']) Command.run(command=['bash', '-c', bash_command], raise_on_error=False) if boot_theme: theme_path = ''.join( [lookup_path, '/etc/bootsplash/themes/', boot_theme]) if os.path.exists(theme_path + '/cdrom/gfxboot.cfg'): bash_command = ' '.join( ['cp', theme_path + '/cdrom/*', loader_data]) Command.run(['bash', '-c', bash_command]) # don't move down one menu entry the first time a F-key is used Command.run([ 'gfxboot', '--config-file', loader_data + '/gfxboot.cfg', '--change-config', 'install::autodown=0' ]) if os.path.exists(theme_path + '/bootloader/message'): Command.run( ['cp', theme_path + '/bootloader/message', loader_data]) Path.create(media_boot_path) data = DataSync(loader_data, media_boot_path) data.sync_data(options=['-z', '-a'])
def install(self): """ Install bootloader on self.device """ log.info('Installing zipl on disk %s', self.device) self.boot_mount.mount() bash_command = ' '.join([ 'cd', self.boot_mount.mountpoint, '&&', 'zipl', '-V', '-c', self.boot_mount.mountpoint + '/config', '-m', 'menu' ]) zipl_call = Command.run(['bash', '-c', bash_command]) log.debug('zipl install succeeds with: %s', zipl_call.output)
def create_initrd(self, mbrid=None, basename=None, install_initrd=False): """ Call dracut as chroot operation to create the initrd and move the result into the image build target directory :param object mbrid: unused :param string basename: base initrd file name :param bool install_initrd: installation media initrd """ if self.is_prepared(): log.info('Creating generic dracut initrd archive') kernel_info = Kernel(self.boot_root_directory) kernel_details = kernel_info.get_kernel(raise_on_not_found=True) if basename: dracut_initrd_basename = basename else: dracut_initrd_basename = self.initrd_base_name if install_initrd: included_files = self.included_files_install modules_args = [ '--add', ' {0} '.format(' '.join(self.install_modules)) ] if self.install_modules else [] omit_modules_args = [ '--omit', ' {0} '.format(' '.join( self.omit_install_modules)) ] if self.omit_install_modules else [] else: included_files = self.included_files modules_args = [ '--add', ' {0} '.format(' '.join(self.modules)) ] if self.modules else [] omit_modules_args = [ '--omit', ' {0} '.format(' '.join(self.omit_modules)) ] if self.omit_modules else [] dracut_initrd_basename += '.xz' options = self.dracut_options + modules_args +\ omit_modules_args + included_files dracut_call = Command.run([ 'chroot', self.boot_root_directory, 'dracut', '--force', '--no-hostonly', '--no-hostonly-cmdline', '--xz' ] + options + [dracut_initrd_basename, kernel_details.version], stderr_to_stdout=True) log.debug(dracut_call.output) Command.run([ 'mv', os.sep.join([self.boot_root_directory, dracut_initrd_basename]), self.target_dir ]) self.initrd_filename = os.sep.join( [self.target_dir, dracut_initrd_basename])
def import_shell_environment(self, profile): """ Create profile environment to let scripts consume information from the XML description. :param object profile: Instance of Profile """ profile_file = self.root_dir + '/.profile' log.info('Creating .profile environment') profile_environment = profile.create() with open(profile_file, 'w') as profile: for line in profile_environment: profile.write(line + '\n') log.debug('--> %s', line)
def _install_bootloader(self, device_map): root_device = device_map['root'] boot_device = root_device if 'boot' in device_map: boot_device = device_map['boot'] if 'readonly' in device_map: root_device = device_map['readonly'] custom_install_arguments = { 'boot_device': boot_device.get_device(), 'root_device': root_device.get_device(), 'firmware': self.firmware, 'target_removable': self.target_removable } if 'efi' in device_map: efi_device = device_map['efi'] custom_install_arguments.update( {'efi_device': efi_device.get_device()} ) if 'prep' in device_map: prep_device = device_map['prep'] custom_install_arguments.update( {'prep_device': prep_device.get_device()} ) if self.volume_manager_name: self.system.umount_volumes() custom_install_arguments.update( {'system_volumes': self.system.get_volumes()} ) if self.bootloader is not 'custom': log.debug( "custom arguments for bootloader installation %s", custom_install_arguments ) bootloader = BootLoaderInstall( self.bootloader, self.root_dir, self.disk.storage_provider, custom_install_arguments ) if bootloader.install_required(): bootloader.install() self.system_setup.call_edit_boot_install_script( self.diskname, boot_device.get_device() )
def _install_bootloader(self, device_map): root_device = device_map['root'] boot_device = root_device if 'boot' in device_map: boot_device = device_map['boot'] if 'readonly' in device_map: root_device = device_map['readonly'] custom_install_arguments = { 'boot_device': boot_device.get_device(), 'root_device': root_device.get_device(), 'firmware': self.firmware, 'target_removable': self.target_removable } if 'efi' in device_map: efi_device = device_map['efi'] custom_install_arguments.update( {'efi_device': efi_device.get_device()}) if 'prep' in device_map: prep_device = device_map['prep'] custom_install_arguments.update( {'prep_device': prep_device.get_device()}) if self.volume_manager_name: self.system.umount_volumes() custom_install_arguments.update( {'system_volumes': self.system.get_volumes()}) # create bootloader config prior bootloader installation self.bootloader_config.setup_disk_image_config( boot_options=custom_install_arguments) # cleanup bootloader config resources taken prior to next steps del self.bootloader_config if self.bootloader != 'custom': log.debug("custom arguments for bootloader installation %s", custom_install_arguments) bootloader = BootLoaderInstall(self.bootloader, self.root_dir, self.disk.storage_provider, custom_install_arguments) if bootloader.install_required(): bootloader.install() self.system_setup.call_edit_boot_install_script( self.diskname, boot_device.get_device())
def create(self, name, mbsize, type_name, flags=None): """ Create msdos partition :param string name: partition name :param int mbsize: partition size :param string type_name: partition type :param list flags: additional flags """ self.partition_id += 1 fdisk_input = NamedTemporaryFile() if self.partition_id > 1: # Undefined start sector value skips this for fdisk and # use its default value self.start_sector = None with open(fdisk_input.name, 'w') as partition: log.debug( '%s: fdisk: n p %d cur_position +%sM w q', name, self.partition_id, format(mbsize) ) partition.write( 'n\np\n{0}\n{1}\n{2}\nw\nq\n'.format( self.partition_id, '' if not self.start_sector else self.start_sector, '' if mbsize == 'all_free' else '+{0}M'.format(mbsize) ) ) bash_command = ' '.join( ['cat', fdisk_input.name, '|', 'fdisk', self.disk_device] ) try: Command.run( ['bash', '-c', bash_command] ) except Exception: # unfortunately fdisk reports that it can't read in the partition # table which I consider a bug in fdisk. However the table was # correctly created and therefore we continue. Problem is that we # are not able to detect real errors with the fdisk operation at # that point. log.debug('potential fdisk errors were ignored') self.set_flag(self.partition_id, type_name) if flags: for flag_name in flags: self.set_flag(self.partition_id, flag_name)
def get_format(self): """ Detect compression format :return: compression format name or None if it couldn't be inferred :rtype: Optional[str] """ for zipper in self.supported_zipper: cmd = [zipper, '-l', self.source_filename] try: Command.run(cmd) return zipper except Exception as exc: log.debug( 'Error running "{cmd:s}", got a {exc_t:s}: {exc:s}'.format( cmd=' '.join(cmd), exc_t=type(exc).__name__, exc=str(exc)))
def install(self): """ Install bootloader on self.device """ log.info('Installing zipl on disk %s', self.device) self.boot_mount.mount() bash_command = ' '.join( [ 'cd', self.boot_mount.mountpoint, '&&', 'zipl', '-V', '-c', self.boot_mount.mountpoint + '/config', '-m', 'menu' ] ) zipl_call = Command.run( ['bash', '-c', bash_command] ) log.debug('zipl install succeeds with: %s', zipl_call.output)
def set_hybrid_mbr(self): """ Turn partition table into hybrid GPT/MBR table """ partition_ids = [] partition_number_to_embed = self.partition_id if partition_number_to_embed > 3: # the max number of partitions to embed is 3 # for details see man sgdisk log.debug( 'maximum number of GPT hybrid MBR partitions is 3, got %d', partition_number_to_embed) partition_number_to_embed = 3 log.debug('reduced GPT hybrid MBR partition count to %d', partition_number_to_embed) for number in range(1, partition_number_to_embed + 1): partition_ids.append(format(number)) Command.run( ['sgdisk', '-h', ':'.join(partition_ids), self.disk_device])
def wipe(self): """ Zap (destroy) any GPT and MBR data structures if present For DASD disks create a new VTOC table """ if 'dasd' in self.table_type: log.debug('Initialize DASD disk with new VTOC table') fdasd_input = NamedTemporaryFile() with open(fdasd_input.name, 'w') as vtoc: vtoc.write('y\n\nw\nq\n') bash_command = ' '.join( [ 'cat', fdasd_input.name, '|', 'fdasd', '-f', self.storage_provider.get_device() ] ) try: Command.run( ['bash', '-c', bash_command] ) except Exception: # unfortunately fdasd reports that it can't read in the # partition table which I consider a bug in fdasd. However # the table was correctly created and therefore we continue. # Problem is that we are not able to detect real errors # with the fdasd operation at that point. log.debug('potential fdasd errors were ignored') else: log.debug('Initialize %s disk', self.table_type) Command.run( [ 'sgdisk', '--zap-all', self.storage_provider.get_device() ] )
def wipe(self): """ Zap (destroy) any GPT and MBR data structures if present For DASD disks create a new VTOC table """ if 'dasd' in self.table_type: log.debug('Initialize DASD disk with new VTOC table') fdasd_input = NamedTemporaryFile() with open(fdasd_input.name, 'w') as vtoc: vtoc.write('y\n\nw\nq\n') bash_command = ' '.join([ 'cat', fdasd_input.name, '|', 'fdasd', '-f', self.storage_provider.get_device() ]) try: Command.run(['bash', '-c', bash_command]) except Exception: # unfortunately fdasd reports that it can't read in the # partition table which I consider a bug in fdasd. However # the table was correctly created and therefore we continue. # Problem is that we are not able to detect real errors # with the fdasd operation at that point. log.debug('potential fdasd errors were ignored') else: log.debug('Initialize %s disk', self.table_type) Command.run( ['sgdisk', '--zap-all', self.storage_provider.get_device()])
def _create_volume_no_zero(lvcreate_args): """ Create an LV using specified arguments to lvcreate The LV will be created with the '-Zn' option prepended to the arguments, which disables the zeroing of the new device's header; this action will fail when running in an environment where udev is not enabled, such as in a chroot'd Open Build Service build. Since the backing device for a kiwi LVM device is a zero filled qemu-img created file, there should be no negative side effects to skipping the zeroing of this header block. Then we run 'vgscan --mknodes' to ensure that any /dev nodes have been created for the new LV. :param list lvcreate_args: list of lvcreate arguments. """ log.debug('--> running "lvcreate -Zn %s"', " ".join(lvcreate_args)) Command.run(['lvcreate', '-Zn'] + lvcreate_args) log.debug('--> running "vgscan --mknodes" to create missing nodes') Command.run(['vgscan', '--mknodes'])
def create(self, name, mbsize, type_name, flags=None): """ Create msdos partition :param string name: partition name :param int mbsize: partition size :param string type_name: partition type :param list flags: additional flags """ self.partition_id += 1 fdisk_input = NamedTemporaryFile() if self.partition_id > 1: # Undefined start sector value skips this for fdisk and # use its default value self.start_sector = None with open(fdisk_input.name, 'w') as partition: log.debug('%s: fdisk: n p %d cur_position +%sM w q', name, self.partition_id, format(mbsize)) partition.write('n\np\n{0}\n{1}\n{2}\nw\nq\n'.format( self.partition_id, '' if not self.start_sector else self.start_sector, '' if mbsize == 'all_free' else '+{0}M'.format(mbsize))) bash_command = ' '.join( ['cat', fdisk_input.name, '|', 'fdisk', self.disk_device]) try: Command.run(['bash', '-c', bash_command]) except Exception: # unfortunately fdisk reports that it can't read in the partition # table which I consider a bug in fdisk. However the table was # correctly created and therefore we continue. Problem is that we # are not able to detect real errors with the fdisk operation at # that point. log.debug('potential fdisk errors were ignored') self.set_flag(self.partition_id, type_name) if flags: for flag_name in flags: self.set_flag(self.partition_id, flag_name)
def set_hybrid_mbr(self): """ Turn partition table into hybrid GPT/MBR table """ partition_ids = [] partition_number_to_embed = self.partition_id if partition_number_to_embed > 3: # the max number of partitions to embed is 3 # for details see man sgdisk log.debug( 'maximum number of GPT hybrid MBR partitions is 3, got %d', partition_number_to_embed ) partition_number_to_embed = 3 log.debug( 'reduced GPT hybrid MBR partition count to %d', partition_number_to_embed ) for number in range(1, partition_number_to_embed + 1): partition_ids.append(format(number)) Command.run( ['sgdisk', '-h', ':'.join(partition_ids), self.disk_device] )
def create_header_end_block(self, isofile): """ Find offset address of file containing the header_id and replace it by a list of 2k blocks in range 0 - offset + 1 This is the required preparation to support hybrid ISO images, meaning to let isohybrid work correctly :param string isofile: path to the ISO file :raises KiwiIsoLoaderError: if the header_id file is not found :return: 512 byte blocks offset address :rtype: int """ file_count = 0 offset = 0 found_id = False iso_tool = IsoToolsCdrTools(self.source_dir) with open(isofile, 'rb') as iso: for start in iso_tool.list_iso(isofile): if file_count >= 8: # check only the first 8 files break file_count += 1 read_buffer = '' for index in range(0, -9, -1): # go back up to 8 blocks offset = start + index iso.seek(offset << 11, 0) read_buffer = iso.read(len(self.header_id)) if read_buffer == self.header_id.encode(): iso.seek(0, 0) file_count = 8 found_id = True break if found_id: log.debug('Found ISO header_end id at offset:') log.debug('--> 2k blocks: %d', offset) log.debug('--> 512 byte blocks(isohybrid): %d', offset * 4) log.debug('--> bytes(loop mount): %d', offset * 2048) with open(self.header_end_file, 'wb') as marker: for index in range(offset + 1): marker.write(iso.read(2048)) else: raise KiwiIsoLoaderError( 'Header ID not found in iso file %s' % isofile ) return offset * 4