Esempio n. 1
0
    def add_child(self, child, logical_block_size, allow_duplicate=False):
        '''
        A method to add a child to this object.  Note that this is called both
        during parsing and when adding a new object to the system, so it
        it shouldn't have any functionality that is not appropriate for both.

        Parameters:
         child - The child directory record object to add.
         logical_block_size - The size of a logical block for this volume descriptor.
         allow_duplicate - Whether to allow duplicate names, as there are situations where duplicate children are allowed.
        Returns:
         True if adding this child caused the directory to overflow into another
         extent, False otherwise.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                "Directory Record not yet initialized")

        if not self.isdir:
            raise pycdlibexception.PyCdlibInvalidInput(
                "Trying to add a child to a record that is not a directory")

        # First ensure that this is not a duplicate.  For speed purposes, we
        # recognize that bisect_left will always choose an index to the *left*
        # of a duplicate child.  Thus, to check for duplicates we only need to
        # see if the child to be added is a duplicate with the entry that
        # bisect_left returned.
        index = bisect.bisect_left(self.children, child)
        if index != len(self.children) and self.children[
                index].file_ident == child.file_ident:
            if not self.children[index].is_associated_file(
            ) and not child.is_associated_file():
                if not (self.rock_ridge is not None
                        and self.file_identifier() == b"RR_MOVED"):
                    if not allow_duplicate:
                        raise pycdlibexception.PyCdlibInvalidInput(
                            "Parent %s already has a child named %s" %
                            (self.file_identifier(), child.file_identifier()))
                    else:
                        self.children[index].data_continuation = child
                        index += 1
        self.children.insert(index, child)

        # We now have to check if we need to add another logical block.
        # We have to iterate over the entire list again, because where we
        # placed this last entry may rearrange the empty spaces in the blocks
        # that we've already allocated.
        num_extents, dirrecord_unused = self._recalculate_extents_and_offsets(
            index, logical_block_size)

        overflowed = False
        if num_extents * logical_block_size > self.data_length:
            overflowed = True
            # When we overflow our data length, we always add a full block.
            self.data_length += logical_block_size
            # We also have to make sure to update the length of the dot child,
            # as that should always reflect the length.
            self.children[0].data_length = self.data_length

        return overflowed
Esempio n. 2
0
    def new(self, sector_count, load_seg, media_name, system_type, bootable):
        # type: (int, int, str, int, bool) -> None
        '''
        A method to create a new El Torito Entry.

        Parameters:
         sector_count - The number of sectors to assign to this El Torito Entry.
         load_seg - The load segment address of the boot image.
         media_name - The name of the media type, one of 'noemul', 'floppy', or 'hdemul'.
         system_type - The partition type to assign to the entry.
         bootable - Whether this entry is bootable.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'El Torito Entry already initialized')

        if media_name == 'noemul':
            media_type = self.MEDIA_NO_EMUL
        elif media_name == 'floppy':
            if sector_count == 2400:
                media_type = self.MEDIA_12FLOPPY
            elif sector_count == 2880:
                media_type = self.MEDIA_144FLOPPY
            elif sector_count == 5760:
                media_type = self.MEDIA_288FLOPPY
            else:
                raise pycdlibexception.PyCdlibInvalidInput(
                    'Invalid sector count for floppy media type; must be 2400, 2880, or 5760'
                )
            # With floppy booting, the sector_count always ends up being 1
            sector_count = 1
        elif media_name == 'hdemul':
            media_type = self.MEDIA_HD_EMUL
            # With HD emul booting, the sector_count always ends up being 1
            sector_count = 1
        else:
            raise pycdlibexception.PyCdlibInvalidInput(
                "Invalid media name '%s'" % (media_name))

        if bootable:
            self.boot_indicator = 0x88
        else:
            self.boot_indicator = 0
        self.boot_media_type = media_type
        self.load_segment = load_seg
        self.system_type = system_type
        self.sector_count = sector_count
        self.load_rba = 0  # This will get set later
        self.selection_criteria_type = 0  # FIXME: allow the user to set this
        self.selection_criteria = b''.ljust(19, b'\x00')

        self._initialized = True
    def _rr_path_to_iso_path(self, rr_path):
        # type: (str) -> str
        '''
        An internal method to convert an ISO9660 path to a Rock Ridge one. This
        is accomplished by find the Rock Ridge directory record, then
        reconstructing the ISO path by walking up to the root.

        Parameters:
         rr_path - The Rock Ridge path to generate an ISO9660 path for.
        Returns:
         The ISO9660 path corresponding to the Rock Ridge path.
        '''
        if rr_path[0] != '/':
            raise pycdlibexception.PyCdlibInvalidInput(
                "rr_path must start with '/'")

        record = self.pycdlib_obj._find_rr_record(utils.normpath(rr_path))  # pylint: disable=protected-access
        if record.is_root:
            iso_path = b'/'
        else:
            iso_path = b''
            parent = record  # type: Optional[dr.DirectoryRecord]
            while parent is not None:
                if not parent.is_root:
                    iso_path = b'/' + parent.file_identifier() + iso_path
                parent = parent.parent

        return iso_path.decode('utf-8')
 def __init__(self, pycdlib_obj):
     # type: (pycdlib.PyCdlib) -> None
     if not pycdlib_obj.has_rock_ridge():
         raise pycdlibexception.PyCdlibInvalidInput(
             'Can only instantiate a Rock Ridge facade for a Rock Ridge ISO'
         )
     self.pycdlib_obj = pycdlib_obj
