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 <path>

to unmount any FUSE filesystems mounted at <path>.


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 <virtualdir> 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 <path>.
    :directory?(path)   # Return true if <path> is a directory.
    :file?(path)        # Return true if <path> is a file (not a directory).
    :executable?(path)  # Return true if <path> 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 <path>.

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 <path>.
    :write_to(path,str) # Write the contents of <str> to file at <path>.

    :can_delete?(path)  # Return true if the user can delete file at <path>.
    :delete(path)       # Delete the file at <path>

  Directory manipulation:

    :can_mkdir?(path)   # Return true if user can make a directory at <path>.
    :mkdir(path)        # Make a directory at path.

    :can_rmdir?(path)   # Return true if user can remove directory at <path>.
    :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 <object>. 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
                                    <path>. 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!
