class FuseFS::MetaDir

A full in-memory filesystem defined with hashes. It is writable to the user that mounted it may create and edit files within it, as well as the programmer

Usage

root = Metadir.new()
root.mkdir("/hello")
root.write_to("/hello/world","Hello World!\n")
root.write_to("/hello/everybody","Hello Everyone!\n")

FuseFS.start(mntpath,root)

Because Metadir is fully recursive, you can mount your own or other defined directory structures under it. For example, to mount a dictionary filesystem (see samples/dictfs.rb), use:

root.mkdir("/dict",DictFS.new())

Constants

DEFAULT_FS

Attributes

stats[R]

@return [StatsHelper] helper for filesystem accounting (df etc)

Public Class Methods

new(stats = nil) click to toggle source
# File lib/fusefs/metadir.rb, line 27
def initialize(stats = nil)
    @subdirs  = Hash.new(nil)
    @files    = Hash.new(nil)
    @xattr   = Hash.new() { |h,k| h[k] = Hash.new }
    @stats = stats || StatsHelper.new()
    @stats.adjust(0,1)
end

Public Instance Methods

can_delete?(path) click to toggle source

Delete a file

# File lib/fusefs/metadir.rb, line 107
def can_delete?(path)
    pathmethod(:can_delete?,path) do |filename|
        return mount_user?
    end
end
can_mkdir?(path) click to toggle source

mkdir - does not make intermediate dirs!

# File lib/fusefs/metadir.rb, line 121
def can_mkdir?(path)
    pathmethod(:can_mkdir?,path) do |dirname|
        return mount_user?
    end
end
can_rmdir?(path) click to toggle source

Delete an existing directory make sure it is not empty

# File lib/fusefs/metadir.rb, line 135
def can_rmdir?(path)
    pathmethod(:can_rmdir?,path) do |dirname|
        return mount_user? && @subdirs.has_key?(dirname) && @subdirs[dirname].contents("/").empty?
    end
end
can_write?(path) click to toggle source

can_write only applies to files… see can_mkdir for directories…

# File lib/fusefs/metadir.rb, line 86
def can_write?(path)
    pathmethod(:can_write?,path) do |filename|
        return mount_user?
    end
end
contents(path) click to toggle source

List directory contents

# File lib/fusefs/metadir.rb, line 56
def contents(path)
    pathmethod(:contents,path) do | filename |
        if !filename
            (@files.keys + @subdirs.keys).sort.uniq
        else
            @subdirs[filename].contents("/")
        end
    end
end
delete(path) click to toggle source
# File lib/fusefs/metadir.rb, line 113
def delete(path)
    pathmethod(:delete,path) do |filename|
        contents = @files.delete(filename)
        @stats.adjust(-contents.to_s.length,-1)
    end
end
directory?(path) click to toggle source
# File lib/fusefs/metadir.rb, line 43
def directory?(path)
    pathmethod(:directory?,path) do |filename|
        !filename || filename == "/" || @subdirs.has_key?(filename)
    end
end
file?(path) click to toggle source
# File lib/fusefs/metadir.rb, line 49
def file?(path)
    pathmethod(:file?,path) do |filename|
        @files.has_key?(filename)
    end
end
mkdir(path,dir=nil) click to toggle source
# File lib/fusefs/metadir.rb, line 127
def mkdir(path,dir=nil)
    pathmethod(:mkdir,path,dir) do | dirname,dirobj |
        dirobj ||= MetaDir.new(@stats)
        @subdirs[dirname] = dirobj
    end
end
read_file(path) click to toggle source
# File lib/fusefs/metadir.rb, line 73
def read_file(path)
    pathmethod(:read_file,path) do |filename|
        @files[filename].to_s
    end
