FuseFS API DOCUMENT =================== Last updated: 2005.09.19 by Greg Millam WARNING ------- Note: If you use DirLink (in demo.rb) or in any way access a FuseFS filesystem from *within* the ruby script accessing the FuseFS, then FuseFS will hang, and the only recourse is a kill -KILL. Also: If there are any open files or shells with 'pwd's in your filesystem when you exit your ruby script, fuse *might* not actually be unmounted. To unmount a path yourself, run the command: fusermount -u to unmount any FUSE filesystems mounted at . FuseFS API ---------- FuseFS provides a layer of abstraction to a programmer who wants to create a virtual filesystem via FUSE. FuseFS programs consist of two parts: 1) FuseFS, which is defined in 'fusefs.rb' 2) An object that defines a virtual directory. This must define a number of methods (given below, in "Directory Methods" section) in order to be usable. To write a FuseFS program, you must: * Define and create a Directory object that responds to the methods required by FuseFS for its desired use. * Call FuseFS.set_root with the object defining your virtual directory. * Mount FuseFS under a real directory on your filesystem. * Call FuseFS.run to start receiving and executing events. Happy Filesystem Hacking! Hello World FS -------------- helloworld.rb This creates a filesystem that contains exactly 1 file: "hello.txt" that, when read, returns "Hello, World!" This is not writable to, and contains no other files. require 'fusefs' class HelloDir def contents(path) ['hello.txt'] end def file?(path) path -- '/hello.txt' end def read_file(path) "Hello, World!\n" end end hellodir = HelloDir.new FuseFS.set_root( hellodir ) # Mount under a directory given on the command line. FuseFS.mount_under ARGV.shift FuseFS.run Directory Methods ----------------- Without any methods defined, any object is by default a content-less, file-less directory. The following are necessary for most or all filesystems: Directory listing and file type methods: :contents(path) # Return an array of file and dirnames within . :directory?(path) # Return true if is a directory. :file?(path) # Return true if is a file (not a directory). :executable?(path) # Return true if is an executable file. :size(path) # Return the file size. Necessary for apache, xmms, etc. File reading: :read_file(path) # Return the contents of the file at location . The following are only necessary if you want a filesystem that can be modified by the user. Without defining any of the below, the contents of the filesystem are automatically read-only. File manipulation: :can_write?(path) # Return true if the user can write to file at . :write_to(path,str) # Write the contents of to file at . :can_delete?(path) # Return true if the user can delete file at . :delete(path) # Delete the file at Directory manipulation: :can_mkdir?(path) # Return true if user can make a directory at . :mkdir(path) # Make a directory at path. :can_rmdir?(path) # Return true if user can remove directory at . :rmdir(path) # Remove it. Neat "toy": :touch(path) # Called when a file is 'touch'd or otherwise has their timestamps explicitly modified. I envision this as a neat toy, maybe you can use it for a push-button file? "touch button" -> unmounts fusefs? "touch musicfile.mp3" -> Play the mp3. If you want a lower level control of your file, then you can use: :raw_open(path,mode) # mode is "r" "w" or "rw", with "a" if the file is opened for append. If raw_open returns true, then the following calls are made: :raw_read(path,off,sz) # Read sz bites from file at path starting at offset off :raw_write(path,off,sz,buf) # Write sz bites of buf to path starting at offset off :raw_close(path) # Close the file. Method call flow ================ List contents: :directory? will be checked before :contents (Most 'ls' or 'dir' functions will go on next to getattr() for all contents) Read file: :file? will be checked before :read_file Getattr :directory? will be checked first. :file? will be checked before :can_write? :file? will be checked before :executable? Writing files: * directory? is usually called on the directory The FS wants to write a new file to, before this can occur. :can_write? will be checked before :write_to Deleting files: :file? will be checked before :can_delete? :can_delete? will be checked before :delete Creating dirs: * directory? is usually called on the directory The FS wants to make a new directory in, before this can occur. :directory? will be checked. :can_mkdir? is called only if :directory? is false. :can_mkdir? will be checked before :mkdir Deleting dirs: :directory? will be checked before :can_rmdir? :can_rmdir? will be checked before :rmdir module FuseFS ------------- FuseFS methods: FuseFS.set_root(object) Set the root virtual directory to . All queries for obtaining file information is directed at object. FuseFS.mount_under(path[,opt[,opt,...]]) This will cause FuseFS to virtually mount itself under the given path. 'path' is required to be a valid directory in your actual filesystem. 'opt's are FUSE options. Most likely, you will only want 'allow_other' or 'allow_root'. The two are mutually exclusive in FUSE, but allow_other will let other users, including root, access your filesystem. allow_root will only allow root to access it. Also available for FuseFS users are: default_permissions, max_read=N, fsname=NAME. For more information, look at FUSE. (P.S: I know FUSE allows other options, but I don't think any of the rest will do any good with FuseFS. If you think otherwise, please let me know!) FuseFS.run This is the final step to make your virtual filesystem accessible. It is recommended you run this as your main thread, but you can thread off to run this. FuseFS.handle_editor = bool (true by default) If handle_editor is true, then FuseFS will attempt to capture all editor files and prevent them from being passed to FuseRoot. It also prevents created and unmodified files from being passed as well, as vim (among others) will attempt to create and then remove a file that does not exist. FuseFS.reader_uid and FuseFS.reader_gid When the filesystem is accessed, the accessor's uid or gid is returned by FuseFS.reader_uid and FuseFS.reader_gid. You can use this in determining your permissions, or even provide different files for different users! FuseFS.fuse_fd and FuseFS.process These are not intended for use by the programmer. If you want to muck with this, read the code to see what they do :D. FuseDir ---------- FuseFS::FuseDir defines the methods "split_path" and "scan_path". You should typically inherit from FuseDir in your own directory objects. base, rest = split_path(path) # base is the file or directory in the current context. rest is either nil, or a path that is requested. If 'rest' exists, then you should recurse the paths. base, *rest = scan_path(path) # scan_path returns an array of all directory and file elements given by . This is useful when you're encapsulating an entire fs into one object. MetaDir ------- MetaDir is a full filesystem defined with hashes. It is writable, and the user can 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, Everybody!\n") FuseFS.set_root(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 (as demonstrated in samples/dictfs.rb), use: root.mkdir("/dict",DictFS.new) Conclusion ---------- Happy Hacking! If you do anything neat with this, please let me know! My email address is walker@deafcode.com Thanks for using FuseFS!