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
@return [StatsHelper] helper for filesystem accounting (df etc)
Public Class Methods
# 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
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
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
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 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
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
# 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
# File lib/fusefs/metadir.rb, line 43 def directory?(path) pathmethod(:directory?,path) do |filename| !filename || filename == "/" || @subdirs.has_key?(filename) end end
# File lib/fusefs/metadir.rb, line 49 def file?(path) pathmethod(:file?,path) do |filename| @files.has_key?(filename) end end
# 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
# File lib/fusefs/metadir.rb, line 73 def read_file(path) pathmethod(:read_file,path) do |filename| @files[filename].to_s end end
# 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
# File lib/fusefs/metadir.rb, line 141 def rmdir(path) pathmethod(:rmdir,path) do |dirname| @subdirs.delete(dirname) @stats.adjust(0,-1) end end
# File lib/fusefs/metadir.rb, line 39 def scan_path DEFAULT_FS.scan_path(path) end
# File lib/fusefs/metadir.rb, line 79 def size(path) pathmethod(:size,path) do | filename | return @files[filename].to_s.length end end
# File lib/fusefs/metadir.rb, line 35 def split_path(path) DEFAULT_FS.split_path(path) end
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
# 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
Extended attributes
# File lib/fusefs/metadir.rb, line 67 def xattr(path) pathmethod(:xattr,path) do | path | @xattr[path] end end
Private Instance Methods
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
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