end
rename(from_path,to_path,to_fusefs = self) click to toggle source
# File lib/fusefs/metadir.rb, line 148
def rename(from_path,to_path,to_fusefs = self)

    from_base,from_rest = split_path(from_path)

    case
    when !from_base
        # Shouldn't ever happen.
        raise Errno::EACCES.new("Can't move root")
    when !from_rest
        # So now we have a file or directory to move
        if @files.has_key?(from_base)
            return false unless can_delete?(from_base) && to_fusefs.can_write?(to_path)
            to_fusefs.write_to(to_path,@files[from_base])
            to_fusefs.xattr(to_path).merge!(@xattr[from_base])
            @xattr.delete(from_base)
            @files.delete(from_base)
        elsif @subdirs.has_key?(from_base)
            # we don't check can_rmdir? because that would prevent us
            # moving non empty directories
            return false unless mount_user? && to_fusefs.can_mkdir?(to_path)
            begin
                to_fusefs.mkdir(to_path,@subdirs[from_base])
                to_fusefs.xattr(to_path).merge!(@xattr[from_base])
                @xattr.delete(from_base)
                @subdirs.delete(from_base)
                @stats.adjust(0,-1)
                return true
            rescue ArgumentError
                # to_rest does not support mkdir with an arbitrary object
                return false
            end
        else
            #We shouldn't get this either
            return false
        end
    when @subdirs.has_key?(from_base)
        begin
            if to_fusefs != self
                #just keep recursing..
                return @subdirs[from_base].rename(from_rest,to_path,to_fusefs)
            else
                to_base,to_rest = split_path(to_path)
                if from_base == to_base
                    #mv within a subdir, just pass it on
                    return @subdirs[from_base].rename(from_rest,to_rest)
                else
                    #OK, this is the tricky part, we want to move something further down
                    #our tree into something in another part of the tree.
                    #from this point on we keep a reference to the fusefs that owns
                    #to_path (ie us) and pass it down, but only if the eventual path
                    #is writable anyway!
                    if (file?(to_path))
                        return false unless can_write?(to_path)
                    else
                        return false unless can_mkdir?(to_path)
                    end

                    return @subdirs[from_base].rename(from_rest,to_path,self)
                end
            end
        rescue NoMethodError
            #sub dir doesn't support rename
            return false
        rescue ArgumentError
            #sub dir doesn't support rename with additional to_fusefs argument
            return false
        end
    else
        return false
    end
end
rmdir(path) click to toggle source
# File lib/fusefs/metadir.rb, line 141
def rmdir(path)
    pathmethod(:rmdir,path) do |dirname|
        @subdirs.delete(dirname)
        @stats.adjust(0,-1)
    end
end
scan_path() click to toggle source
# File lib/fusefs/metadir.rb, line 39
def scan_path
    DEFAULT_FS.scan_path(path)
end
size(path) click to toggle source
# File lib/fusefs/metadir.rb, line 79
def size(path)
    pathmethod(:size,path) do | filename |
        return @files[filename].to_s.length
    end
end
split_path(path) click to toggle source
# File lib/fusefs/metadir.rb, line 35
def split_path(path)
    DEFAULT_FS.split_path(path)
end
statistics(path) click to toggle source

path is ignored? - recursively calculate for all subdirs - but cache and then rely on fuse to keep count

# File lib/fusefs/metadir.rb, line 221
def statistics(path)
    pathmethod(:statistics,path) do |stats_path|
        if @subdirs.has_key?(stats_path)
            #unlike all the other functions where this metadir applies
            #the function to @subdirs - we need to pass it on
            @subdirs[stats_path].statistics("/")
        else
            @stats.to_statistics
        end
    end
end
write_to(path,contents) click to toggle source
# File lib/fusefs/metadir.rb, line 92
def write_to(path,contents)
    pathmethod(:write_to,path,contents) do |filename, filecontents |
        adj_size = filecontents.to_s.length
        adj_nodes = 1
        if @files.has_key?(filename)
            adj_size = adj_size - @files[filename].to_s.length
            adj_nodes = 0
        end
        @stats.adjust(adj_size,adj_nodes)

        @files[filename] = filecontents
    end
end
xattr(path) click to toggle source

Extended attributes

# File lib/fusefs/metadir.rb, line 67
def xattr(path)
    pathmethod(:xattr,path) do | path |
       @xattr[path]
    end
end

Private Instance Methods

mount_user?() click to toggle source

is the accessing user the same as the user that mounted our FS?, used for all write activity

# File lib/fusefs/metadir.rb, line 247
def mount_user?
    return Process.uid == FuseFS.reader_uid
end
pathmethod(method, path,*args) { |nil,*args| ... } click to toggle source

All our FuseFS methods follow the same pattern…

# File lib/fusefs/metadir.rb, line 252
def pathmethod(method, path,*args)
    base,rest = split_path(path)

    case
    when ! base
        #request for the root of our fs
        yield(nil,*args)
    when ! rest
        #base is the filename, no more directories to traverse
        yield(base,*args)
    when @subdirs.has_key?(base)
        #base is a subdirectory, pass it on if we can
        begin
            @subdirs[base].send(method,rest,*args)
        rescue NoMethodError
            #Oh well
            return DEFAULT_FS.send(method,rest,*args)
        rescue ArgumentError
            #can_mkdir,mkdir
            if args.pop.nil?
                #possibly a default arg, try sending again with one fewer arg
                @subdirs[base].send(method,rest,*args)
            else
                #definitely not a default arg, reraise
                Kernel.raise
            end
        end
    else
        #return the default response
        return DEFAULT_FS.send(method,path,*args)
    end
end