class FuseFS::Fuse::Root

Implements RFuseFS via RFuse The path supplied to these methods is generally validated by FUSE itself with a prior “getattr” call so we do not revalidate here. sourceforge.net/apps/mediawiki/fuse/index.php?title=FuseInvariants

Constants

CHECK_FILE

Public Class Methods

context(ctx) { || ... } click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 511
def self.context(ctx,&block)
  begin
    Thread.current[:fusefs_reader_uid] = ctx.uid
    Thread.current[:fusefs_reader_gid] = ctx.gid
    yield
  ensure
    Thread.current[:fusefs_reader_uid] = nil
    Thread.current[:fusefs_reader_gid] = nil
  end
end
new(root) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 121
def initialize(root)
  @root = root
  @created_files = { }

  # Keep track of changes to file counts and sizes made via Fuse - for #statfs
  @adj_nodes = 0
  @adj_size = 0

  #Define method missing for our filesystem
  #so we can just call all the API methods as required.
  def @root.method_missing(method,*args)
    # our filesystem might implement method_missing itself
    super
  rescue NoMethodError
    DEFAULT_FS.send(method,*args)
  end

  # Define sig<name> methods to handle signals
  sigmethods = Signal.list.keys.map { |sn| "sig#{sn.downcase}".to_sym }.select { |sm| @root.respond_to?(sm) }
  def_delegators(:@root,*sigmethods)
end

Public Instance Methods

flush(ctx,path,ffi) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 348
def flush(ctx,path,ffi)
  return wrap_context(ctx,__method__,path,ffi) if ctx
  fh = ffi.fh

  if fh && !fh.raw && fh.modified?
    #write contents to the file and mark it unmodified
    @root.write_to(path,fh.flush())
    #if it was created with mknod it now exists in the filesystem...
    @created_files.delete(path)
  end
end
fsync(ctx,path,datasync,ffi) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 333
def fsync(ctx,path,datasync,ffi)
  return wrap_context(ctx,__method__,path,datasync,ffi) if ctx
  fh = ffi.fh

  if fh && fh.raw
    if FuseFS::RFUSEFS_COMPATIBILITY
      @root.raw_sync(path,datasync != 0,fh.raw)
    else
      @root.raw_sync(path,datasync != 0)
    end
  else
    flush(nil,path,ffi)
  end
end
ftruncate(ctx,path,offset,ffi) click to toggle source

ftruncate - eg called after opening a file for write without append sizes are adjusted at file close

# File lib/fuse/rfusefs-fuse.rb, line 219
def ftruncate(ctx,path,offset,ffi)

  return wrap_context(ctx,__method__,path,offset,ffi) if ctx

  fh = ffi.fh

  if fh.raw
    @root.raw_truncate(path,offset,fh.raw)
    if (offset <= 0)
      fh.contents = ""
    else
      fh.contents = fh.contents[0..offset]
    end
  end
end
getattr(ctx,path) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 159
def getattr(ctx,path)

  return wrap_context(ctx,__method__,path) if ctx

  uid = Process.uid
  gid = Process.gid

  if  path == "/" || @root.directory?(path)
    #set "w" flag based on can_mkdir? || can_write? to path + "/._rfuse_check"
    write_test_path = (path == "/" ? "" : path) + CHECK_FILE

    mode = (@root.can_mkdir?(write_test_path) || @root.can_write?(write_test_path)) ? 0777 : 0555
    atime,mtime,ctime = @root.times(path)
    #nlink is set to 1 because apparently this makes find work.
    return RFuse::Stat.directory(mode,{ :uid => uid, :gid => gid, :nlink => 1, :atime => atime, :mtime => mtime, :ctime => ctime })
  elsif @created_files.has_key?(path)
    return @created_files[path]
  elsif @root.file?(path)
    #Set mode from can_write and executable
    mode = 0444
    mode |= 0222 if @root.can_write?(path)
    mode |= 0111 if @root.executable?(path)
    size = size(path)
    atime,mtime,ctime = @root.times(path)
    return RFuse::Stat.file(mode,{ :uid => uid, :gid => gid, :size => size, :atime => atime, :mtime => mtime, :ctime => ctime })
  else
    raise Errno::ENOENT.new(path)
  end

end
getxattr(ctx,path,name) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 439
def getxattr(ctx,path,name)
  return wrap_context(ctx,__method__,path,name) if ctx
  result = @root.xattr(path)[name]
  raise Errno::ENODATA.new("No attribute #{name}") unless result
  result.to_s
