Exemplo n.º 1
0
  def _propogateRename(self, item, change):
    # TODO(conflicts): Verify inode numbers here
    oldfusepath = item.getOldFilename()
    newfusepath = item.getNewFilename()
    os.rename(tsumufs.nfsPathOf(item.getOldFilename()),
              tsumufs.nfsPathOf(item.getNewFilename()))

    return False
Exemplo n.º 2
0
  def _propogateUnlink(self, item, change):
    # TODO(conflicts): Conflict if the file type or inode have changed
    fusepath = item.getFilename()

    if item.getFileType() != 'dir':
      os.unlink(tsumufs.nfsPathOf(fusepath))
    else:
      os.rmdir(tsumufs.nfsPathOf(fusepath))

    return False
Exemplo n.º 3
0
  def ftruncate(self, size):
    logger.debug('opcode: ftruncate | size: %d' % size)

    try:
      statgoo = tsumufs.cacheManager.statFile(self._path)

      # Get inode number
      try:
        inum = tsumufs.NameToInodeMap.nameToInode(tsumufs.nfsPathOf(self._path))
      except KeyError, e:
        try:
          inum = statgoo.st_ino
        except (IOError, OSError), e:
          inum = -1

      except Exception, e:
        exc_info = sys.exc_info()

        logger.debug('*** Unhandled exception occurred')
        logger.debug('***     Type: %s' % str(exc_info[0]))
        logger.debug('***    Value: %s' % str(exc_info[1]))
        logger.debug('*** Traceback:')

        for line in traceback.extract_tb(exc_info[2]):
          logger.debug('***    %s(%d) in %s: %s' % line)
Exemplo n.º 4
0
  def chmod(self, fusepath, mode):
    '''
    Chmod a file.

    Returns:
      None

    Raises:
      OSError, IOError
    '''

    self.lockFile(fusepath)

    try:
      opcodes = self._genCacheOpcodes(fusepath)
      self._validateCache(fusepath, opcodes)
      realpath = self._generatePath(fusepath, opcodes)

      try:
        perms = tsumufs.permsOverlay.getPerms(fusepath)
      except KeyError:
        statgoo = self.statFile(fusepath)
        perms = { 'uid': statgoo.st_uid,
                  'gid': statgoo.st_gid,
                  'mode': statgoo.st_mode }

      perms.mode = (perms.mode & 070000) | mode
      tsumufs.permsOverlay.setPerms(fusepath, perms.uid, perms.gid, perms.mode)

      self._invalidateStatCache(tsumufs.nfsPathOf(fusepath))
    finally:
      self.unlockFile(fusepath)
Exemplo n.º 5
0
  def _nfsDataChanged(self, fusepath):
    '''
    Check to see if the NFS data has changed since our last stat.

    Returns:
      Boolean true or false.

    Raises:
      Any error that might occur during an os.lstat(), aside from ENOENT.
    '''

    self.lockFile(fusepath)

    try:
      try:
        cachedstat = self._cachedStats[fusepath]['stat']
        realstat   = os.lstat(tsumufs.nfsPathOf(fusepath))

        if ((cachedstat.st_blocks != realstat.st_blocks) or
            (cachedstat.st_mtime != realstat.st_mtime) or
            (cachedstat.st_size != realstat.st_size) or
            (cachedstat.st_ino != realstat.st_ino)):
          return True
        else:
          return False

      except OSError, e:
        if e.errno == errno.ENOENT:
          return False
        else:
          raise

      except KeyError, e:
        return False
