text: # -*- coding: utf-8 -*-
  """The CPIO extracted file-like object implementation."""
  
  import os
  
  from dfvfs.file_io import file_io
  from dfvfs.resolver import resolver
  
  
  class CPIOFile(file_io.FileIO):
    """File-like object using CPIOArchiveFile."""
  
    def __init__(self, resolver_context):
      """Initializes a file-like object.
  
      Args:
        resolver_context (Context): resolver context.
      """
      super(CPIOFile, self).__init__(resolver_context)
      self._cpio_archive_file = None
      self._cpio_archive_file_entry = None
      self._current_offset = 0
      self._file_system = None
      self._size = 0
  
    def _Close(self):
      """Closes the file-like object."""
      self._cpio_archive_file_entry = None
      self._cpio_archive_file = None
  
      self._file_system.Close()
      self._file_system = None
  
    def _Open(self, path_spec=None, mode='rb'):
      """Opens the file-like object defined by path specification.
  
      Args:
        path_spec (Optional[PathSpec]): path specification.
        mode (Optional[str]): file access mode.
  
      Raises:
        AccessError: if the access to open the file was denied.
        IOError: if the file-like object could not be opened.
        OSError: if the file-like object could not be opened.
        PathSpecError: if the path specification is incorrect.
        ValueError: if the path specification is invalid.
      """
      if not path_spec:
        raise ValueError('Missing path specification.')
  
      file_system = resolver.Resolver.OpenFileSystem(
          path_spec, resolver_context=self._resolver_context)
  
      file_entry = file_system.GetFileEntryByPathSpec(path_spec)
      if not file_entry:
        file_system.Close()
        raise IOError('Unable to retrieve file entry.')
  
      self._file_system = file_system
      self._cpio_archive_file = self._file_system.GetCPIOArchiveFile()
      self._cpio_archive_file_entry = file_entry.GetCPIOArchiveFileEntry()
  
      self._current_offset = 0
  
    # Note: that the following functions do not follow the style guide
    # because they are part of the file-like object interface.
    # pylint: disable=invalid-name
  
    def read(self, size=None):
      """Reads a byte string from the file-like object at the current offset.
  
      The function will read a byte string of the specified size or
      all of the remaining data if no size was specified.
  
      Args:
        size (Optional[int]): number of bytes to read, where None is all
            remaining data.
  
      Returns:
        bytes: data read.
  
      Raises:
        IOError: if the read failed.
        OSError: if the read failed.
      """
      if not self._is_open:
        raise IOError('Not opened.')
  
      if self._current_offset >= self._cpio_archive_file_entry.data_size:
        return b''
  
      file_offset = (
          self._cpio_archive_file_entry.data_offset + self._current_offset)
  
      read_size = self._cpio_archive_file_entry.data_size - self._current_offset
      if read_size > size:
        read_size = size
  
      data = self._cpio_archive_file.ReadDataAtOffset(file_offset, read_size)
  
      # It is possible the that returned data size is not the same as the
      # requested data size. At this layer we don't care and this discrepancy
      # should be dealt with on a higher layer if necessary.
      self._current_offset += len(data)
  
      return data
  
    def seek(self, offset, whence=os.SEEK_SET):
      """Seeks to an offset within the file-like object.
  
      Args:
        offset (int): offset to seek to.
        whence (Optional(int)): value that indicates whether offset is an absolute
            or relative position within the file.
  
      Raises:
        IOError: if the seek failed.
        OSError: if the seek failed.
      """
      if not self._is_open:
        raise IOError('Not opened.')
  
      if whence == os.SEEK_CUR:
        offset += self._current_offset
      elif whence == os.SEEK_END:
        offset += self._cpio_archive_file_entry.data_size
      elif whence != os.SEEK_SET:
        raise IOError('Unsupported whence.')
  
      if offset < 0:
        raise IOError('Invalid offset value less than zero.')
  
      self._current_offset = offset
  
    def get_offset(self):
      """Retrieves the current offset into the file-like object.
  
      Returns:
        int: current offset in the CPIO archived file.
  
      Raises:
        IOError: if the file-like object has not been opened.
        OSError: if the file-like object has not been opened.
      """
      if not self._is_open:
        raise IOError('Not opened.')
  
      return self._current_offset
  
    def get_size(self):
      """Retrieves the size of the file-like object.
  
      Returns:
        int: size of the CPIO archived file.
  
      Raises:
        IOError: if the file-like object has not been opened.
        OSError: if the file-like object has not been opened.
      """
      if not self._is_open:
        raise IOError('Not opened.')
  
      return self._cpio_archive_file_entry.data_size
  