class FuseFS::PathMapperFS
A FuseFS
that maps files from their original location into a new path eg tagged audio files can be mapped by title etc…
Attributes
should filesystem support writing through to the real files @return [Boolean]
default is false
@return [StatsHelper] accumulated filesystem statistics
should raw file access should be used - useful for binary files @return [Boolean]
default is false
Public Class Methods
Creates a new Path Mapper filesystem over an existing directory @param [String] dir @param [Hash] options @yieldparam [String] file path to map @yieldreturn [String] @see initialize @see map_directory
# File lib/fusefs/pathmapper.rb, line 190 def PathMapperFS.create(dir,options={ },&block) pm_fs = self.new(options) pm_fs.map_directory(dir,&block) return pm_fs end
Create a new Path Mapper filesystem @param [Hash] options @option options [Boolean] :use_raw_file_access @option options [Boolean] :allow_write @option options [Integer] :max_space available space for writes (for df) @option options [Integer] :max_nodes available nodes for writes (for df)
# File lib/fusefs/pathmapper.rb, line 202 def initialize(options = { }) @stats = StatsHelper.new() @stats.max_space = options[:max_space] @stats.max_nodes = options[:max_nodes] @root = MNode.new(nil,@stats) @use_raw_file_access = options[:use_raw_file_access] @allow_write = options[:allow_write] end
Convert FuseFS
raw_mode strings back to IO open mode strings
# File lib/fusefs/pathmapper.rb, line 152 def self.open_mode(raw_mode) case raw_mode when "r" "r" when "ra" "r" #not really sensible.. when "rw" "r+" when "rwa" "a+" when "w" "w" when "wa" "a" end end
Public Instance Methods
@!visibility private We can only write to existing files because otherwise we don't have anything to back it
# File lib/fusefs/pathmapper.rb, line 299 def can_write?(path) @allow_write && file?(path) end
Deletes files and directories. Yields each {#node} in the filesystem and deletes it if the block returns true
Useful if your filesystem is periodically remapping the entire contents and you need to delete entries that have not been touched in the latest scan
@yieldparam [Hash] filesystem node @yieldreturn [true,false] should this node be deleted
# File lib/fusefs/pathmapper.rb, line 268 def cleanup(&block) recursive_cleanup(@root,&block) end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 280 def contents(path) node(path).files.keys end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 274 def directory?(path) possible_dir = node(path) possible_dir && possible_dir.directory? end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 285 def file?(path) filename = unmap(path) filename && File.file?(filename) end
Recursively find all files and map according to the given block @param [String…] dirs directories to list @yieldparam [String] file path to map @yieldreturn [String] the mapped path @yieldreturn nil to skip mapping this file
# File lib/fusefs/pathmapper.rb, line 216 def map_directory(*dirs) require 'find' Find.find(*dirs) do |file| new_path = yield file map_file(file,new_path) if new_path end end
Add (or replace) a mapped file
@param [String] real_path pointing at the real file location @param [String] new_path the mapped path @param [Hash<Symbol,Object>] options metadata for this path @option options [Hash<String,String>] :xattr hash to be used as extended attributes @return [MNode]
a node representing the mapped path. See {#node}
# File lib/fusefs/pathmapper.rb, line 234 def map_file(real_path,new_path,options = {}) make_node(new_path).init_file(real_path,options) end
Note we don't impleemnt can_mkdir? so this can only be called by code. Really only useful to create empty directories
# File lib/fusefs/pathmapper.rb, line 306 def mkdir(path,options = {}) make_node(path).init_dir(options) end
Retrieve in memory node for a mapped path
@param [String] path @return [MNode] in memory node at path @return nil if path does not exist in the filesystem
# File lib/fusefs/pathmapper.rb, line 244 def node(path) path_components = scan_path(path) #not actually injecting anything here, we're just following the hash of hashes... path_components.inject(@root) { |dir,file| break unless dir.files[file] dir.files[file] } end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 394 def raw_close(path,file=nil) file = @openfiles.delete(path) unless file if file && !file.closed? begin flags = file.fcntl(Fcntl::F_GETFL) & Fcntl::O_ACCMODE if flags == Fcntl::O_WRONLY || flags == Fcntl::O_RDWR #update stats node = node(path) node.updated if node end ensure file.close end end end
@!visibility private Will create, store and return a File object for the underlying file for subsequent use with the raw_read/raw_close methods expects file? to return true before this method is called
# File lib/fusefs/pathmapper.rb, line 343 def raw_open(path,mode,rfusefs = nil) return false unless @use_raw_file_access return false if mode.include?("w") && (!@allow_write) @openfiles ||= Hash.new() unless rfusefs real_path = unmap(path) unless real_path if rfusefs raise Errno::ENOENT.new(path) else #fusefs will go on to call file? return false end end file = File.new(real_path,PathMapperFS.open_mode(mode)) @openfiles[path] = file unless rfusefs return file end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 370 def raw_read(path,off,sz,file=nil) file = @openfiles[path] unless file file.sysseek(off) file.sysread(sz) end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 384 def raw_sync(path,datasync,file=nil) file = @openfiles[path] unless file if datasync file.fdatasync else file.sync end end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 377 def raw_write(path,offset,sz,buf,file=nil) file = @openfiles[path] unless file file.sysseek(offset) file.syswrite(buf[0,sz]) end
@!visibility private only called if option :raw_reads is not set
# File lib/fusefs/pathmapper.rb, line 292 def read_file(path) IO.read(unmap(path)) end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 318 def size(path) File.size(unmap(path)) end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 413 def statistics(path) @stats.to_statistics end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 323 def times(path) realpath = unmap(path) if (realpath) stat = File.stat(realpath) return [ stat.atime, stat.mtime, stat.ctime ] else # We're a directory return [0,0,0] end end
Takes a mapped file name and returns the original real_path
# File lib/fusefs/pathmapper.rb, line 255 def unmap(path) node = node(path) (node && node.file?) ? node.real_path : nil end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 311 def write_to(path,contents) node = node(path) File.open(node.real_path,"w") { |f| f.print(contents) } node.updated end
@!visibility private
# File lib/fusefs/pathmapper.rb, line 335 def xattr(path) result = node(path).xattr end
Private Instance Methods
# File lib/fusefs/pathmapper.rb, line 419 def make_node(path) #split path into components components = path.to_s.scan(/[^\/]+/) components.inject(@root) { |parent_dir, file| parent_dir.files[file] ||= MNode.new(parent_dir,@stats) } end
# File lib/fusefs/pathmapper.rb, line 427 def recursive_cleanup(dir_node,&block) dir_node.files.delete_if do |path,child| del = if child.file? yield child else recursive_cleanup(child,&block) child.files.size == 0 end child.deleted if del del end end