Exemplo n.º 6
0
  def _generatePath(self, fusepath, opcodes=None):
    '''
    Return the path to use for all file operations, based upon the current state
    of the world generated by _genCacheOpcodes.

    Returns:
      None

    Raises:
      Nothing
    '''

    if opcodes == None:
      opcodes = self._genCacheOpcodes(fusepath)

    logger.debug('Opcodes are: %s' % opcodes)

    for opcode in opcodes:
      if opcode == 'enoent':
        logger.debug('ENOENT on %s' % fusepath)
        raise OSError(errno.ENOENT, os.strerror(errno.ENOENT))
      if opcode == 'use-nfs':
        logger.debug('Returning nfs path for %s' % fusepath)
        return tsumufs.nfsPathOf(fusepath)
      if opcode == 'use-cache':
        logger.debug('Returning cache path for %s' % fusepath)
        return tsumufs.cachePathOf(fusepath)
Exemplo n.º 7
0
  def truncateFile(self, fusepath, newsize):
    '''
    Truncate a file to newsize.
    '''

    try:
      self.lockFile(fusepath)

      try:
        nfspath = tsumufs.nfsPathOf(fusepath)

        fp = open(nfspath, 'r+')
        fp.truncate(newsize)
        fp.close()

      except OSError, e:
        if e.errno in (errno.EIO, errno.ESTALE):
          logger.debug('Got %s while writing a region to %s.' %
                      (str(e), nfspath))
          logger.debug('Triggering a disconnect.')

          tsumufs.nfsAvailable.clear()
          tsumufs.nfsAvailable.notifyAll()

          raise tsumufs.NFSMountError()
        else:
          raise

    finally:
      self.unlockFile(fusepath)
Exemplo n.º 8
0
  def _propogateNew(self, item, change):
    fusepath = item.getFilename()
    nfspath = tsumufs.nfsPathOf(fusepath)

    # Horrible hack, but it works to test for the existance of a file.
    try:
      tsumufs.nfsMount.readFileRegion(fusepath, 0, 0)
    except (OSError, IOError), e:
      if e.errno != errno.ENOENT:
        return True
Exemplo n.º 9
0
  def _cacheDir(self, fusepath):
    '''
    Cache the directory referenced by path.

    If the directory should not be cached to disk (as specified in the
    cachespec) then only the contents of the directory hash table will
    be stored in the _cachedFiles hash.

    Returns:
      None

    Raises:
      OSError - when an error operating on the filesystem occurs.
    '''

    self.lockFile(fusepath)

    try:
      nfspath   = tsumufs.nfsPathOf(fusepath)
      cachepath = tsumufs.cachePathOf(fusepath)
      stat      = os.lstat(nfspath)

      logger.debug('nfspath = %s' % nfspath)
      logger.debug('cachepath = %s' % cachepath)

      if fusepath == '/':
        logger.debug('Asking to cache root -- skipping the cache to '
                    'disk operation, but caching data in memory.')
      else:
        try:
          os.mkdir(cachepath)
        except OSError, e:
          # Skip EEXIST errors -- if it already exists, it may have files in it
          # already. Simply copy the stat and chown it again, then cache the
          # listdir operation as well.

          if e.errno != errno.EEXIST:
            raise

        shutil.copystat(nfspath, cachepath)

        tsumufs.permsOverlay.setPerms(fusepath,
                                      stat.st_uid,
                                      stat.st_gid,
                                      stat.st_mode)

      logger.debug('Caching directory %s to disk.' % fusepath)
      self._cachedDirents[fusepath] = os.listdir(nfspath)
Exemplo n.º 10
0
  def writeFileRegion(self, filename, start, end, data):
    '''
    Method to write a region to a file on the NFS mount. Additionally
    adds the resulting inode to filename mapping to the InodeMap
    singleton.

    Args:
      filename: the complete pathname to the file to write to.
      start: the beginning offset to write to.
      end: the ending offset to write to.
      data: the data to write.

    Raises:
      NFSMountError: An error occurred during an NFS call.
      RangeError: The start and end provided are invalid.
      OSError: Usually relating to permissions on the file.
    '''

    if end - start > len(data):
      raise dataregion.RangeError('The length of data specified in start and '
                                  'end does not match the data length.')

    try:
      self.lockFile(filename)

      try:
        nfspath = tsumufs.nfsPathOf(filename)

        fp = open(nfspath, 'r+')
        fp.seek(start)
        fp.write(data)
        fp.close()

      except OSError, e:
        if e.errno in (errno.EIO, errno.ESTALE):
          logger.debug('Got %s while writing a region to %s.' %
                      (str(e), filename))
          logger.debug('Triggering a disconnect.')

          tsumufs.nfsAvailable.clear()
          tsumufs.nfsAvailable.notifyAll() # TODO: AttributeError

          raise tsumufs.NFSMountError()
        else:
          raise

    finally:
      self.unlockFile(filename)
