def _rr_new(self, rr_version, rr_name, rr_symlink_target, rr_relocated_child, rr_relocated, rr_relocated_parent, file_mode): ''' Internal method to add Rock Ridge to a Directory Record. Parameters: rr_version - A string containing the version of Rock Ridge to use for this record. rr_name - The Rock Ridge name to associate with this directory record. rr_symlink_target - The target for the symlink, if this is a symlink record (otherwise, None). rr_relocated_child - True if this is a directory record for a rock ridge relocated child. rr_relocated - True if this is a directory record for a relocated entry. rr_relocated_parent - True if this is a directory record for a rock ridge relocated parent. file_mode - The Unix file mode for this Rock Ridge entry. Returns: Nothing. ''' self.rock_ridge = rockridge.RockRidge() is_first_dir_record_of_root = self.file_ident == b'\x00' and self.parent.parent is None bytes_to_skip = 0 if self.xa_record is not None: bytes_to_skip = XARecord.length() self.dr_len = self.rock_ridge.new(is_first_dir_record_of_root, rr_name, file_mode, rr_symlink_target, rr_version, rr_relocated_child, rr_relocated, rr_relocated_parent, bytes_to_skip, self.dr_len) if self.isdir: if self.parent.parent is not None: if self.file_ident == b'\x00': self.parent.rock_ridge.add_to_file_links() self.rock_ridge.add_to_file_links() elif self.file_ident == b'\x01': self.rock_ridge.copy_file_links( self.parent.parent.children[1].rock_ridge) else: self.parent.rock_ridge.add_to_file_links() self.parent.children[0].rock_ridge.add_to_file_links() else: if self.file_ident != b'\x00' and self.file_ident != b'\x01': self.parent.children[0].rock_ridge.add_to_file_links() self.parent.children[1].rock_ridge.add_to_file_links() else: self.rock_ridge.add_to_file_links()
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