Пример #1
0
    def record_big_endian(self):
        '''
        A method to generate a string representing the big endian version of
        this Path Table Record.

        Parameters:
         None.
        Returns:
         A string representing the big endian version of this Path Table Record.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError("Path Table Record not yet initialized")

        return self._record(utils.swab_32bit(self.extent_location),
                            utils.swab_16bit(self.parent_directory_num))
Пример #2
0
    def record_big_endian(self):
        '''
        A method to generate a string representing the big endian version of
        this Path Table Record.

        Parameters:
         None.
        Returns:
         A string representing the big endian version of this Path Table Record.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError("Path Table Record not yet initialized")

        return self._record(utils.swab_32bit(self.extent_location),
                            utils.swab_16bit(self.parent_directory_num))
Пример #3
0
    def new(self, platform_id):
        '''
        A method to 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')

        self.platform_id = platform_id
        self.id_string = b'\x00' * 24  # FIXME: let the user set this
        self.checksum = 0
        self.checksum = utils.swab_16bit(self._checksum(self._record()) - 1)
        self._initialized = True
Пример #4
0
    def record_big_endian(self):
        # type: () -> bytes
        """
        Generate a string representing the big endian version of
        this Path Table Record.

        Parameters:
         None.
        Returns:
         A string representing the big endian version of this Path Table Record.
        """
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'Path Table Record not initialized')

        return self._record(utils.swab_32bit(self.extent_location),
                            utils.swab_16bit(self.parent_directory_num))
Пример #5
0
    def new(self, platform_id):
        # type: (int) -> None
        '''
        A method to 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')

        self.platform_id = platform_id
        self.id_string = b'\x00' * 24  # FIXME: let the user set this
        self.checksum = 0
        self.checksum = utils.swab_16bit(self._checksum(self._record()) - 1)
        self._initialized = True
Пример #6
0
    def record(self):
        '''
        A method to generate the string representing this Directory Record.

        Parameters:
         None.
        Returns:
         String representing this Directory Record.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'Directory Record not yet initialized')

        # Ecma-119 9.1.5 says the date should reflect the time when the
        # record was written, so we make a new date now and use that to
        # write out the record.
        self.date = dates.DirectoryRecordDate()
        self.date.new()

        padlen = struct.calcsize(self.FMT) + self.len_fi
        padstr = b'\x00' * (padlen % 2)

        extent_loc = self._extent_location()

        xa_rec = b''
        if self.xa_record is not None:
            xa_rec = b'\x00' * self.xa_pad_size + self.xa_record.record()
        rr_rec = b''
        if self.rock_ridge is not None:
            rr_rec = self.rock_ridge.record_dr_entries()

        outlist = [
            struct.pack(self.FMT, self.dr_len, self.xattr_len, extent_loc,
                        utils.swab_32bit(extent_loc), self.data_length,
                        utils.swab_32bit(self.data_length), self.date.record(),
                        self.file_flags, self.file_unit_size,
                        self.interleave_gap_size, self.seqnum,
                        utils.swab_16bit(self.seqnum), self.len_fi) +
            self.file_ident + padstr + xa_rec + rr_rec
        ]

        outlist.append(b'\x00' * (len(outlist[0]) % 2))

        return b''.join(outlist)
Пример #7
0
    def new(self, platform_id):
        '''
        A method to 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")

        self.header_id = 1
        self.platform_id = platform_id
        self.id_string = b"\x00" * 24  # FIXME: let the user set this
        self.keybyte1 = 0x55
        self.keybyte2 = 0xaa
        self.checksum = 0
        self.checksum = utils.swab_16bit(self._checksum(self._record()) - 1)
        self.initialized = True
