Reads data from a file.

    Args:
      path: The path to the file to read.
      length: How many bytes to read.
      offset: Offset in bytes from which reading should start.
      fh: A file handler. Not used.

    Returns:
      A string containing the file contents requested.

    Raises:
      FuseOSError: If we try and read a directory or if we try and read an
      object that doesn't support reading.

def Read(self, path, length=None, offset=0, fh=None):
    """Reads data from a file.

    Args:
      path: The path to the file to read.
      length: How many bytes to read.
      offset: Offset in bytes from which reading should start.
      fh: A file handler. Not used.

    Returns:
      A string containing the file contents requested.

    Raises:
      FuseOSError: If we try and read a directory or if we try and read an
      object that doesn't support reading.

    """
    del fh

    if self._IsDir(path):
      raise fuse.FuseOSError(errno.EISDIR)

    fd = aff4.FACTORY.Open(self.root.Add(path), token=self.token)

    # If the object has Read() and Seek() methods, let's use them.
    if all((hasattr(fd, "Read"), hasattr(fd, "Seek"), callable(fd.Read),
            callable(fd.Seek))):
      # By default, read the whole file.
      if length is None:
        length = fd.Get(fd.Schema.SIZE)

      fd.Seek(offset)
      return fd.Read(length)
    else:
      # If we don't have Read/Seek methods, we probably can't read this object.
      raise fuse.FuseOSError(errno.EIO)