Source code for pyvfs.utils

"""
pyvfs.utils -- utility classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Utility classes for VFS
"""
import os
import ast
import logging
import threading
from collections import deque
from pyvfs.vfs import Inode, Storage

protocols = []

try:
    from py9p import py9p
    from pyvfs.v9fs import v9fs
    protocols.append("9p")
except:
    pass


try:
    import fuse
    from pyvfs.ffs import ffs
    protocols.append("fuse")
except:
    pass


[docs]class logInode(Inode): """ Deque-based log file. Should be read-only on the filesystem. Can be used as a stream for Python ``logging.StreamHandler()`` objects. Stores ``maxlen`` of records, addition of records above ``maxlen`` at the same time discards discards old records. """ def __init__(self, name, parent, maxlen=30): Inode.__init__(self, name, parent) self.deque = deque(maxlen=maxlen) def sync(self): self.seek(0) self.truncate() for i in self.deque: Inode.write(self, i) def commit(self): self.deque.clear()
[docs] def write(self, value): self.deque.append(value)
[docs] def flush(self): pass
[docs]class indexInode(Inode): """ An inode that lists full storage file index. Can be used for debugging purposes. """ def sync(self): self.seek(0) self.truncate() self.write("# storage file index debug\n") self.write("%-20s : %-8s : %s\n\n" % ( "hash8", "mode", "name")) for (i, k) in list(self.storage.files.items()): self.write("%-20s : %-8s : \"%s\"\n" % ( i, oct(k.mode), k.absolute_path()))
[docs]class Server(threading.Thread): """ The main interface to create and start a filesystem. The filesystem will be exported with the protocol you will choose. Supported protocols now are ``9p`` and ``fuse``. For ``9p`` you should have py9p installed, for ``fuse``, respectively, fuse-python binding. With ``9p`` you will be able to mount the FS with mount(8) or with any other 9p implementation:: mount -t 9p -o ro,port=10001 127.0.0.1 /mnt/debugfs In the case of ``fuse`` protocol, the FS will be mounted immediately with the script startup. You can configure the behaviour with environment variables: * **PYVFS_PROTO** -- ``9p`` (default) or ``fuse`` * **PYVFS_PORT** -- tcp port for TCP sockets and access mode for UNIX sockets (9p only, default: 10001) * **PYVFS_ADDRESS** -- IPv4 address, use 0.0.0.0 to allow public access (9p only, default: 127.0.0.1) * **PYVFS_MOUNTPOINT** -- the mountpoint (fuse only, default: ./mnt) * **PYVFS_DEBUG** -- turn on stderr debug output of the FS protocol * **PYVFS_LOG** -- create /log inode * **PYVFS_ALLOW_ROOT** -- allow root to access the mountpoint (fuse only, default: False) * **PYVFS_ALLOW_OTHER** -- allow other users to access the mountpoint, requires ``user_allow_other`` in ``/etc/fuse.conf`` (fuse only, default: False) * **AUTHMODE** -- authentication mode for 9p, can be ``pki`` (9p only, default: none) * **KEYFILES** -- map of user public key files (9p only, default: none) .. warning:: No authentication for 9p is used in this library yet. Do not expose the socket to the public access unless you completely understand what are you doing. The typical code should look like that:: from pyvfs.utils import Server server = Server() server.start() To run server in the backgroung, use ``start()``, in the foreground -- ``run()`` method. If you want to use some custom storage, you can derive a class from pyvfs.vfs.Storage, and use an instance of it as an argument in the Server constructor:: from somewhere import CustomStorage from pyvfs.utils import Server server = Server(CustomStorage()) server.start() """ parser = { "proto": "9p", "address": "127.0.0.1", "port": 10001, "mountpoint": "./mnt", "debug": False, "log": False, "allow_root": False, "allow_other": False, "authmode": "", "keyfiles": {}, } def __init__(self, fs=None): threading.Thread.__init__(self, name="PyVFS for ObjectFS at 0x%x" % (id(fs))) self.setDaemon(True) self.fs = fs or Storage() for (i, k) in list(self.parser.items()): value = os.environ.get("PYVFS_%s" % (i.upper()), str(k)) if isinstance(k, bool): value = value.lower() in ( "yes", "true", "on", "t", "1") elif isinstance(k, dict): value = ast.literal_eval(value) elif not isinstance(k, str): value = type(k)(value) setattr(self, i, value) if self.authmode == "": self.authmode = None for i in list(self.protocols.keys()): if i not in protocols: del self.protocols[i] if self.proto not in self.protocols: raise Exception("Requested protocol <%s> is not available" %\ (self.proto)) if self.log: indexInode("index", self.fs.root) log = logInode("log", self.fs.root, maxlen=1024) logger = logging.getLogger() logger.setLevel(logging.DEBUG) handler = logging.StreamHandler(log) handler.setLevel(logging.DEBUG) formatter = logging.Formatter( "%(asctime)s : %(levelname)s : %(message)s") handler.setFormatter(formatter) logger.addHandler(handler) for i in range(len(logger.handlers) - 1): logger.removeHandler(logger.handlers[0]) logger.debug("PyVFS started") self.run = self.protocols[self.proto](self) def mount_v9fs(self): srv = py9p.Server(listen=(self.address, self.port), authmode=self.authmode, key=self.keyfiles, chatty=self.debug, dotu=True) srv.mount(v9fs(self.fs)) return srv.serve def mount_fuse(self): srv = ffs(storage=self.fs, version="%prog " + fuse.__version__, dash_s_do='undef') srv.fuse_args.setmod('foreground') if self.debug: srv.fuse_args.add('debug') if self.allow_root: srv.fuse_args.add('allow_root') if self.allow_other: srv.fuse_args.add('allow_other') srv.fuse_args.mountpoint = os.path.realpath(self.mountpoint) return srv.main protocols = { "9p": mount_v9fs, "fuse": mount_fuse, }