Пример #8
0
    def record(self):
        '''
        A method to generate the string representing this Directory Record.

        Parameters:
         None.
        Returns:
         String representing this Directory Record.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError("Directory Record not yet initialized")

        # Ecma-119 9.1.5 says the date should reflect the time when the
        # record was written, so we make a new date now and use that to
        # write out the record.
        self.date = dates.DirectoryRecordDate()
        self.date.new()

        padlen = struct.calcsize(self.FMT) + self.len_fi
        padstr = b'\x00' * (padlen % 2)

        extent_loc = self._extent_location()

        xa_rec = b""
        if self.xa_record is not None:
            xa_rec = b'\x00' * self.xa_pad_size + self.xa_record.record()
        rr_rec = b""
        if self.rock_ridge is not None:
            rr_rec = self.rock_ridge.record_dr_entries()

        outlist = [struct.pack(self.FMT, self.dr_len, self.xattr_len,
                               extent_loc, utils.swab_32bit(extent_loc),
                               self.data_length, utils.swab_32bit(self.data_length),
                               self.date.record(), self.file_flags,
                               self.file_unit_size, self.interleave_gap_size,
                               self.seqnum, utils.swab_16bit(self.seqnum),
                               self.len_fi) + self.file_ident + padstr + xa_rec + rr_rec]

        outlist.append(b'\x00' * (len(outlist[0]) % 2))

        return b"".join(outlist)
Пример #9
0
    def equal_to_be(self, be_record):
        '''
        A method to compare a little-endian path table record to its
        big-endian counterpart.  This is used to ensure that the ISO is sane.

        Parameters:
         be_record - The big-endian object to compare with the little-endian
                     object.
        Returns:
         Nothing.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError("This Path Table Record is not yet initialized")

        if be_record.len_di != self.len_di or \
           be_record.xattr_length != self.xattr_length or \
           utils.swab_32bit(be_record.extent_location) != self.extent_location or \
           utils.swab_16bit(be_record.parent_directory_num) != self.parent_directory_num or \
           be_record.directory_identifier != self.directory_identifier:
            return False
        return True
