Exemplo n.º 1
0
    def parse(self, valstr):
        '''
        A method to parse an El Torito Validation Entry out of a string.

        Parameters:
         valstr - The string to parse the El Torito Validation Entry out of.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError('El Torito Validation Entry already initialized')

        (header_id, self.platform_id, reserved_unused, self.id_string,
         self.checksum, keybyte1,
         keybyte2) = struct.unpack_from(self.FMT, valstr, 0)

        if header_id != 1:
            raise pycdlibexception.PyCdlibInvalidISO('El Torito Validation entry header ID not 1')

        if self.platform_id not in (0, 1, 2):
            raise pycdlibexception.PyCdlibInvalidISO('El Torito Validation entry platform ID not valid')

        if keybyte1 != 0x55:
            raise pycdlibexception.PyCdlibInvalidISO('El Torito Validation entry first keybyte not 0x55')
        if keybyte2 != 0xaa:
            raise pycdlibexception.PyCdlibInvalidISO('El Torito Validation entry second keybyte not 0xaa')

        # Now that we've done basic checking, calculate the checksum of the
        # validation entry and make sure it is right.
        if self._checksum(valstr) != 0:
            raise pycdlibexception.PyCdlibInvalidISO('El Torito Validation entry checksum not correct')

        self._initialized = True
Exemplo n.º 2
0
    def parse(self, valstr):
        '''
        A method to parse an El Torito Entry out of a string.

        Parameters:
         valstr - The string to parse the El Torito Entry out of.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError('El Torito Entry already initialized')

        (self.boot_indicator, self.boot_media_type, self.load_segment,
         self.system_type, unused1, self.sector_count, self.load_rba,
         self.selection_criteria_type,
         self.selection_criteria) = struct.unpack_from(self.FMT, valstr, 0)

        if self.boot_indicator not in (0x88, 0x00):
            raise pycdlibexception.PyCdlibInvalidISO('Invalid eltorito initial entry boot indicator')
        if self.boot_media_type > 4:
            raise pycdlibexception.PyCdlibInvalidISO('Invalid eltorito boot media type')

        # FIXME: check that the system type matches the partition table

        if unused1 != 0:
            raise pycdlibexception.PyCdlibInvalidISO('El Torito unused field must be 0')

        # According to the specification, the El Torito unused end field (bytes
        # 0xc - 0x1f, unused2 field) should be all zero.  However, we have found
        # ISOs in the wild where that is not the case, so skip that particular
        # check here.

        self._initialized = True
Exemplo n.º 3
0
    def parse(self, xastr):
        '''
        Parse an Extended Attribute Record out of a string.

        Parameters:
         xastr - The string to parse.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'This XARecord is already initialized!')

        (self._group_id, self._user_id, self._attributes, signature,
         self._filenum, unused) = struct.unpack_from(self.FMT, xastr, 0)

        if signature != b'XA':
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid signature on the XARecord!')

        if unused != b'\x00\x00\x00\x00\x00':
            raise pycdlibexception.PyCdlibInvalidISO(
                'Unused fields should be 0')

        self._initialized = True
Exemplo n.º 4
0
    def parse(self, instr):
        # type: (bytes) -> int
        '''
        Parse a GPT Partition Header out of existing data.

        Parameters:
         instr - The data to parse.
        Returns:
         The number of bytes consumed.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'This GPTPartHeader object is already initialized')

        (self.part_type_guid, part_guid, self.first_lba, self.last_lba,
         self.attributes,
         name) = struct.unpack_from(self.FMT,
                                    instr[:struct.calcsize(self.FMT)], 0)

        if self.part_type_guid not in (self.BASIC_PARTITION,
                                       self.HFS_PARTITION):
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid Partition Type UUID')

        self.name = name.decode('utf-16_le').rstrip('\x00')

        self.part_guid = uuid.UUID(bytes=part_guid)

        self._initialized = True

        return struct.calcsize(self.FMT)