end
listxattr(ctx,path) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 446
def listxattr(ctx,path)
  return wrap_context(ctx,__method__,path) if ctx
  @root.xattr(path).keys
end
mkdir(ctx,path,mode) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 190
def mkdir(ctx,path,mode)

  return wrap_context(ctx,__method__,path,mode) if ctx

  unless @root.can_mkdir?(path)
    raise Errno::EACCES.new(path)
  end

  @root.mkdir(path)
  @adj_nodes += 1
end
mknod(ctx,path,mode,major,minor) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 202
def mknod(ctx,path,mode,major,minor)

  return wrap_context(ctx,__method__,path,mode,major,minor) if ctx

  unless ((RFuse::Stat::S_IFMT & mode) == RFuse::Stat::S_IFREG ) && @root.can_write?(path)
    raise Errno::EACCES.new(path)
  end

  now = Time.now
  stat = RFuse::Stat.file(mode,{ :uid => Process.uid, :gid => Process.gid, :atime => now, :mtime => now, :ctime => now })

  @created_files[path] = stat
  @adj_nodes += 1
end
mounted() click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 503
def mounted()
  @root.mounted()
end
open(ctx,path,ffi) click to toggle source

Open. Create a FileHandler and store in fuse file info This will be returned to us in read/write No O_CREATE (mknod first?), no O_TRUNC (truncate first)

# File lib/fuse/rfusefs-fuse.rb, line 258
def open(ctx,path,ffi)
  return wrap_context(ctx,__method__,path,ffi) if ctx
  fh = FileHandle.new(path,ffi.flags)

  #Save the value return from raw_open to be passed back in raw_read/write etc..
  if (FuseFS::RFUSEFS_COMPATIBILITY)
    fh.raw = @root.raw_open(path,fh.raw_mode,true)
  else
    fh.raw = @root.raw_open(path,fh.raw_mode)
  end

  unless fh.raw
    if fh.rdonly?
      fh.contents = @root.read_file(path)
    elsif fh.writing?
      unless @root.can_write?(path)
        raise Errno::EACCES.new(path)
      end

      if @created_files.has_key?(path)
        fh.create
      else
        if fh.rdwr? || fh.append?
          fh.contents = @root.read_file(path)
        else #wronly && !append
          #We should get a truncate 0, but might as well play it safe
          fh.contents = ""
        end
      end
    else
      raise Errno::ENOPERM.new(path)
    end
  end

  #If we get this far, save our filehandle in the FUSE structure
  ffi.fh=fh
end
read(ctx,path,size,offset,ffi) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 296
def read(ctx,path,size,offset,ffi)
  return wrap_context(ctx,__method__,path,size,offset,ffi) if ctx

  fh = ffi.fh

  if fh.raw
    if FuseFS::RFUSEFS_COMPATIBILITY
      return @root.raw_read(path,offset,size,fh.raw)
    else
      return @root.raw_read(path,offset,size)
    end
  elsif offset >= 0
    return fh.read(offset,size)
  else
    #TODO: Raise? what does a negative offset mean
    return ""
  end
rescue EOFError
  return ""
end
readdir(ctx,path,filler,offset,ffi) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 143
def readdir(ctx,path,filler,offset,ffi)

  return wrap_context(ctx,__method__,path,filler,offset,ffi) if ctx

  #Always have "." and ".."
  filler.push(".",nil,0)
  filler.push("..",nil,0)

  files = @root.contents(path)

  files.each do | filename |
    filler.push(filename,nil,0)
  end

end
release(ctx,path,ffi) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 360
def release(ctx,path,ffi)
  return wrap_context(ctx,__method__,path,ffi) if ctx


  fh = ffi.fh
  if fh && fh.raw
    if (FuseFS::RFUSEFS_COMPATIBILITY)
      @root.raw_close(path,fh.raw)
    else
      @root.raw_close(path)
    end
    # if was handled as raw, then assume the file has now been created (or not)
    @created_files.delete(path)
  else
    # Probably just had flush called, but no harm calling it again
    flush(nil,path,ffi)
  end
end
removexattr(ctx,path,name) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 451
def removexattr(ctx,path,name)
  return wrap_context(ctx,__method__,path,name) if ctx
  @root.xattr(path).delete(name)
end
rename(ctx,from,to) click to toggle source

