[Kimchi-devel] [PATCH] PowerPC bootable ISO detection code

Paulo Ricardo Paz Vital pvital at linux.vnet.ibm.com
Wed Jul 30 20:11:08 UTC 2014


-- 
Reviewed-by: Paulo Vital <pvital at linux.vnet.ibm.com>


On Mon, 2014-07-28 at 07:27 -0300, Daniel Barboza wrote:
> From: Daniel Henrique Barboza <danielhb at linux.vnet.ibm.com>
> 
> PowerPC ISO images does not follow the same rules as Intel's.
> Intel ISOs uses ISO9660 extension called 'El Torito', which
> describes the format of the Boot Record. PowerPC firmware, on
> the other hand, looks for a file '/ppc/bootinfo.txt' which
> describes how to boot the image.
> 
> This patch creates a function that opens the ISO file without
> mounting it and, following ISO9660 standards, read the filesystem
> trying to find the boot file that enables PowerPC booting.
> 
> Signed-off-by: Daniel Henrique Barboza <danielhb at linux.vnet.ibm.com>
> ---
>  src/kimchi/isoinfo.py | 166 +++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 165 insertions(+), 1 deletion(-)
> 
> diff --git a/src/kimchi/isoinfo.py b/src/kimchi/isoinfo.py
> index c394a32..72b956d 100644
> --- a/src/kimchi/isoinfo.py
> +++ b/src/kimchi/isoinfo.py
> @@ -18,6 +18,7 @@
>  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
> 
>  import glob
> +import platform
>  import os
>  import re
>  import struct
> @@ -132,6 +133,12 @@ class IsoImage(object):
>      EL_TORITO_BOOT_RECORD = struct.Struct("=B5sB32s32sI")
>      EL_TORITO_VALIDATION_ENTRY = struct.Struct("=BBH24sHBB")
>      EL_TORITO_BOOT_ENTRY = struct.Struct("=BBHBBHL20x")
> +    # Path table info starting in ISO9660 offset 132. We force little
> +    # endian byte order (the '<' sign) because Power systems can run on
> +    # both.
> +    # First int is path table size, next 4 bytes are discarded (it is
> +    # the same info but in big endian) and next int is the location.
> +    PATH_TABLE_SIZE_LOC = struct.Struct("<I 4s I")
> 
>      def __init__(self, path):
>          self.path = path
> @@ -224,6 +231,160 @@ class IsoImage(object):
>              raise IsoFormatError("KCHISO0005E",
>                                   {'filename': self.path})
> 
> +    def _scan_ppc(self):
> +        """
> +        PowerPC firmware does not use the conventional El Torito boot
> +        specification. Instead, it looks for a file '/ppc/bootinfo.txt'
> +        which contains boot information. A PPC image is bootable if
> +        this file exists in the filesystem [1].
> +
> +        To detect if a PPC ISO is bootable, we could simply mount the
> +        ISO and search for the boot file as we would with any other
> +        file in the filesystem. We can also look for the boot file
> +        searching byte by byte the ISO image. This is possible because
> +        the PPC ISO image follows the ISO9660 standard [2]. Mounting
> +        the ISO requires extra resources and it takes longer than
> +        searching the image data, thus we chose the latter approach
> +        in this code.
> +
> +        To locate a file we must access the Path Table, which contains
> +        the records of all the directories in the ISO. After locating
> +        the directory/subdirectory that contains the file, we access
> +        the Directory Record to find it.
> +
> +
> +        .. [1] https://www.ibm.com/developerworks/community/wikis/home?\
> +lang=en#!/wiki/W51a7ffcf4dfd_4b40_9d82_446ebc23c550/page/PowerLinux\
> +%20Boot%20howto
> +        .. [2] http://wiki.osdev.org/ISO_9660
> +        """
> +
> +        # To locate any file we must access the Path Table, which
> +        # contains the records of all the directories in the ISO.
> +        # ISO9660 dictates that the Path Table location information
> +        # is at offset 132, inside the Primary Volume Descriptor,
> +        # after the SystemArea (16*SECTOR_SIZE).
> +        #
> +        # In the Path table info we're forcing little endian byte
> +        # order (the '<' sign) because Power systems can run on
> +        # both.
> +        #
> +        # First int is path table size, next 4 bytes are discarded (it is
> +        # the same info but in big endian) and next int is the location.
> +        PATH_TABLE_LOC_OFFSET = 16 * IsoImage.SECTOR_SIZE + 132
> +        PATH_TABLE_SIZE_LOC = struct.Struct("<I 4s I")
> +
> +        path_table_loc_data = self._get_iso_data(PATH_TABLE_LOC_OFFSET,
> +                                                 PATH_TABLE_SIZE_LOC.size)
> +        path_size, unused, path_loc = self._unpack(PATH_TABLE_SIZE_LOC,
> +                                                   path_table_loc_data)
> +        # Fetch the Path Table using location and size found above
> +        path_table_offset = path_loc * IsoImage.SECTOR_SIZE
> +        path_table_data = self._get_iso_data(path_table_offset, path_size)
> +
> +        # Loop inside the path table to find the directory 'ppc'.
> +        # The contents of the registers are:
> +        # - length of the directory identifier (1 byte)
> +        # - extended attribute record length (1 byte)
> +        # - location of directory register (4 bytes)
> +        # - directory number of parent dir (2 bytes)
> +        # - directory name (size varies according to length)
> +        # - padding field - 1 byte if the length is odd, not present if even
> +        DIR_NAMELEN_LOCATION_PARENT = struct.Struct("<B B I H")
> +        dir_struct_size = DIR_NAMELEN_LOCATION_PARENT.size
> +        i = 0
> +        while i < path_size:
> +            dir_data = path_table_data[i: i+dir_struct_size]
> +            i += dir_struct_size
> +            # We won't use the Extended Attribute Record
> +            dir_namelen, unused, dir_loc, dir_parent = \
> +                self._unpack(DIR_NAMELEN_LOCATION_PARENT, dir_data)
> +            if dir_parent == 1:
> +                # read the dir name using the namelen
> +                dir_name = path_table_data[i: i+dir_namelen].rstrip()
> +                if dir_name == 'ppc':
> +                    # stop searching, dir was found
> +                    break
> +            # Need to consider the optional padding field as well
> +            i += dir_namelen + dir_namelen % 2
> +
> +        if i > path_size:
> +            # Didn't find the '/ppc' directory. ISO is not bootable.
> +            self.bootable = False
> +            return
> +
> +        # Get the 'ppc' directory record using 'dir_loc'.
> +        ppc_dir_offset = dir_loc * IsoImage.SECTOR_SIZE
> +
> +        # We need to find the sector size of this dir entry. The
> +        # size of the File Section is located 10 bytes after
> +        # the dir location.
> +        DIR_SIZE_FMT = struct.Struct("<10sI")
> +        data = self._get_iso_data(ppc_dir_offset, DIR_SIZE_FMT.size)
> +        unused, dir_size = self._unpack(DIR_SIZE_FMT, data)
> +        # If the dir is in the middle of a sector, the sector is
> +        # padded zero and won't be utilized. We need to round up
> +        # the result
> +        dir_sectorsize = dir_size / IsoImage.SECTOR_SIZE
> +        if dir_size % IsoImage.SECTOR_SIZE:
> +            dir_sectorsize += 1
> +
> +        # Fixed-size directory record fields:
> +        # - length of directory record (1 byte)
> +        # - extended attr. record length (1 byte)
> +        # - location of extend in both-endian format (8 bytes)
> +        # - data length (size of extend) in both-endian (8 bytes)
> +        # - recording date and time (7 bytes)
> +        # - file flags (1 byte)
> +        # - file unit size interleaved (1 byte)
> +        # - interleave gap size (1 byte)
> +        # - volume sequence number (4 bytes)
> +        # - length of file identifier (1 byte)
> +        #
> +        #  Of all these fields, we will use only 3 of them, 'ignoring'
> +        #  30 bytes total.
> +        STATIC_DIR_RECORD_FMT = struct.Struct("<B 24s B 6s B")
> +        static_rec_size = STATIC_DIR_RECORD_FMT.size
> +
> +        # Maximum offset possible of all the records of this directory
> +        DIR_REC_MAX = ppc_dir_offset + dir_sectorsize*IsoImage.SECTOR_SIZE
> +        # Max size of a given directory record
> +        MAX_DIR_SIZE = 255
> +        # Name of the boot file
> +        BOOT_FILE_NAME = "bootinfo.txt"
> +
> +        # Loop until one of the following happens:
> +        # - boot file is found
> +        # - end of directory record listing for the 'ppc' dir
> +        while ppc_dir_offset < DIR_REC_MAX:
> +            record_data = self._get_iso_data(ppc_dir_offset, MAX_DIR_SIZE)
> +            dir_rec_len, unused, file_flags, unused2, file_name_len = \
> +                self._unpack(STATIC_DIR_RECORD_FMT, record_data)
> +
> +            # if dir_rec_len = 0, increment offset (skip the
> +            # dir_rec_len byte) and continue the loop
> +            if dir_rec_len == 0:
> +                ppc_dir_offset += 1
> +                continue
> +
> +            # Get filename of the file/dir we're at.
> +            filename = record_data[static_rec_size:
> +                                   static_rec_size + file_name_len].rstrip()
> +            # The second bit of the file_flags indicate if this record
> +            # is a directory.
> +            if filename == BOOT_FILE_NAME and (file_flags & 2) != 1:
> +                self.bootable = True
> +                return
> +
> +            # Update offset and keep looking. There is a padding here
> +            # if the length of the file identifier is EVEN.
> +            padding = 0
> +            if not file_name_len % 2:
> +                padding = 1
> +            ppc_dir_offset += dir_rec_len + padding
> +        # If reached this point the file wasn't found = not bootable
> +        self.bootable = False
> +
>      def _scan_primary_vol(self, data):
>          """
>          Scan one sector for a Primary Volume Descriptor and extract the
> @@ -269,7 +430,10 @@ class IsoImage(object):
>              return
> 
>          self._scan_primary_vol(data)
> -        self._scan_el_torito(data)
> +        if platform.machine().startswith('ppc'):
> +            self._scan_ppc()
> +        else:
> +            self._scan_el_torito(data)
> 
> 
>  class Matcher(object):




More information about the Kimchi-devel mailing list