Пример #10
0
    def equal_to_be(self, be_record):
        '''
        A method to compare a little-endian path table record to its
        big-endian counterpart.  This is used to ensure that the ISO is sane.

        Parameters:
         be_record - The big-endian object to compare with the little-endian
                     object.
        Returns:
         Nothing.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError("This Path Table Record is not yet initialized")

        if be_record.len_di != self.len_di or \
           be_record.xattr_length != self.xattr_length or \
           utils.swab_32bit(be_record.extent_location) != self.extent_location or \
           utils.swab_16bit(be_record.parent_directory_num) != self.parent_directory_num or \
           be_record.directory_identifier != self.directory_identifier:
            return False
        return True
Пример #11
0
    def equal_to_be(self, be_record):
        # type: (PathTableRecord) -> bool
        '''
        Compare a little-endian path table record to its big-endian counterpart.
        This is used to ensure that the ISO is sane.

        Parameters:
         be_record - The big-endian object to compare with the little-endian
                     object.
        Returns:
         True if this record is equal to the big-endian record passed in,
         False otherwise.
        '''
        if not self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'Path Table Record not initialized')

        if be_record.len_di != self.len_di or \
           be_record.xattr_length != self.xattr_length or \
           utils.swab_32bit(be_record.extent_location) != self.extent_location or \
           utils.swab_16bit(be_record.parent_directory_num) != self.parent_directory_num or \
           be_record.directory_identifier != self.directory_identifier:
            return False
        return True
Пример #12
0
    def parse(self, vd, record, parent):
        '''
        Parse a directory record out of a string.

        Parameters:
         vd - The Volume Descriptor this record is part of.
         record - The string to parse for this record.
         parent - The parent of this record.
        Returns:
         True if this Directory Record has Rock Ridge extensions, False otherwise.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'Directory Record already initialized')

        if len(record) > 255:
            # Since the length is supposed to be 8 bits, this should never
            # happen.
            raise pycdlibexception.PyCdlibInvalidISO(
                'Directory record longer than 255 bytes!')

        (self.dr_len, self.xattr_len, extent_location_le, extent_location_be,
         data_length_le, data_length_be_unused, dr_date, self.file_flags,
         self.file_unit_size, self.interleave_gap_size, seqnum_le, seqnum_be,
         self.len_fi) = struct.unpack_from(self.FMT, record[:33], 0)

        # In theory we should have a check here that checks to make sure that
        # the length of the record we were passed in matches the data record
        # length.  However, we have seen ISOs in the wild where this is
        # incorrect, so we elide the check here.

        if extent_location_le != utils.swab_32bit(extent_location_be):
            raise pycdlibexception.PyCdlibInvalidISO(
                'Little-endian (%d) and big-endian (%d) extent location disagree'
                % (extent_location_le, utils.swab_32bit(extent_location_be)))
        self.orig_extent_loc = extent_location_le

        # Theoretically, we should check to make sure that the little endian
        # data length is the same as the big endian data length.  In practice,
        # though, we've seen ISOs where this is wrong.  Skip the check, and just
        # pick the little-endian as the 'actual' size, and hope for the best.

        self.data_length = data_length_le

        if seqnum_le != utils.swab_16bit(seqnum_be):
            raise pycdlibexception.PyCdlibInvalidISO(
                'Little-endian and big-endian seqnum disagree')
        self.seqnum = seqnum_le

        self.date = dates.DirectoryRecordDate()
        self.date.parse(dr_date)

        # OK, we've unpacked what we can from the beginning of the string.  Now
        # we have to use the len_fi to get the rest.

        self.parent = parent
        self.vd = vd

        if self.parent is None:
            self.is_root = True

            # A root directory entry should always be exactly 34 bytes.
            # However, we have seen ISOs in the wild that get this wrong, so we
            # elide a check for it.

            self.file_ident = bytes(bytearray([record[33]]))

            # A root directory entry should always have 0 as the identifier.
            # However, we have seen ISOs in the wild that don't have this set
            # properly to 0.  In that case, we override what we parsed out from
            # the original with the correct value (\x00), and hope for the best.
            if self.file_ident != b'\x00':
                self.file_ident = b'\x00'
            self.isdir = True
        else:
            record_offset = 33
            self.file_ident = record[record_offset:record_offset + self.len_fi]
            record_offset += self.len_fi
            if self.file_flags & (1 << self.FILE_FLAG_DIRECTORY_BIT):
                self.isdir = True

            if self.len_fi % 2 == 0:
                record_offset += 1

            if len(record[record_offset:]) >= XARecord.length():
                xa_rec = XARecord()

                try:
                    xa_rec.parse(record[record_offset:record_offset +
                                        XARecord.length()])
                    self.xa_record = xa_rec
                    record_offset += XARecord.length()
                except pycdlibexception.PyCdlibInvalidISO:
                    # We've seen some ISOs in the wild (Windows 98 SE) that
                    # put the XA record all the way at the back, with some
                    # padding.  Try again from the back.
                    try:
                        xa_rec.parse(record[-XARecord.length():])
                        self.xa_record = xa_rec
                        self.xa_pad_size = len(
                            record) - record_offset - XARecord.length()
                        record_offset = len(record)
                    except pycdlibexception.PyCdlibInvalidISO:
                        pass

            if len(record[record_offset:]
                   ) >= 2 and record[record_offset:record_offset + 2] in (
                       b'SP', b'RR', b'CE', b'PX', b'ER', b'ES', b'PN', b'SL',
                       b'NM', b'CL', b'PL', b'TF', b'SF', b'RE'):
                self.rock_ridge = rockridge.RockRidge()
                is_first_dir_record_of_root = self.file_ident == b'\x00' and parent.parent is None

                if is_first_dir_record_of_root:
                    bytes_to_skip = 0
                elif parent.parent is None:
                    bytes_to_skip = parent.children[0].rock_ridge.bytes_to_skip
                else:
                    bytes_to_skip = parent.rock_ridge.bytes_to_skip

                self.rock_ridge.parse(record[record_offset:],
                                      is_first_dir_record_of_root,
                                      bytes_to_skip, False)

        if self.xattr_len != 0:
            if self.file_flags & (1 << self.FILE_FLAG_RECORD_BIT):
                raise pycdlibexception.PyCdlibInvalidISO(
                    'Record Bit not allowed with Extended Attributes')
            if self.file_flags & (1 << self.FILE_FLAG_PROTECTION_BIT):
                raise pycdlibexception.PyCdlibInvalidISO(
                    'Protection Bit not allowed with Extended Attributes')

        if self.rock_ridge is None:
            ret = None
        else:
            ret = self.rock_ridge.rr_version

        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

        return ret