def iso_path_to_rr_name(iso_path, interchange_level, is_dir):
    # type: (str, int, bool) -> str
    '''
    A method to take an absolute ISO path and generate a corresponding
    Rock Ridge basename.

    Parameters:
     iso_path - The absolute iso_path to generate a Rock Ridge name from.
     interchange_level - The interchange level at which to operate.
     is_dir - Whether this will be a directory or not.
    Returns:
     The Rock Ridge name as a string.
    '''

    if iso_path[0] != '/':
        raise pycdlibexception.PyCdlibInvalidInput(
            "iso_path must start with '/'")

    namesplit = utils.split_path(utils.normpath(iso_path))
    iso_name = namesplit.pop()

    if is_dir:
        rr_name = utils.mangle_dir_for_iso9660(iso_name.decode('utf-8'),
                                               interchange_level)
    else:
        basename, ext = utils.mangle_file_for_iso9660(iso_name.decode('utf-8'),
                                                      interchange_level)
        rr_name = '.'.join([basename, ext])

    return rr_name
Esempio n. 6
0
def encode_space_pad(instr, length, encoding):
    '''
    A function to pad out an input string with spaces to the length specified.
    The space is first encoded into the specified encoding, then appended to
    the input string until the length is reached.

    Parameters:
     instr - The input string to encode and pad.
     length - The length to pad the input string to.
     encoding - The encoding to use.
    Returns:
     The input string encoded in the encoding and padded with encoded spaces.
    '''
    output = instr.decode('utf-8').encode(encoding)
    if len(output) > length:
        raise pycdlibexception.PyCdlibInvalidInput("Input string too long!")

    encoded_space = ' '.encode(encoding)

    left = length - len(output)
    while left > 0:
        output += encoded_space
        left -= len(encoded_space)

    if left < 0:
        output = output[:left]

    return output
    def readinto(self, b):
        # type: (bytes) -> int
        if not self._open:
            raise pycdlibexception.PyCdlibInvalidInput(
                'I/O operation on closed file.')

        readsize = self._length - self._offset
        if readsize > 0:
            if sys.version_info >= (3, 0):
                # Python 3
                mv = memoryview(b)  # type: ignore
                m = mv.cast('B')  # type: ignore
                readsize = min(readsize, len(m))
                data = self._fp.read(readsize)
                n = len(data)
                m[:n] = data
            else:
                # Python 2
                readsize = min(readsize, len(b))
                data = self._fp.read(readsize)
                n = len(data)
                try:
                    b[:n] = data
                except TypeError as err:
                    if not isinstance(b, array.array):
                        raise err
                    b[:n] = array.array(b'b', data)
        else:
            n = 0

        return n
    def read(self, size=None):
        # type: (Optional[int]) -> bytes
        '''
        Read and return up to size bytes.

        Parameters:
         size - Optional parameter to read size number of bytes; if None or
                negative, all remaining bytes in the file will be read
        Returns:
         The number of bytes requested or the rest of the data left in the file,
         whichever is smaller.  If the file is at or past EOF, returns an empty
         bytestring.
        '''
        if not self._open:
            raise pycdlibexception.PyCdlibInvalidInput(
                'I/O operation on closed file.')

        if self._offset >= self._length:
            return b''

        if size is None or size < 0:
            data = self.readall()
        else:
            readsize = min(self._length - self._offset, size)
            data = self._fp.read(readsize)
            self._offset += readsize

        return data
Esempio n. 9
0
    def readinto(self, b):
        # type: (Union[bytearray, memoryview, array.array[Any], mmap]) -> int
        if not self._open:
            raise pycdlibexception.PyCdlibInvalidInput('I/O operation on closed file.')

        readsize = self._length - self._offset
        if readsize > 0:
            if have_py_3:
                mv = memoryview(b)
                m = mv.cast('B')
                readsize = min(readsize, len(m))
                data = self._fp.read(readsize)
                n = len(data)
                m[:n] = data
            else:
                readsize = min(readsize, len(b))
                data = self._fp.read(readsize)
                n = len(data)
                try:
                    b[:n] = data
                except TypeError as err:
                    if not isinstance(b, array.array):
                        raise err
                    b[:n] = array.array(b'b', data)  # type: ignore
        else:
            n = 0

        return n
Esempio n. 10
0
File: dr.py Progetto: fsiler/pycdlib
    def __init__(self, drobj, logical_block_size):
        if drobj.isdir:
            raise pycdlibexception.PyCdlibInvalidInput(
                "Cannot write out a directory")

        self.drobj = drobj

        while self.drobj.target is not None:
            self.drobj = self.drobj.target
        self.logical_block_size = logical_block_size
