Exemplo n.º 1
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)
Exemplo n.º 2
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
Exemplo n.º 3
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
Exemplo n.º 4
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