def _ReadFileHeader(self, file_object): """Reads the file header. Args: file_object: the file-like object to read from. Raises: FileFormatError: if file format related errors are detected. """ file_object.seek(0, os.SEEK_SET) file_header = self._FILE_HEADER_STRUCT.parse_stream(file_object) self._compressed_data_offset = file_object.get_offset() if file_header.signature != self._FILE_SIGNATURE: raise errors.FileFormatError( u'Unsuppored file signature: 0x{0:04x}.'.format( file_header.signature)) if file_header.compression_method != self._COMPRESSION_METHOD_DEFLATE: raise errors.FileFormatError( u'Unsuppored compression method: {0:d}.'.format( file_header.compression_method)) self.modification_time = file_header.modification_time self.operating_system = file_header.operating_system if file_header.flags & self._FLAG_FEXTRA: extra_field_data_size = construct.ULInt16( u'extra_field_data_size').parse_stream(file_object) file_object.seek(extra_field_data_size, os.SEEK_CUR) self._compressed_data_offset += 2 + extra_field_data_size if file_header.flags & self._FLAG_FNAME: # Since encoding is set construct will convert the C string to Unicode. # Note that construct 2 does not support the encoding to be a Unicode # string. self.original_filename = construct.CString( u'original_filename', encoding='iso-8859-1').parse_stream( file_object) self._compressed_data_offset = file_object.get_offset() if file_header.flags & self._FLAG_FCOMMENT: # Since encoding is set construct will convert the C string to Unicode. # Note that construct 2 does not support the encoding to be a Unicode # string. self.comment = construct.CString( u'comment', encoding='iso-8859-1').parse_stream(file_object) self._compressed_data_offset = file_object.get_offset() if file_header.flags & self._FLAG_FHCRC: self._compressed_data_offset += 2 self._compressed_data_size = ( file_object.get_size() - (self._compressed_data_offset + 8))
def _ReadMemberHeader(self, file_object): """Reads a member header. Args: file_object (FileIO): file-like object to read from. Raises: FileFormatError: if the member header cannot be read. """ file_offset = file_object.get_offset() member_header = self._ReadStructure(file_object, file_offset, self._MEMBER_HEADER_SIZE, self._MEMBER_HEADER, 'member header') if member_header.signature != self._GZIP_SIGNATURE: raise errors.FileFormatError( 'Unsupported signature: 0x{0:04x}.'.format( member_header.signature)) if member_header.compression_method != self._COMPRESSION_METHOD_DEFLATE: raise errors.FileFormatError( 'Unsupported compression method: {0:d}.'.format( member_header.compression_method)) self.modification_time = member_header.modification_time self.operating_system = member_header.operating_system if member_header.flags & self._FLAG_FEXTRA: file_offset = file_object.get_offset() extra_field_data_size = self._ReadStructure( file_object, file_offset, self._UINT16LE_SIZE, self._UINT16LE, 'extra field data size') file_object.seek(extra_field_data_size, os.SEEK_CUR) if member_header.flags & self._FLAG_FNAME: file_offset = file_object.get_offset() string_value = self._ReadString(file_object, file_offset, self._CSTRING, 'original filename') self.original_filename = string_value.rstrip('\x00') if member_header.flags & self._FLAG_FCOMMENT: file_offset = file_object.get_offset() string_value = self._ReadString(file_object, file_offset, self._CSTRING, 'comment') self.comment = string_value.rstrip('\x00') if member_header.flags & self._FLAG_FHCRC: file_object.read(2)
def _ReadStructureFromByteStream( self, byte_stream, file_offset, data_type_map, description, context=None): """Reads a structure from a byte stream. Args: byte_stream (bytes): byte stream. file_offset (int): offset of the data relative from the start of the file-like object. data_type_map (dtfabric.DataTypeMap): data type map of the structure. description (str): description of the structure. context (Optional[dtfabric.DataTypeMapContext]): data type map context. Returns: object: structure values object. Raises: FileFormatError: if the structure cannot be read. ValueError: if file-like object or date type map are invalid. """ if not byte_stream: raise ValueError('Invalid byte stream.') if not data_type_map: raise ValueError('Invalid data type map.') try: return data_type_map.MapByteStream(byte_stream, context=context) except dtfabric_errors.MappingError as exception: raise errors.FileFormatError(( 'Unable to map {0:s} data at offset: 0x{1:08x} with error: ' '{2!s}').format(description, file_offset, exception))
def _ReadAndParseHeader(self, file_object): """Reads the member header and sets relevant member values. Args: file_object (FileIO): file-like object to read from. Raises: FileFormatError: if file format related errors are detected. """ member_header = self._MEMBER_HEADER_STRUCT.parse_stream(file_object) if member_header.signature != self._GZIP_SIGNATURE: raise errors.FileFormatError( 'Unsupported file signature: 0x{0:04x}.'.format( member_header.signature)) if member_header.compression_method != self._COMPRESSION_METHOD_DEFLATE: raise errors.FileFormatError( 'Unsupported compression method: {0:d}.'.format( member_header.compression_method)) self.modification_time = member_header.modification_time self.operating_system = member_header.operating_system if member_header.flags & self._FLAG_FEXTRA: extra_field_data_size = construct.ULInt16( 'extra_field_data_size').parse_stream(file_object) file_object.seek(extra_field_data_size, os.SEEK_CUR) if member_header.flags & self._FLAG_FNAME: # Since encoding is set construct will convert the C string to Unicode. # Note that construct 2 does not support the encoding to be a Unicode # string. self.original_filename = construct.CString( 'original_filename', encoding=b'iso-8859-1').parse_stream(file_object) if member_header.flags & self._FLAG_FCOMMENT: # Since encoding is set construct will convert the C string to Unicode. # Note that construct 2 does not support the encoding to be a Unicode # string. self.comment = construct.CString( 'comment', encoding=b'iso-8859-1').parse_stream(file_object) if member_header.flags & self._FLAG_FHCRC: file_object.read(2)
def _ReadData(self, file_object, file_offset, data_size, description): """Reads data. Args: file_object (FileIO): file-like object. file_offset (int): offset of the data relative from the start of the file-like object. data_size (int): size of the data. description (str): description of the data. Returns: bytes: byte stream containing the data. Raises: FileFormatError: if the structure cannot be read. ValueError: if file-like object or date type map are invalid. """ if not file_object: raise ValueError('Invalid file-like object.') file_object.seek(file_offset, os.SEEK_SET) read_error = '' try: data = file_object.read(data_size) if len(data) != data_size: read_error = 'missing data' except IOError as exception: read_error = '{0!s}'.format(exception) if read_error: raise errors.FileFormatError(( 'Unable to read {0:s} data at offset: 0x{1:08x} with error: ' '{2:s}').format(description, file_offset, read_error)) return data
def _ReadFileEntry(self, file_object, file_offset): """Reads a file entry. Args: file_object (FileIO): file-like object. file_offset (int): offset of the data relative from the start of the file-like object. Returns: CPIOArchiveFileEntry: a file entry. Raises: FileFormatError: if the file entry cannot be read. """ if self.file_format == 'bin-big-endian': data_type_map = self._CPIO_BINARY_BIG_ENDIAN_FILE_ENTRY file_entry_data_size = self._CPIO_BINARY_BIG_ENDIAN_FILE_ENTRY_SIZE elif self.file_format == 'bin-little-endian': data_type_map = self._CPIO_BINARY_LITTLE_ENDIAN_FILE_ENTRY file_entry_data_size = self._CPIO_BINARY_LITTLE_ENDIAN_FILE_ENTRY_SIZE elif self.file_format == 'odc': data_type_map = self._CPIO_PORTABLE_ASCII_FILE_ENTRY file_entry_data_size = self._CPIO_PORTABLE_ASCII_FILE_ENTRY_SIZE elif self.file_format in ('crc', 'newc'): data_type_map = self._CPIO_NEW_ASCII_FILE_ENTRY file_entry_data_size = self._CPIO_NEW_ASCII_FILE_ENTRY_SIZE file_entry = self._ReadStructure(file_object, file_offset, file_entry_data_size, data_type_map, 'file entry') file_offset += file_entry_data_size if self.file_format in ('bin-big-endian', 'bin-little-endian'): file_entry.modification_time = ( (file_entry.modification_time.upper << 16) | file_entry.modification_time.lower) file_entry.file_size = ((file_entry.file_size.upper << 16) | file_entry.file_size.lower) if self.file_format == 'odc': for attribute_name in self._CPIO_ATTRIBUTE_NAMES_ODC: value = getattr(file_entry, attribute_name, None) try: value = int(value, 8) except ValueError: raise errors.FileFormatError( 'Unable to convert attribute: {0:s} into an integer'. format(attribute_name)) value = setattr(file_entry, attribute_name, value) elif self.file_format in ('crc', 'newc'): for attribute_name in self._CPIO_ATTRIBUTE_NAMES_CRC: value = getattr(file_entry, attribute_name, None) try: value = int(value, 16) except ValueError: raise errors.FileFormatError( 'Unable to convert attribute: {0:s} into an integer'. format(attribute_name)) value = setattr(file_entry, attribute_name, value) path_data = file_object.read(file_entry.path_size) file_offset += file_entry.path_size path = path_data.decode(self._encoding) path, _, _ = path.partition('\x00') if self.file_format in ('bin-big-endian', 'bin-little-endian'): padding_size = file_offset % 2 if padding_size > 0: padding_size = 2 - padding_size elif self.file_format == 'odc': padding_size = 0 elif self.file_format in ('crc', 'newc'): padding_size = file_offset % 4 if padding_size > 0: padding_size = 4 - padding_size file_offset += padding_size archive_file_entry = CPIOArchiveFileEntry() archive_file_entry.data_offset = file_offset archive_file_entry.data_size = file_entry.file_size archive_file_entry.group_identifier = file_entry.group_identifier archive_file_entry.inode_number = file_entry.inode_number archive_file_entry.modification_time = file_entry.modification_time archive_file_entry.path = path archive_file_entry.mode = file_entry.mode archive_file_entry.size = (file_entry_data_size + file_entry.path_size + padding_size + file_entry.file_size) archive_file_entry.user_identifier = file_entry.user_identifier file_offset += file_entry.file_size if self.file_format in ('bin-big-endian', 'bin-little-endian'): padding_size = file_offset % 2 if padding_size > 0: padding_size = 2 - padding_size elif self.file_format == 'odc': padding_size = 0 elif self.file_format in ('crc', 'newc'): padding_size = file_offset % 4 if padding_size > 0: padding_size = 4 - padding_size if padding_size > 0: archive_file_entry.size += padding_size return archive_file_entry