Esempio n. 11
0
    def length(self):
        # type: () -> int
        """
        Return the length of the current file.

        Parameters:
         None.
        Returns:
         The length of the file.
        """
        if not self._open:
            raise pycdlibexception.PyCdlibInvalidInput('I/O operation on closed file.')
        return self._length
Esempio n. 12
0
    def seekable(self):
        # type: () -> bool
        """
        Determine whether this file is seekable.

        Parameters:
         None.
        Returns:
         True in all cases.
        """
        if not self._open:
            raise pycdlibexception.PyCdlibInvalidInput('I/O operation on closed file.')
        return True
Esempio n. 13
0
    def tell(self):
        # type: () -> int
        """
        Return the current stream position.

        Parameters:
         None.
        Returns:
         The current stream position.
        """
        if not self._open:
            raise pycdlibexception.PyCdlibInvalidInput('I/O operation on closed file.')
        return self._offset
    def readable(self):
        # type: () -> bool
        '''
        A method to determine whether this file is readable.

        Parameters:
         None.
        Returns:
         True in all cases.
        '''
        if not self._open:
            raise pycdlibexception.PyCdlibInvalidInput(
                'I/O operation on closed file.')
        return True
Esempio n. 15
0
    def add_section(self, ino, sector_count, load_seg, media_name, system_type,
                    efi, bootable):
        # type: (inode.Inode, int, int, str, int, bool, bool) -> None
        '''
        A method to add an section header and entry to this Boot Catalog.

        Parameters:
         ino - The Inode object to associate with the new Entry.
         sector_count - The number of sectors to assign to the new Entry.
         load_seg - The load segment address of the boot image.
         media_name - The name of the media type, one of 'noemul', 'floppy', or 'hdemul'.
         system_type - The type of partition this entry should be.
         efi - Whether this section is an EFI section.
         bootable - Whether this entry should be bootable.
        Returns:
         Nothing.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'El Torito Boot Catalog not yet initialized')

        # The Eltorito Boot Catalog can only be 2048 bytes (1 extent).  By
        # default, the first 64 bytes are used by the Validation Entry and the
        # Initial Entry, which leaves 1984 bytes.  Each section takes up 32
        # bytes for the Section Header and 32 bytes for the Section Entry, for
        # a total of 64 bytes, so we can have a maximum of 1984/64 = 31
        # sections.
        if len(self.sections) == 31:
            raise pycdlibexception.PyCdlibInvalidInput(
                'Too many Eltorito sections')

        sec = EltoritoSectionHeader()
        platform_id = self.validation_entry.platform_id
        if efi:
            platform_id = 0xef
        sec.new(b'\x00' * 28, platform_id)

        secentry = EltoritoEntry()
        secentry.new(sector_count, load_seg, media_name, system_type, bootable)
        secentry.set_inode(ino)
        ino.linked_records.append(secentry)

        sec.add_new_entry(secentry)

        if self.sections:
            self.sections[-1].set_record_not_last()

        self.sections.append(sec)
Esempio n. 16
0
    def seek(self, offset, whence=0):
        # type: (int, int) -> int
        """
        Change the stream position to byte offset offset.  The offset is
        interpreted relative to the position indicated by whence.  Valid values
        for whence are:

        * 0 -- start of stream (the default); offset should be zero or positive
        * 1 -- current stream position; offset may be negative
        * 2 -- end of stream; offset is usually negative

        Parameters:
         offset - The byte offset to seek to.
         whence - The position in the file to start from (0 for start, 1 for
                  current, 2 for end)
        Returns:
         The new absolute position.
        """
        if not self._open:
            raise pycdlibexception.PyCdlibInvalidInput('I/O operation on closed file.')

        if isinstance(offset, float):
            raise pycdlibexception.PyCdlibInvalidInput('an integer is required')

        if whence == 0:
            # From beginning of file
            if offset < 0:
                raise pycdlibexception.PyCdlibInvalidInput('Invalid offset value (must be positive)')

            if offset < self._length:
                self._fp.seek(self._startpos + offset, 0)

            self._offset = offset
        elif whence == 1:
            # From current file position
            if self._offset + offset < 0:
                raise pycdlibexception.PyCdlibInvalidInput('Invalid offset value (cannot seek before start of file)')

            if self._offset + offset < self._length:
                self._fp.seek(self._startpos + self._offset + offset, 0)

            self._offset += offset
        elif whence == 2:
            # From end of file
            if offset < 0 and abs(offset) > self._length:
                raise pycdlibexception.PyCdlibInvalidInput('Invalid offset value (cannot seek before start of file)')

            if self._length + offset < self._length:
                self._fp.seek(self._startpos + self._length + offset, 0)

            self._offset = self._length + offset
        else:
            raise pycdlibexception.PyCdlibInvalidInput('Invalid value for whence (options are 0, 1, and 2)')

        return self._offset
Esempio n. 17
0
def split_path(iso_path):
    # type: (bytes) -> List[bytes]
    '''
    A function to take a fully-qualified iso path and split it into components.

    Parameters:
     iso_path - The path to split.
    Returns:
     The components of the path as a list.
    '''
    if not starts_with_slash(iso_path):
        raise pycdlibexception.PyCdlibInvalidInput('Must be a path starting with /')

    # Split the path along the slashes.  Since our paths are always absolute,
    # the front is blank.
    return iso_path.split(b'/')[1:]
Esempio n. 18
0
    def _rr_path_to_iso_path_and_rr_name(self, rr_path, is_dir):
        # type: (str, bool) -> Tuple[str, str]
        '''
        An internal method to split a Rock Ridge absolute path into an absolute
        ISO9660 path and a Rock Ridge name.  This is accomplished by finding the
        Rock Ridge directory record of the parent, then reconstructing the ISO
        parent path by walking up to the root.

        Parameters:
         rr_path - The absolute Rock Ridge path to generate for.
         is_dir - Whether this path is a directory or a file.
        Returns:
         A tuple where the first element is the absolute ISO9660 path of the
         parent, and the second element is the Rock Ridge name.
        '''
        if rr_path[0] != '/':
            raise pycdlibexception.PyCdlibInvalidInput(
                "rr_path must start with '/'")

        namesplit = utils.split_path(utils.normpath(rr_path))
        rr_name = namesplit.pop()
        rr_parent_path = b'/' + b'/'.join(namesplit)
        parent_record = self.pycdlib_obj._find_rr_record(rr_parent_path)  # pylint: disable=protected-access
        if parent_record.is_root:
            iso_parent_path = b'/'
        else:
            iso_parent_path = b''
            parent = parent_record  # type: Optional[dr.DirectoryRecord]
            while parent is not None:
                if not parent.is_root:
                    iso_parent_path = b'/' + parent.file_identifier(
                    ) + iso_parent_path
                parent = parent.parent

        if is_dir:
            iso_name = utils.mangle_dir_for_iso9660(
                rr_name.decode('utf-8'), self.pycdlib_obj.interchange_level)
        else:
            basename, ext = utils.mangle_file_for_iso9660(
                rr_name.decode('utf-8'), self.pycdlib_obj.interchange_level)
            iso_name = '.'.join([basename, ext])

        iso_path = iso_parent_path.decode('utf-8') + '/' + iso_name

        return iso_path, rr_name.decode('utf-8')
Esempio n. 19
0
    def add_parsed_entry(self, entry):
        '''
        A method to add a parsed entry to the list of entries of this header.
        If the number of parsed entries exceeds what was expected from the
        initial parsing of the header, this method will throw an Exception.

        Parameters:
         entry - The EltoritoEntry object to add to the list of entries.
        Returns:
         Nothing.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError('El Torito Section Header not yet initialized')

        if len(self.section_entries) >= self.num_section_entries:
            raise pycdlibexception.PyCdlibInvalidInput('Eltorito section had more entries than expected by section header; ISO is corrupt')

        self.section_entries.append(entry)