def symlink(path,as) end

# File lib/fuse/rfusefs-fuse.rb, line 417
def rename(ctx,from,to)
  return wrap_context(ctx,__method__,from,to) if ctx

  if @root.rename(from,to)
    # nothing to do
  elsif @root.file?(from) && @root.can_write?(to) &&  @root.can_delete?(from)
    contents = @root.read_file(from)
    @root.write_to(to,contents)
    @root.delete(from)
  else
    raise Errno::EACCES.new("Unable to move directory #{from}")
  end
end
rmdir(ctx,path) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 405
def rmdir(ctx,path)
  return wrap_context(ctx,__method__,path) if ctx

  unless @root.can_rmdir?(path)
    raise Errno::EACCES.new(path)
  end
  @root.rmdir(path)
end
setxattr(ctx,path,name,value,flags) click to toggle source

def link(path,as) end

# File lib/fuse/rfusefs-fuse.rb, line 434
def setxattr(ctx,path,name,value,flags)
  return wrap_context(ctx,__method__,path,name,value,flags) if ctx
  @root.xattr(path)[name]=value
end
statfs(ctx,path) click to toggle source

Some random numbers to show with df command bsize preferred block size = 1K unless @root provides something different frsize = bsize (but apparently unused) blocks = total number of blocks bfree = number of free blocks bavail = bfree if mounted -o allow_other files = count of all files ffree - count of free file inode

# File lib/fuse/rfusefs-fuse.rb, line 475
def statfs(ctx,path)
  return wrap_context(ctx,__method__,path) if ctx
  block_size = 1024

  stats = @root.statistics(path)
  case stats
  when Array
    used_space, used_files, total_space, total_files = stats
    used_files ||= 0
    used_space ||= 0
    total_files ||= used_files
    total_space ||= used_space
    result = RFuse::StatVfs.new(
      "bsize" => block_size,
      "frsize" => block_size,
      "blocks" => total_space / block_size,
      "bfree" => (total_space - used_space)/block_size,
      "bavail" => (total_space - used_space)/block_size,
      "files" => total_files,
      "ffree" => (total_files - used_files)
    )
    return result
  else
    #expected to quack like rfuse:statvfs
    return stats
  end
end
to_s() click to toggle source

@private - no doc

# File lib/fuse/rfusefs-fuse.rb, line 523
def to_s
  "RFuseFS::#{@root}"
end
truncate(ctx,path,offset) click to toggle source

truncate a file outside of open files

# File lib/fuse/rfusefs-fuse.rb, line 236
def truncate(ctx,path,offset)
  return wrap_context(ctx,__method__,path,offset) if ctx

  unless @root.can_write?(path)
    raise Errno::EACESS.new(path)
  end

  current_size = size(path)
  unless @root.raw_truncate(path,offset)
    contents = @root.read_file(path)
    if (offset <= 0)
      @root.write_to(path,"")
    elsif offset < contents.length
      @root.write_to(path,contents[0..offset] )
    end
  end
  @adj_size = @adj_size - current_size + (offset <= 0 ? 0 : offset)
end
unmounted() click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 507
def unmounted()
  @root.unmounted()
end
utime(ctx,path,actime,modtime) click to toggle source

def chown(path,uid,gid) end

# File lib/fuse/rfusefs-fuse.rb, line 385
def utime(ctx,path,actime,modtime)
  return wrap_context(ctx,__method__,path,actime,modtime) if ctx

  #Touch...
  @root.touch(path,modtime) if @root.respond_to?(:touch)
end
write(ctx,path,buf,offset,ffi) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 317
def write(ctx,path,buf,offset,ffi)
  return wrap_context(ctx,__method__,path,buf,offset,ffi) if ctx
  fh = ffi.fh

  if fh.raw
    if FuseFS::RFUSEFS_COMPATIBILITY
      return @root.raw_write(path,offset,buf.length,buf,fh.raw)
    else
      @root.raw_write(path,offset,buf.length,buf)
      return buf.length
    end
  else
    return fh.write(offset,buf)
  end
end

Private Instance Methods

size(path) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 533
def size(path)
  @root.respond_to?(:size) ? @root.size(path) : @root.read_file(path).length
end
wrap_context(ctx,method,*args) click to toggle source
# File lib/fuse/rfusefs-fuse.rb, line 529
def wrap_context(ctx,method,*args)
  self.class.context(ctx) { send(method,nil,*args) }
end