Exemplo n.º 5
0
    def parse(self, instr):
        # type: (bytes) -> None
        '''
        Parse an APM Partition Header out of existing data.

        Parameters:
         instr - The data to parse.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'This APMPartHeader object is already initialized')

        (sig, resv_unused, self.map_count, self.start_block, self.block_count,
         name, type_desc, self.data_start, self.data_count, self.status,
         self.boot_start, self.boot_count, self.boot_load, self.boot_load2,
         self.boot_entry, self.boot_entry2, self.boot_cksum, self.processor,
         self.driver_sig,
         padding_unused) = struct.unpack_from(self.FMT, instr, 0)

        if sig != self.MAC_PARTITION_MAGIC:
            raise pycdlibexception.PyCdlibInvalidISO('Invalid APM signature')

        self.name = name.decode('ascii').rstrip('\x00')
        self.type_desc = type_desc.decode('ascii').rstrip('\x00')

        self._initialized = True
Exemplo n.º 6
0
    def parse(self, datestr):
        '''
        Parse a Volume Descriptor Date out of a string.  A string of all zeros
        is valid, which means that the date in this field was not specified.

        Parameters:
          datestr - string to be parsed
        Returns:
          Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'This Volume Descriptor Date object is already initialized')

        if len(datestr) != 17:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid ISO9660 date string')

        timestruct = string_to_timestruct(datestr[:-3])
        self.year = timestruct.tm_year
        self.month = timestruct.tm_mon
        self.dayofmonth = timestruct.tm_mday
        self.hour = timestruct.tm_hour
        self.minute = timestruct.tm_min
        self.second = timestruct.tm_sec
        if timestruct.tm_year == 0 and timestruct.tm_mon == 0 and timestruct.tm_mday == 0 and timestruct.tm_hour == 0 and timestruct.tm_min == 0 and timestruct.tm_sec == 0:
            self.hundredthsofsecond = 0
            self.gmtoffset = 0
            self.date_str = self.EMPTY_STRING
        else:
            self.hundredthsofsecond = int(datestr[14:15])
            self.gmtoffset, = struct.unpack_from('=b', datestr, 16)
            self.date_str = datestr

        self._initialized = True
Exemplo n.º 7
0
    def parse(self, instr):
        # type: (bytes) -> int
        '''
        Parse a GPT Header out of existing data.

        Parameters:
         instr - The data to parse.
        Returns:
         The number of bytes consumed.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'This GPTHeader object is already initialized')

        (sig, revision, header_size, header_crc_unused, resv1_unused,
         self.current_lba, self.backup_lba, self.first_usable_lba,
         self.last_usable_lba, disk_guid, self.partition_entries_lba,
         self.num_parts, self.size_of_partition_entries,
         partition_entries_crc_unused, resv2_unused) = struct.unpack_from(
             self.FMT, instr[:struct.calcsize(self.FMT)], 0)

        if sig != self.GPT_SIG:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Failed to find GPT signature while parsing GPT Header')

        if revision != self.GPT_REV:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Failed to find GPT revision while parsing GPT Header')

        if header_size != self.GPT_HEADER_SIZE:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid GPT Header size while parsing GPT Header')

        self.disk_guid = uuid.UUID(bytes=disk_guid)

        self._initialized = True

        return struct.calcsize(self.FMT)
Exemplo n.º 8
0
    def parse(self, datestr):
        '''
        Parse a Volume Descriptor Date out of a string.  A string of all zeros
        is valid, which means that the date in this field was not specified.

        Parameters:
          datestr - string to be parsed
        Returns:
          Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                "This Volume Descriptor Date object is already initialized")

        if len(datestr) != 17:
            raise pycdlibexception.PyCdlibInvalidISO(
                "Invalid ISO9660 date string")

        if datestr == self.EMPTY_STRING or datestr == b'\x00' * 17 or datestr == b'0' * 17:
            # Ecma-119, 8.4.26.1 specifies that if the string was all the
            # digit zero, with the last byte 0, the time wasn't specified.
            # However, in practice I have found that some ISOs specify this
            # field as all the number 0, so we allow both.
            self.year = 0
            self.month = 0
            self.dayofmonth = 0
            self.hour = 0
            self.minute = 0
            self.second = 0
            self.hundredthsofsecond = 0
            self.gmtoffset = 0
            self.present = False
        else:
            timestruct = time.strptime(datestr[:-3].decode('utf-8'),
                                       self.TIME_FMT)
            self.year = timestruct.tm_year
            self.month = timestruct.tm_mon
            self.dayofmonth = timestruct.tm_mday
            self.hour = timestruct.tm_hour
            self.minute = timestruct.tm_min
            self.second = timestruct.tm_sec
            self.hundredthsofsecond = int(datestr[14:15])
            self.gmtoffset, = struct.unpack_from("=b", datestr, 16)
            self.present = True

        self._initialized = True
        self.date_str = datestr