Esempio n. 20
0
    def new(self, platform_id):
        # type: (int) -> None
        '''
        Create a new El Torito Validation Entry.

        Parameters:
         platform_id - The platform ID to set for this validation entry.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError('El Torito Validation Entry already initialized')
        if platform_id not in (0, 1, 2, 0xef):
            raise pycdlibexception.PyCdlibInvalidInput('Invalid platform ID (must be one of 0, 1, 2, or 0xef)')

        self.platform_id = platform_id
        self.id_string = b'\x00' * 24  # FIXME: let the user set this
        self.checksum = 0
        self.checksum = self._checksum(self._record())
        self._initialized = True
Esempio n. 21
0
def normpath(path):
    # type: (str) -> bytes
    '''
    Normalize the given path, eliminating double slashes, etc.  This function is
    a copy of the built-in python normpath, except we do *not* allow double
    slashes at the start.

    Parameters:
     path - The path to normalize.
    Returns:
     The normalized path.
    '''
    sep = '/'
    empty = ''
    dot = '.'
    dotdot = '..'

    if path == empty:
        return dot.encode('utf-8')

    initial_slashes = path.startswith(sep)
    comps = path.split(sep)
    new_comps = []  # type: List[str]
    for comp in comps:
        if comp in (empty, dot):
            continue
        if comp != dotdot or (not initial_slashes and not new_comps) or (
                new_comps and new_comps[-1] == dotdot):
            new_comps.append(comp)
        elif new_comps:
            new_comps.pop()
    newpath = sep * initial_slashes + sep.join(new_comps)
    if sys.version_info >= (3, 0):
        newpath_bytes = newpath.encode('utf-8')
    else:
        newpath_bytes = newpath.decode('utf-8').encode('utf-8')
    if not starts_with_slash(newpath_bytes):
        raise pycdlibexception.PyCdlibInvalidInput(
            'Must be a path starting with /')

    return newpath_bytes
Esempio n. 22
0
    def readall(self):
        # type: () -> bytes
        """
        Read and return the remaining bytes in the file.

        Parameters:
         None.
        Returns:
         The rest of the data left in the file.  If the file is at or past EOF,
         returns an empty bytestring.
        """
        if not self._open:
            raise pycdlibexception.PyCdlibInvalidInput('I/O operation on closed file.')

        readsize = self._length - self._offset
        if readsize > 0:
            data = self._fp.read(readsize)
            self._offset += readsize
        else:
            data = b''

        return data
Esempio n. 23
0
def hdmbrcheck(disk_mbr, sector_count, bootable):
    # type: (bytes, int, bool) -> int
    '''
    A function to sanity check an El Torito Hard Drive Master Boot Record (HDMBR).
    On success, it returns the system_type (also known as the partition type) that
    should be fed into the rest of the El Torito methods.  On failure, it raises
    an exception.

    Parameters:
     disk_mbr - The data to look in.
     sector_count - The number of sectors expected in the MBR.
     bootable - Whether this MBR is bootable.
    Returns:
     The system (or partition) type the should be fed into the rest of El Torito.
    '''
    # The MBR that we want to see to do hd emulation boot for El Torito is a standard
    # x86 MBR, documented here:
    # https://en.wikipedia.org/wiki/Master_boot_record#Sector_layout
    #
    # In brief, it should consist of 512 bytes laid out like:
    # Offset 0x0 - 0x1BD:   Bootstrap code area
    # Offset 0x1BE - 0x1CD: Partition entry 1
    # Offset 0x1CE - 0x1DD: Partition entry 2
    # Offset 0x1DE - 0x1ED: Partition entry 3
    # Offset 0x1EE - 0x1FD: Partition entry 4
    # Offset 0x1FE:         0x55
    # Offset 0x1FF:         0xAA
    #
    # Each partition entry above should consist of:
    # Offset 0x0: Active (bit 7 set) or inactive (all zeros)
    # Offset 0x1 - 0x3: CHS address of first sector in partition
    #   Offset 0x1: Head
    #   Offset 0x2: Sector in bits 0-5, bits 6-7 are high bits of of cylinder
    #   Offset 0x3: bits 0-7 of cylinder
    # Offset 0x4: Partition type (almost all of these are valid, see https://en.wikipedia.org/wiki/Partition_type)
    # Offset 0x5 - 0x7: CHS address of last sector in partition (same format as first sector)
    # Offset 0x8 - 0xB: LBA of first sector in partition
    # Offset 0xC - 0xF: number of sectors in partition

    PARTITION_TYPE_UNUSED = 0x0

    PARTITION_STATUS_ACTIVE = 0x80

    (bootstrap_unused, part1, part2, part3, part4, keybyte1,
     keybyte2) = struct.unpack_from('=446s16s16s16s16sBB', disk_mbr, 0)

    if keybyte1 != 0x55 or keybyte2 != 0xAA:
        raise pycdlibexception.PyCdlibInvalidInput('Invalid magic on HD MBR')

    parts = [part1, part2, part3, part4]
    system_type = PARTITION_TYPE_UNUSED
    for part in parts:
        (status, s_head, s_seccyl, s_cyl, parttype, e_head, e_seccyl, e_cyl,
         lba_unused, num_sectors_unused) = struct.unpack('=BBBBBBBBLL', part)

        if parttype == PARTITION_TYPE_UNUSED:
            continue

        if system_type != PARTITION_TYPE_UNUSED:
            raise pycdlibexception.PyCdlibInvalidInput(
                'Boot image has multiple partitions')

        if bootable and status != PARTITION_STATUS_ACTIVE:
            # genisoimage prints a warning in this case, but we have no other
            # warning prints in the whole codebase, and an exception will probably
            # make us too fragile.  So we leave the code but don't do anything.
            with open(os.devnull, 'w') as devnull:
                print('Warning: partition not marked active', file=devnull)

        cyl = ((s_seccyl & 0xC0) << 10) | s_cyl
        sec = s_seccyl & 0x3f
        if cyl != 0 or s_head != 1 or sec != 1:
            # genisoimage prints a warning in this case, but we have no other
            # warning prints in the whole codebase, and an exception will probably
            # make us too fragile.  So we leave the code but don't do anything.
            with open(os.devnull, 'w') as devnull:
                print('Warning: partition does not start at 0/1/1',
                      file=devnull)

        cyl = ((e_seccyl & 0xC0) << 10) | e_cyl
        sec = e_seccyl & 0x3f
        geometry_sectors = (cyl + 1) * (e_head + 1) * sec

        if sector_count != geometry_sectors:
            # genisoimage prints a warning in this case, but we have no other
            # warning prints in the whole codebase, and an exception will probably
            # make us too fragile.  So we leave the code but don't do anything.
            with open(os.devnull, 'w') as devnull:
                print('Warning: image size does not match geometry',
                      file=devnull)

        system_type = parttype

    if system_type == PARTITION_TYPE_UNUSED:
        raise pycdlibexception.PyCdlibInvalidInput(
            'Boot image has no partitions')

    return system_type
Esempio n. 24
0
    def _new(self, mangledname, parent, seqnum, isdir, length, xa):
        '''
        Internal method to create a new Directory Record.

        Parameters:
         mangledname - The ISO9660 name for this directory record.
         parent - The parent of this directory record.
         seqnum - The sequence number to associate with this directory record.
         isdir - Whether this directory record represents a directory.
         length - The length of the data for this directory record.
         xa - True if this is an Extended Attribute record.
        Returns:
         Nothing.
        '''

        # Adding a new time should really be done when we are going to write
        # the ISO (in record()).  Ecma-119 9.1.5 says:
        #
        # "This field shall indicate the date and the time of the day at which
        # the information in the Extent described by the Directory Record was
        # recorded."
        #
        # We create it here just to have something in the field, but we'll
        # redo the whole thing when we are mastering.
        self.date = dates.DirectoryRecordDate()
        self.date.new()

        if length > 2**32 - 1:
            raise pycdlibexception.PyCdlibInvalidInput(
                "Maximum supported file length is 2^32-1")

        self.data_length = length
        # FIXME: if the length of the item is more than 2^32 - 1, and the
        # interchange level is 3, we should make duplicate directory record
        # entries so we can represent the whole file (see
        # http://wiki.osdev.org/ISO_9660, Size Limitations for a discussion of
        # this).

        self.file_ident = mangledname

        self.isdir = isdir

        self.seqnum = seqnum
        # For a new directory record entry, there is no original_extent_loc,
        # so we leave it at None.
        self.orig_extent_loc = None
        self.len_fi = len(self.file_ident)
        self.dr_len = struct.calcsize(self.FMT) + self.len_fi

        # From Ecma-119, 9.1.6, the file flag bits are:
        #
        # Bit 0 - Existence - 0 for existence known, 1 for hidden
        # Bit 1 - Directory - 0 for file, 1 for directory
        # Bit 2 - Associated File - 0 for not associated, 1 for associated
        # Bit 3 - Record - 0 for structure not in xattr, 1 for structure in xattr
        # Bit 4 - Protection - 0 for no owner and group in xattr, 1 for owner and group in xattr
        # Bit 5 - Reserved
        # Bit 6 - Reserved
        # Bit 7 - Multi-extent - 0 for final directory record, 1 for not final directory record
        # FIXME: We probably want to allow the existence, associated file, xattr
        # record, and multi-extent bits to be set by the caller.
        self.file_flags = 0
        if self.isdir:
            self.file_flags |= (1 << self.FILE_FLAG_DIRECTORY_BIT)
        self.file_unit_size = 0  # FIXME: we don't support setting file unit size for now
        self.interleave_gap_size = 0  # FIXME: we don't support setting interleave gap size for now
        self.xattr_len = 0  # FIXME: we don't support xattrs for now
        self.children = []

        self.parent = parent
        self.is_root = False
        if parent is None:
            # If no parent, then this is the root
            self.is_root = True

        self.xa_record = None
        if xa:
            self.xa_record = XARecord()
            self.xa_record.new()
            self.dr_len += XARecord.length()

        self.dr_len += (self.dr_len % 2)

        self.rock_ridge = None

        if self.is_root:
            self._printable_name = b'/'
        elif self.file_ident == b'\x00':
            self._printable_name = b'.'
        elif self.file_ident == b'\x01':
            self._printable_name = b'..'
        else:
            self._printable_name = self.file_ident

        self._initialized = True
Esempio n. 25
0
    def _new(self, vd, name, parent, seqnum, isdir, length, xa):
        '''
        Internal method to create a new Directory Record.

        Parameters:
         vd - The Volume Descriptor this record is part of.
         name - The name for this directory record.
         parent - The parent of this directory record.
         seqnum - The sequence number to associate with this directory record.
         isdir - Whether this directory record represents a directory.
         length - The length of the data for this directory record.
         xa - True if this is an Extended Attribute record.
        Returns:
         Nothing.
        '''

        # Adding a new time should really be done when we are going to write
        # the ISO (in record()).  Ecma-119 9.1.5 says:
        #
        # 'This field shall indicate the date and the time of the day at which
        # the information in the Extent described by the Directory Record was
        # recorded.'
        #
        # We create it here just to have something in the field, but we'll
        # redo the whole thing when we are mastering.
        self.date = dates.DirectoryRecordDate()
        self.date.new()

        if length > 2**32 - 1:
            raise pycdlibexception.PyCdlibInvalidInput(
                'Maximum supported file length is 2^32-1')

        self.data_length = length

        self.file_ident = name

        self.isdir = isdir

        self.seqnum = seqnum
        # For a new directory record entry, there is no original_extent_loc,
        # so we leave it at None.
        self.orig_extent_loc = None
        self.len_fi = len(self.file_ident)
        self.dr_len = struct.calcsize(self.FMT) + self.len_fi

        # From Ecma-119, 9.1.6, the file flag bits are:
        #
        # Bit 0 - Existence - 0 for existence known, 1 for hidden
        # Bit 1 - Directory - 0 for file, 1 for directory
        # Bit 2 - Associated File - 0 for not associated, 1 for associated
        # Bit 3 - Record - 0=structure not in xattr, 1=structure in xattr
        # Bit 4 - Protection - 0=no owner and group, 1=owner and group in xattr
        # Bit 5 - Reserved
        # Bit 6 - Reserved
        # Bit 7 - Multi-extent - 0=final directory record, 1=not final directory record
        self.file_flags = 0
        if self.isdir:
            self.file_flags |= (1 << self.FILE_FLAG_DIRECTORY_BIT)
        self.file_unit_size = 0  # FIXME: we don't support setting file unit size for now
        self.interleave_gap_size = 0  # FIXME: we don't support setting interleave gap size for now
        self.xattr_len = 0  # FIXME: we don't support xattrs for now

        self.parent = parent
        if parent is None:
            # If no parent, then this is the root
            self.is_root = True

        if xa:
            self.xa_record = XARecord()
            self.xa_record.new()
            self.dr_len += XARecord.length()

        self.dr_len += (self.dr_len % 2)

        if self.is_root:
            self._printable_name = b'/'
        elif self.file_ident == b'\x00':
            self._printable_name = b'.'
        elif self.file_ident == b'\x01':
            self._printable_name = b'..'
        else:
            self._printable_name = self.file_ident

        self.vd = vd

        self._initialized = True
Esempio n. 26
0
    def _add_child(self, child, logical_block_size, allow_duplicate,
                   check_overflow):
        '''
        An internal method to add a child to this object.  Note that this is called both
        during parsing and when adding a new object to the system, so it
        it shouldn't have any functionality that is not appropriate for both.

        Parameters:
         child - The child directory record object to add.
         logical_block_size - The size of a logical block for this volume descriptor.
         allow_duplicate - Whether to allow duplicate names, as there are situations where duplicate children are allowed.
         check_overflow - Whether to check for overflow; if we are parsing, we don't want to do this.
        Returns:
         True if adding this child caused the directory to overflow into another
         extent, False otherwise.
        '''
        if not self.isdir:
            raise pycdlibexception.PyCdlibInvalidInput(
                'Trying to add a child to a record that is not a directory')

        # First ensure that this is not a duplicate.  For speed purposes, we
        # recognize that bisect_left will always choose an index to the *left*
        # of a duplicate child.  Thus, to check for duplicates we only need to
        # see if the child to be added is a duplicate with the entry that
        # bisect_left returned.
        index = bisect.bisect_left(self.children, child)
        if index != len(self.children) and self.children[
                index].file_ident == child.file_ident:
            if not self.children[index].is_associated_file(
            ) and not child.is_associated_file():
                if not (self.rock_ridge is not None
                        and self.file_identifier() == b'RR_MOVED'):
                    if not allow_duplicate:
                        raise pycdlibexception.PyCdlibInvalidInput(
                            'Failed adding duplicate name to parent')
                    else:
                        self.children[index].data_continuation = child
                        index += 1
        self.children.insert(index, child)

        if child.rock_ridge is not None and not child.is_dot(
        ) and not child.is_dotdot():
            lo = 0
            hi = len(self.rr_children)
            while lo < hi:
                mid = (lo + hi) // 2
                if self.rr_children[mid].rock_ridge.name(
                ) < child.rock_ridge.name():
                    lo = mid + 1
                else:
                    hi = mid
            rr_index = lo

            self.rr_children.insert(rr_index, child)

        # We now have to check if we need to add another logical block.
        # We have to iterate over the entire list again, because where we
        # placed this last entry may rearrange the empty spaces in the blocks
        # that we've already allocated.
        num_extents, offset_unused = self._recalculate_extents_and_offsets(
            index, logical_block_size)

        overflowed = False
        if check_overflow and (num_extents * logical_block_size >
                               self.data_length):
            overflowed = True
            # When we overflow our data length, we always add a full block.
            self.data_length += logical_block_size
            # We also have to make sure to update the length of the dot child,
            # as that should always reflect the length.
            self.children[0].data_length = self.data_length
            # We also have to update all of the dotdot entries.  If this is
            # the root directory record (no parent), we first update the root
            # dotdot entry.  In all cases, we update the dotdot entry of all
            # children that are directories.
            if self.parent is None:
                self.children[1].data_length = self.data_length

            for c in self.children:
                if not c.is_dir():
                    continue
                if len(c.children) > 1:
                    c.children[1].data_length = self.data_length

        return overflowed
Esempio n. 27
0
    def new(self, efi, mac, part_entry, mbr_id, part_offset, geometry_sectors,
            geometry_heads, part_type):
        # type: (bool, bool, int, Optional[int], int, int, int, int) -> None
        '''
        Add ISO hybridization to an ISO.

        Parameters:
         efi - Whether this ISO should be setup for EFI boot.
         mac - Whether this ISO should be made bootable for the Macintosh.
         part_entry - The partition entry for the hybridization.
         mbr_id - The mbr_id to use for the hybridization.
         part_offset - The partition offset to use for the hybridization.
         geometry_sectors - The number of sectors to use for the hybridization.
         geometry_heads - The number of heads to use for the hybridization.
         part_type - The partition type for the hybridization.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'This IsoHybrid object is already initialized')

        if geometry_sectors < 1 or geometry_sectors > 63:
            raise pycdlibexception.PyCdlibInvalidInput(
                'Geometry sectors can only be between 1 and 63, inclusive')

        if geometry_heads < 1 or geometry_heads > 256:
            raise pycdlibexception.PyCdlibInvalidInput(
                'Geometry heads can only be between 1 and 256, inclusive')

        if mac and part_type != 0:
            raise pycdlibexception.PyCdlibInvalidInput(
                'When generating for Mac, partition type must be 0')

        isohybrid_data_hd0 = b'\x33\xed\xfa\x8e\xd5\xbc\x00\x7c\xfb\xfc\x66\x31\xdb\x66\x31\xc9\x66\x53\x66\x51\x06\x57\x8e\xdd\x8e\xc5\x52\xbe\x00\x7c\xbf\x00\x06\xb9\x00\x01\xf3\xa5\xea\x4b\x06\x00\x00\x52\xb4\x41\xbb\xaa\x55\x31\xc9\x30\xf6\xf9\xcd\x13\x72\x16\x81\xfb\x55\xaa\x75\x10\x83\xe1\x01\x74\x0b\x66\xc7\x06\xf1\x06\xb4\x42\xeb\x15\xeb\x00\x5a\x51\xb4\x08\xcd\x13\x83\xe1\x3f\x5b\x51\x0f\xb6\xc6\x40\x50\xf7\xe1\x53\x52\x50\xbb\x00\x7c\xb9\x04\x00\x66\xa1\xb0\x07\xe8\x44\x00\x0f\x82\x80\x00\x66\x40\x80\xc7\x02\xe2\xf2\x66\x81\x3e\x40\x7c\xfb\xc0\x78\x70\x75\x09\xfa\xbc\xec\x7b\xea\x44\x7c\x00\x00\xe8\x83\x00\x69\x73\x6f\x6c\x69\x6e\x75\x78\x2e\x62\x69\x6e\x20\x6d\x69\x73\x73\x69\x6e\x67\x20\x6f\x72\x20\x63\x6f\x72\x72\x75\x70\x74\x2e\x0d\x0a\x66\x60\x66\x31\xd2\x66\x03\x06\xf8\x7b\x66\x13\x16\xfc\x7b\x66\x52\x66\x50\x06\x53\x6a\x01\x6a\x10\x89\xe6\x66\xf7\x36\xe8\x7b\xc0\xe4\x06\x88\xe1\x88\xc5\x92\xf6\x36\xee\x7b\x88\xc6\x08\xe1\x41\xb8\x01\x02\x8a\x16\xf2\x7b\xcd\x13\x8d\x64\x10\x66\x61\xc3\xe8\x1e\x00\x4f\x70\x65\x72\x61\x74\x69\x6e\x67\x20\x73\x79\x73\x74\x65\x6d\x20\x6c\x6f\x61\x64\x20\x65\x72\x72\x6f\x72\x2e\x0d\x0a\x5e\xac\xb4\x0e\x8a\x3e\x62\x04\xb3\x07\xcd\x10\x3c\x0a\x75\xf1\xcd\x18\xf4\xeb\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

        self.mbr = isohybrid_data_hd0
        self.rba = 0  # This will be set later
        self.mbr_id = mbr_id
        if self.mbr_id is None:
            self.mbr_id = random.getrandbits(32)

        self.part_entry = part_entry
        self.bhead = (part_offset // geometry_sectors) % geometry_heads
        self.bsect = (part_offset % geometry_sectors) + 1
        self.bcyle = part_offset // (geometry_heads * geometry_sectors)
        self.bsect += (self.bcyle & 0x300) >> 2
        self.bcyle &= 0xff
        self.ptype = part_type
        self.ehead = geometry_heads - 1
        self.part_offset = part_offset
        self.geometry_heads = geometry_heads
        self.geometry_sectors = geometry_sectors

        self.mac = mac
        if self.mac:
            self.header = self.MAC_AFP
            self.mac_lba = 0  # this will be set later
            self.mac_count = 0  # this will be set later
        else:
            self.header = self.ORIG_HEADER

        self.efi = efi
        if self.efi:
            self.efi_lba = 0  # this will be set later
            self.efi_count = 0  # this will be set later
            self.primary_gpt.new(self.mac)
            self.secondary_gpt.new(self.mac)

        self._initialized = True
Esempio n. 28
0
 def __init__(self, pycdlib_obj):
     # type: (pycdlib.PyCdlib) -> None
     if not pycdlib_obj.has_udf():
         raise pycdlibexception.PyCdlibInvalidInput(
             'Can only instantiate a UDF facade for a UDF ISO')
     self.pycdlib_obj = pycdlib_obj