# Easy in-memory filesystems for FUSE. # Adam Sampson import fuse, stat, time, errno fuse.fuse_python_api = (0, 2) class TreeEntry(object): """An entry in an in-memory filesystem tree.""" def getattr(self): return None def readlink(self): return None def readdir(self): return None def register(self, tree, path): tree[path] = self class TreeDir(TreeEntry): """A directory entry.""" def __init__(self, entries = {}): super(TreeDir, self).__init__() self.entries = {} self.entries.update(entries) def __setitem__(self, key, value): self.entries[key] = value def __getitem__(self, key): return self.entries[key] def __contains__(self, key): return key in self.entries def getattr(self): return fuse.Stat(st_mode = stat.S_IFDIR | 0755, st_nlink = 2) def readdir(self): for e in self.entries.keys() + [".", ".."]: yield fuse.Direntry(e) def register(self, tree, path): super(TreeDir, self).register(tree, path) for name, entry in self.entries.items(): entry.register(tree, path + "/" + name) class TreeLink(TreeEntry): """A symbolic link entry.""" def __init__(self, target): super(TreeLink, self).__init__() self.target = target def getattr(self): return fuse.Stat(st_mode = stat.S_IFLNK | 0777, st_nlink = 0) def readlink(self): return self.target class TreeFS(fuse.Fuse): """FUSE wrapper for an in-memory filesystem.""" def __init__(self, tree, *args, **kwargs): fuse.Fuse.__init__(self, *args, **kwargs) self.tree = tree def getattr(self, path): entries = self.tree.get_entries() if path in entries: return entries[path].getattr() else: return -errno.ENOENT def readlink(self, path): entries = self.tree.get_entries() if path in entries: return entries[path].readlink() else: return -errno.ENOENT def readdir(self, path, offset): entries = self.tree.get_entries() if path in entries: return entries[path].readdir() else: return -errno.ENOENT class Tree(object): """An in-memory filesystem.""" def __init__(self, *args, **kwargs): super(Tree, self).__init__(*args, **kwargs) self.entries = None def needs_rebuild(self): """Return whether the tree needs rebuilding. (Subclasses should override this method, unless they want the tree to be rebuilt for every request.)""" return True def build_tree(self): """Rebuild the tree, returning the root entry. (Subclasses should override this method.)""" return TreeDir() def get_entries(self): """Return a hash of entries in the filesystem tree, rebuilding it if necessary.""" if (not self.needs_rebuild()) and (self.entries is not None): return self.entries root = self.build_tree() self.entries = {} root.register(self.entries, "") self.entries["/"] = self.entries[""] return self.entries def main(self, version): """Export the tree as a FUSE filesystem.""" fs = TreeFS(self, version = version) fs.parse(errex = True) fs.main() class TimedRebuildTree(Tree): """A tree that is rebuilt if it's older than a fixed time.""" def __init__(self, rebuild_time = 10, *args, **kwargs): super(TimedRebuildTree, self).__init__(*args, **kwargs) self.last_rebuild = 0 self.rebuild_time = rebuild_time def needs_rebuild(self): now = time.time() if now - self.last_rebuild > self.rebuild_time: self.last_rebuild = now return True else: return False