Exemplo n.º 9
0
    def parse(self, datestr):
        # type: (bytes) -> None
        """
        Parse a Volume Descriptor Date out of a string.  A string of all zeros
        is valid, which means that the date in this field was not specified.

        Parameters:
         datestr - string to be parsed
        Returns:
         Nothing.
        """
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'This Volume Descriptor Date object is already initialized')

        if len(datestr) != 17:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid ISO9660 date string')

        timestruct = string_to_timestruct(datestr[:-3])
        self.year = timestruct.tm_year
        self.month = timestruct.tm_mon
        self.dayofmonth = timestruct.tm_mday
        self.hour = timestruct.tm_hour
        self.minute = timestruct.tm_min
        self.second = timestruct.tm_sec
        if timestruct.tm_year == 0 and timestruct.tm_mon == 0 and timestruct.tm_mday == 0 and timestruct.tm_hour == 0 and timestruct.tm_min == 0 and timestruct.tm_sec == 0:
            self.hundredthsofsecond = 0
            self.gmtoffset = 0
            self.date_str = self.EMPTY_STRING
        else:
            try:
                self.hundredthsofsecond = int(datestr[14:16])
            except ValueError:
                # We've seen ISOs in the wild (made by MagicISO) that fill
                # hundredthsofseconds with b'\x00\x00'.  Handle that here.
                self.hundredthsofsecond, = struct.unpack('>H', datestr[14:16])
            self.gmtoffset, = struct.unpack_from('=b', datestr, 16)
            self.date_str = datestr

        self._initialized = True
Exemplo n.º 10
0
    def add_parsed_entry(self, entry):
        # type: (EltoritoEntry) -> None
        '''
        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 initialized')

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

        self.section_entries.append(entry)
Exemplo n.º 11
0
    def parse(self, valstr):
        # type: (bytes) -> bool
        '''
        A method to parse an El Torito Boot Catalog out of a string.

        Parameters:
         valstr - The string to parse the El Torito Boot Catalog out of.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'El Torito Boot Catalog already initialized')

        if self.state == self.EXPECTING_VALIDATION_ENTRY:
            # The first entry in an El Torito boot catalog is the Validation
            # Entry.  A Validation entry consists of 32 bytes (described in
            # detail in the parse_eltorito_validation_entry() method).
            self.validation_entry.parse(valstr)
            self.state = self.EXPECTING_INITIAL_ENTRY
        elif self.state == self.EXPECTING_INITIAL_ENTRY:
            # The next entry is the Initial/Default entry.  An Initial/Default
            # entry consists of 32 bytes (described in detail in the
            # parse_eltorito_initial_entry() method).
            self.initial_entry.parse(valstr)
            self.state = self.EXPECTING_SECTION_HEADER_OR_DONE
        else:
            val = bytes(bytearray([valstr[0]]))
            if val == b'\x00':
                # An empty entry tells us we are done parsing El Torito.  Do
                # some sanity checks.
                last_section_index = len(self.sections) - 1
                for index, sec in enumerate(self.sections):
                    if sec.num_section_entries != len(sec.section_entries):
                        raise pycdlibexception.PyCdlibInvalidISO(
                            'El Torito section header specified %d entries, only saw %d'
                            % (sec.num_section_entries, len(
                                sec.section_entries)))
                    if index != last_section_index:
                        if sec.header_indicator != 0x90:
                            raise pycdlibexception.PyCdlibInvalidISO(
                                'Intermediate El Torito section header not properly specified'
                            )
                    # In theory, we should also make sure that the very last
                    # section has a header_indicator of 0x91.  However, we
                    # have seen ISOs in the wild (FreeBSD 11.0 amd64) in which
                    # this is not the case, so we skip that check.
                self._initialized = True
            elif val in (b'\x90', b'\x91'):
                # A Section Header Entry
                section_header = EltoritoSectionHeader()
                section_header.parse(valstr)
                self.sections.append(section_header)
            elif val in (b'\x88', b'\x00'):
                # A Section Entry. According to El Torito 2.4, a Section Entry
                # must follow a Section Header, but we have seen ISOs in the
                # wild that do not follow this (Mageia 4 ISOs, for instance).
                # To deal with this, we get a little complicated here.  If there
                # is a previous section header, and the length of the entries
                # attached to it is less than the number of entries it should
                # have, then we attach this entry to that header.  If there is
                # no previous section header, or if the previous section header
                # is already 'full', then we make this a standalone entry.
                secentry = EltoritoEntry()
                secentry.parse(valstr)
                if self.sections and len(
                        self.sections[-1].section_entries
                ) < self.sections[-1].num_section_entries:
                    self.sections[-1].add_parsed_entry(secentry)
                else:
                    self.standalone_entries.append(secentry)
            elif val == b'\x44':
                # A Section Entry Extension
                self.sections[-1].section_entries[
                    -1].selection_criteria += valstr[2:]
            else:
                raise pycdlibexception.PyCdlibInvalidISO(
                    'Invalid El Torito Boot Catalog entry')

        return self._initialized