Exemplo n.º 11
0
  def readFileRegion(self, filename, start, end):
    '''
    Method to read a region of a file from the NFS mount. Additionally
    adds the inode to filename mapping to the InodeMap singleton.

    Args:
      filename: the complete pathname to the file to read from.
      start: the beginning offset to read from.
      end: the ending offset to read from.

    Returns:
      A string containing the data read.

    Raises:
      NFSMountError: An error occurred during an NFS call which is
        unrecoverable.
      RangeError: The start and end provided are invalid.
      IOError: Usually relating to permissions issues on the file.
    '''

    try:
      self.lockFile(filename)

      try:
        nfspath = tsumufs.nfsPathOf(filename)

        fp = open(nfspath, 'r')
        fp.seek(start)
        result = fp.read(end - start)
        fp.close()

        return result

      except OSError, e:
        if e.errno in (errno.EIO, errno.ESTALE):
          logger.debug('Got %s while reading a region from %s.' %
                      (str(e), filename))
          logger.debug('Triggering a disconnect.')

          tsumufs.nfsAvailable.clear()
          tsumufs.nfsAvailable.notifyAll()
          raise tsumufs.NFSMountError()
        else:
          raise

    finally:
      self.unlockFile(filename)
Exemplo n.º 12
0
  def write(self, new_data, offset):
    logger.debug('opcode: write | path: %s | offset: %d | buf: %s'
                % (self._path, offset, repr(new_data)))

    # Three cases here:
    #   - The file didn't exist prior to our write.
    #   - The file existed, but was extended.
    #   - The file existed, and an existing block was overwritten.

    nfspath = tsumufs.nfsPathOf(self._path)
    statgoo = tsumufs.cacheManager.statFile(self._path)

    try:
      inode = tsumufs.NameToInodeMap.nameToInode(nfspath)
    except KeyError, e:
      try:
        inode = statgoo.st_ino
      except (IOError, OSError), e:
        inode = -1
Exemplo n.º 13
0
  def _cacheFile(self, fusepath):
    '''
    Cache the file referenced by path.

    This method locks the file for reading, determines what type it
    is, and attempts to cache it. Note that if there was an issue
    reading from the NFSMount, this method will mark the NFS mount as
    being unavailble.

    Note: The touch cache isn't implemented here at the moment. As a
    result, the entire cache is considered permacache for now.

    Note: NFS error checking and disable are not handled here for the
    moment. Any errors that would ordinarily shut down the NFS mount
    are just reported as normal OSErrors, aside from ENOENT.

    Returns:
      Nothing.

    Raises:
      OSError if there was an issue attempting to copy the file
      across to cache.
    '''

    # TODO(jtg): Add support for storing the UID/GID

    self.lockFile(fusepath)

    try:
      logger.debug('Caching file %s to disk.' % fusepath)

      nfspath = tsumufs.nfsPathOf(fusepath)
      cachepath = tsumufs.cachePathOf(fusepath)

      curstat = os.lstat(nfspath)

      if (stat.S_ISREG(curstat.st_mode) or
          stat.S_ISFIFO(curstat.st_mode) or
          stat.S_ISSOCK(curstat.st_mode) or
          stat.S_ISCHR(curstat.st_mode) or
          stat.S_ISBLK(curstat.st_mode)):

        shutil.copy(nfspath, cachepath)
        shutil.copystat(nfspath, cachepath)

      elif stat.S_ISLNK(curstat.st_mode):
        dest = os.readlink(nfspath)

        try:
          os.unlink(cachepath)
        except OSError, e:
          if e.errno != errno.ENOENT:
            raise

        os.symlink(dest, cachepath)
        #os.lchown(cachepath, curstat.st_uid, curstat.st_gid)
        #os.lutimes(cachepath, (curstat.st_atime, curstat.st_mtime))
      elif stat.S_ISDIR(curstat.st_mode):
        # Caching a directory to disk -- call cacheDir instead.
        logger.debug('Request to cache a directory -- calling _cacheDir')
        self._cacheDir(fusepath)