Пример #13
0
    def parse(self, record, data_fp, parent):
        '''
        Parse a directory record out of a string.

        Parameters:
         record - The string to parse for this record.
         data_fp - The file object to associate with this record.
         parent - The parent of this record.
        Returns:
         True if this Directory Record has Rock Ridge extensions, False otherwise.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError("Directory Record already initialized")

        if len(record) > 255:
            # Since the length is supposed to be 8 bits, this should never
            # happen.
            raise pycdlibexception.PyCdlibInvalidISO("Directory record longer than 255 bytes!")

        (self.dr_len, self.xattr_len, extent_location_le, extent_location_be,
         data_length_le, data_length_be_unused, dr_date, self.file_flags,
         self.file_unit_size, self.interleave_gap_size, seqnum_le, seqnum_be,
         self.len_fi) = struct.unpack_from(self.FMT, record[:33], 0)

        # In theory we should have a check here that checks to make sure that
        # the length of the record we were passed in matches the data record
        # length.  However, we have seen ISOs in the wild where this is
        # incorrect, so we elide the check here.

        if extent_location_le != utils.swab_32bit(extent_location_be):
            raise pycdlibexception.PyCdlibInvalidISO("Little-endian (%d) and big-endian (%d) extent location disagree" % (extent_location_le, utils.swab_32bit(extent_location_be)))
        self.orig_extent_loc = extent_location_le
        self.new_extent_loc = None

        # Theoretically, we should check to make sure that the little endian
        # data length is the same as the big endian data length.  In practice,
        # though, we've seen ISOs where this is wrong.  Skip the check, and just
        # pick the little-endian as the "actual" size, and hope for the best.

        self.data_length = data_length_le

        if seqnum_le != utils.swab_16bit(seqnum_be):
            raise pycdlibexception.PyCdlibInvalidISO("Little-endian and big-endian seqnum disagree")
        self.seqnum = seqnum_le

        self.date = dates.DirectoryRecordDate()
        self.date.parse(dr_date)

        # OK, we've unpacked what we can from the beginning of the string.  Now
        # we have to use the len_fi to get the rest.

        self.children = []
        self.is_root = False
        self.isdir = False
        self.parent = parent
        self.data_fp = data_fp

        self.rock_ridge = None

        self.xa_record = None

        if self.parent is None:
            self.is_root = True

            # A root directory entry should always be exactly 34 bytes.
            # However, we have seen ISOs in the wild that get this wrong, so we
            # elide a check for it.

            self.file_ident = bytes(bytearray([record[33]]))

            # A root directory entry should always have 0 as the identifier.
            if self.file_ident != b'\x00':
                raise pycdlibexception.PyCdlibInvalidISO("Invalid root directory entry identifier")
            self.isdir = True
        else:
            record_offset = 33
            self.file_ident = record[record_offset:record_offset + self.len_fi]
            record_offset += self.len_fi
            if self.file_flags & (1 << self.FILE_FLAG_DIRECTORY_BIT):
                self.isdir = True
            else:
                self.original_data_location = self.DATA_ON_ORIGINAL_ISO

            if self.len_fi % 2 == 0:
                record_offset += 1

            if len(record[record_offset:]) >= XARecord.length():
                xa_rec = XARecord()

                try:
                    xa_rec.parse(record[record_offset:record_offset + XARecord.length()])
                    self.xa_record = xa_rec
                    record_offset += XARecord.length()
                except pycdlibexception.PyCdlibInvalidISO:
                    # We've seen some ISOs in the wild (Windows 98 SE) that
                    # put the XA record all the way at the back, with some
                    # padding.  Try again from the back.
                    try:
                        xa_rec.parse(record[-XARecord.length():])
                        self.xa_record = xa_rec
                        self.xa_pad_size = len(record) - record_offset - XARecord.length()
                        record_offset = len(record)
                    except pycdlibexception.PyCdlibInvalidISO:
                        pass

            if len(record[record_offset:]) >= 2 and record[record_offset:record_offset + 2] in [b'SP', b'RR', b'CE', b'PX', b'ER', b'ES', b'PN', b'SL', b'NM', b'CL', b'PL', b'TF', b'SF', b'RE']:
                self.rock_ridge = rockridge.RockRidge()
                is_first_dir_record_of_root = self.file_ident == b'\x00' and parent.parent is None

                if is_first_dir_record_of_root:
                    bytes_to_skip = 0
                elif parent.parent is None:
                    bytes_to_skip = parent.children[0].rock_ridge.bytes_to_skip
                else:
                    bytes_to_skip = parent.rock_ridge.bytes_to_skip

                self.rock_ridge.parse(record[record_offset:],
                                      is_first_dir_record_of_root,
                                      bytes_to_skip,
                                      False)

        if self.xattr_len != 0:
            if self.file_flags & (1 << self.FILE_FLAG_RECORD_BIT):
                raise pycdlibexception.PyCdlibInvalidISO("Record Bit not allowed with Extended Attributes")
            if self.file_flags & (1 << self.FILE_FLAG_PROTECTION_BIT):
                raise pycdlibexception.PyCdlibInvalidISO("Protection Bit not allowed with Extended Attributes")

        self._initialized = True

        if self.rock_ridge is None:
            ret = None
        else:
            ret = self.rock_ridge.rr_version

        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

        return ret