Exemplo n.º 12
0
    def parse(self, instr):
        # type: (bytes) -> bool
        '''
        Parse ISO hybridization info out of an existing ISO.

        Parameters:
         instr - The data for the ISO hybridization.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'This IsoHybrid object is already initialized')

        if len(instr) < 512:
            raise pycdlibexception.PyCdlibInternalError(
                'Invalid IsoHybrid MBR passed')

        if instr[0:32] == self.ORIG_HEADER:
            self.header = self.ORIG_HEADER
        elif instr[0:32] == self.MAC_AFP:
            self.header = self.MAC_AFP
        else:
            # If we didn't see anything that we expected, then this is not an
            # IsoHybrid ISO, so just quietly return False
            return False

        (self.mbr, self.rba, unused1, self.mbr_id,
         unused2) = struct.unpack_from(self.FMT,
                                       instr[:32 + struct.calcsize(self.FMT)],
                                       32)

        if unused1 != 0:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid IsoHybrid unused1')

        if unused2 != 0:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid IsoHybrid unused2')

        offset = 32 + struct.calcsize(self.FMT)
        for i in range(1, 5):
            if bytes(bytearray([instr[offset]])) == b'\x80':
                self.part_entry = i
                (const_unused, self.bhead, self.bsect, self.bcyle, self.ptype,
                 self.ehead, esect_unused, ecyle, self.part_offset,
                 psize) = struct.unpack_from('<BBBBBBBBLL',
                                             instr[:offset + 16], offset)
            if i == 2 and instr[offset:offset + 8] == self.EFI_HEADER:
                self.efi = True
                (efi_lba,
                 self.efi_count) = struct.unpack_from('<LL',
                                                      instr[:offset + 16],
                                                      offset + 8)
                self.efi_lba = efi_lba // 4
            if i == 3 and instr[offset:offset + 8] == self.MAC_HEADER:
                self.mac = True
                (mac_lba,
                 self.mac_count) = struct.unpack_from('<LL',
                                                      instr[:offset + 16],
                                                      offset + 8)
                self.mac_lba = mac_lba // 4

            offset += 16

        if self.part_entry < 0:
            raise pycdlibexception.PyCdlibInvalidISO(
                'No valid partition found in IsoHybrid!')

        if instr[510:512] != b'\x55\xaa':
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid tail on isohybrid section')

        self.geometry_heads = self.ehead + 1

        self.geometry_sectors = psize // ((ecyle + 1) * self.geometry_heads)
        if self.geometry_sectors > 63:
            self.geometry_sectors = 63

        if self.efi:
            self.primary_gpt.parse_primary(instr, self.mac)

        self._initialized = True

        return True
Exemplo n.º 13
0
    def parse(self, instr):
        # type: (bytes) -> bool
        '''
        Parse ISO hybridization info out of an existing ISO.

        Parameters:
         instr - The data for the ISO hybridization.
        Returns:
         Nothing.
        '''
        if self._initialized:
            raise pycdlibexception.PyCdlibInternalError(
                'This IsoHybrid object is already initialized')

        if len(instr) != 512:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid size of the instr')

        if instr[0:32] == self.ORIG_HEADER:
            self.header = self.ORIG_HEADER
        elif instr[0:32] == self.MAC_AFP:
            self.header = self.MAC_AFP
        else:
            # If we didn't see anything that we expected, then this is not an
            # IsoHybrid ISO, so just quietly return False
            return False

        (self.mbr, self.rba, unused1, self.mbr_id,
         unused2) = struct.unpack_from(self.FMT,
                                       instr[:32 + struct.calcsize(self.FMT)],
                                       32)

        if unused1 != 0:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid IsoHybrid section')

        if unused2 != 0:
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid IsoHybrid section')

        offset = 32 + struct.calcsize(self.FMT)
        for i in range(1, 5):
            if bytes(bytearray([instr[offset]])) == b'\x80':
                self.part_entry = i
                (const_unused, self.bhead, self.bsect, self.bcyle, self.ptype,
                 self.ehead, self.esect, self.ecyle, self.part_offset,
                 self.psize) = struct.unpack_from('=BBBBBBBBLL',
                                                  instr[:offset + 16], offset)
                break
            offset += 16
        else:
            raise pycdlibexception.PyCdlibInvalidISO(
                'No valid partition found in IsoHybrid!')

        if bytes(bytearray([instr[-2]])) != b'\x55' or bytes(
                bytearray([instr[-1]])) != b'\xaa':
            raise pycdlibexception.PyCdlibInvalidISO(
                'Invalid tail on isohybrid section')

        self.geometry_heads = self.ehead + 1
        # FIXME: I can't see any way to compute the number of sectors from the
        # available information.  For now, we just hard-code this at 32 and
        # hope for the best.
        self.geometry_sectors = 32

        self._initialized = True

        return True
Exemplo n.º 14
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