Exemplo n.º 14
0
  def _propogateChange(self, item, change):
    # Rules:
    #   1. On conflict NFS always wins.
    #   2. Loser data always appended as a list of changes in
    #      ${mount}/._${file}.changes
    #   3. We're no better than NFS

    # Steps:
    #   1. Stat both files, and verify the file type is identical.
    #   2. Read in the regions from NFS.
    #   3. Compare the regions between the changes and NFS.
    #   4. If any changes differ, the entire set is conflicted.
    #      4a. Create a conflict change file and write out the changes
    #          that differ.
    #      4b. Create a 'new' change in the synclog for the conflict
    #          change file.
    #      4c. Erase the cached file on disk.
    #      4d. Invalidate dirent cache for the file's containing dir.
    #      4e. Invalidate the stat cache fo that file.
    #   5. Otherwise:
    #      4a. Iterate over each change and write it out to NFS.

    fusepath   = item.getFilename()
    logger.debug('Fuse path is %s' % fusepath)

    nfs_stat   = os.lstat(tsumufs.nfsPathOf(fusepath))
    cache_stat = os.lstat(tsumufs.cachePathOf(fusepath))

    logger.debug('Validating data hasn\'t changed on NFS.')
    if stat.S_IFMT(nfs_stat.st_mode) != stat.S_IFMT(cache_stat.st_mode):
      logger.debug('File type has completely changed -- conflicted.')
      return True
    elif nfs_stat.st_ino != item.getInum():
      logger.debug('Inode number changed -- conflicted.')
      return True
    else:
      # Iterate over each region, and verify the changes
      for region in change.getDataChanges():
        data = tsumufs.nfsMount.readFileRegion(fusepath,
                                               region.getStart(),
                                               region.getEnd()-region.getStart())

        if len(data) < region.getEnd() - region.getStart():
          data += '\x00' * ((region.getEnd() - region.getStart()) - len(data))

        if region.getData() != data:
          logger.debug('Region has changed -- entire changeset conflicted.')
          logger.debug('Data read was %s' % repr(data))
          logger.debug('Wanted %s' % repr(region.getData()))
          return True

    logger.debug('No conflicts detected.')

    # Propogate changes
    for region in change.getDataChanges():
      data = tsumufs.cacheManager.readFile(fusepath,
                                           region.getStart(),
                                           region.getEnd()-region.getStart(),
                                           os.O_RDONLY)

      # Pad the region with nulls if we get a short read (EOF before the end of
      # the real file. It means we ran into a truncate issue and that the file
      # is shorter than it was originally -- we'll propogate the truncate down
      # the line.
      if len(data) < region.getEnd() - region.getStart():
        data += '\x00' * ((region.getEnd() - region.getStart()) - len(data))

      logger.debug('Writing to %s at [%d-%d] %s'
                  % (fusepath, region.getStart(),
                     region.getEnd(), repr(data)))

      tsumufs.nfsMount.writeFileRegion(fusepath,
                                       region.getStart(),
                                       region.getEnd(),
                                       data)

    # Propogate truncations
    if (cache_stat.st_size < nfs_stat.st_size):
      tsumufs.nfsMount.truncateFile(fusepath, cache_stat.st_size)

    # TODO(writeback): Add in metadata syncing here.
    return False