fusetoys

various hacky fuse filesystem utilities
git clone https://a3nm.net/git/fusetoys/
Log | Files | Refs | README

commit 2159a0d8592704b5bc866098d44ff3b812a8f771
parent 62cef99031565e1f73d071bc65b46c47ed613eaf
Author: Antoine Amarilli <a3nm@a3nm.net>
Date:   Sun, 20 Aug 2023 09:14:22 -0700

mergefs

Diffstat:
mergefs.py | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tabs | 4++++
2 files changed, 183 insertions(+), 0 deletions(-)

diff --git a/mergefs.py b/mergefs.py @@ -0,0 +1,179 @@ +#!/usr/bin/python + +"""MergeFS FUSE filesystem""" +# Does not really work! just an experiment + +import fuse +import errno +import os +import sys +import threading +import logging + +logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s', level=logging.DEBUG) +logging.debug("test debug log") + +fuse.fuse_python_api = (0, 2) + +class MergeFS(fuse.Fuse): + def __init__(self, *args, **kw): + fuse.Fuse.__init__(self, *args, **kw) + + self.rwlock = threading.Lock() + + def fsinit(self): + self.source = self.cmdline[1][0] + + def baseSourcePath(self, path): + return os.path.join(self.source, path[1:]) + + def rawSourcePath(self, path): + # used for new files + return os.path.join(self.source, path[1:]) + + def sourcePath(self, path): + logging.debug("going into sourcepath") + rawSource = self.rawSourcePath(path) + if path.endswith("glibc-hwcaps"): + return rawSource + if '.so.' in path: + return rawSource + if os.sep in path[1:]: + # not at root level + return rawSource + if len(path) <= 2: + # '.' ? + return rawSource + # ok, try to find the file in a subdirectory + if os.path.exists(rawSource): + return rawSource + logging.debug("%s does not exist" % rawSource) + dirs = [] + for name in os.listdir(self.source): + if os.path.isdir(os.path.join(self.source, name)): + logging.debug("found dir: %s" % name) + dirs.append(name) + for dadir in dirs: + cand = os.path.join(self.source, os.path.join(dadir, path[1:])) + logging.debug("try cand: %s" % cand) + if os.path.exists(cand): + return cand + # does not exist anywhere + return rawSource + + def access(self, path, mode): + logging.debug("got access call") + if not os.access(self.sourcePath(path), mode): + return -errno.EACCES + + def chmod(self, path, mode): + logging.debug("got chmod call") + return os.chmod(self.sourcePath(path), mode) + + def chown(self, path, mode): + logging.debug("got chown call") + return os.chown(self.sourcePath(path), mode) + + def create(self, path, flags, mode): + logging.debug("got create call") + return os.open(self.rawSourcePath(path), os.O_WRONLY | os.O_CREAT, mode) + + def getattr(self, path): + logging.debug("got getattr call") + return os.lstat(self.sourcePath(path)) + + def link(self, target, source): + logging.debug("got link call") + os.link(self.sourcePath(source), self.rawSourcePath(target)) + + def mkdir(self, path, mode): + logging.debug("got mkdir call") + return os.mkdir(self.sourcePath(path), mode) + + def mknod(self, path, mode, rdev): + logging.debug("got mknod call") + return os.mknod(self.sourcePath(path), mode, rdev) + + def open(self, path, flags): + logging.debug("got open call") + return 0 + + def read(self, path, size, offset): + logging.debug("got read call") + with self.rwlock: + fh = os.open(self.sourcePath(path), os.O_RDONLY) + os.lseek(fh, offset, 0) + x = os.read(fh, size) + os.close(fh) + return x + + def readdir(self, path, offset): + logging.debug("got readdir call") + path = self.sourcePath(path) + myIno = os.lstat(path).st_ino + print ("will yield dot") + yield fuse.Direntry('.', ino=myIno) + print ("will yield parent") + try: + parentIno = os.lstat(os.path.join(path, "..")).st_ino + print ("yielded parent") + except OSError as e: + parentIno = myIno # root + print ("faked parent") + yield fuse.Direntry('..', ino=parentIno) + print ("will yield children") + for name in os.listdir(path): + print ("yield %s" % os.path.join(path, name)) + ino = os.lstat(os.path.join(path, name)).st_ino + yield fuse.Direntry(name, ino=ino) + print ("alldone") + + def readlink(self, path): + logging.debug("got readlink call") + return os.readlink(self.sourcePath(path)) + + def rename(self, old, new): + logging.debug("got rename call") + return os.rename(self.sourcePath(old), self.rawSourcePath(new)) + + def rmdir(self, path): + logging.debug("got rmdir call") + return os.rmdir(self.sourcePath(path)) + + def statfs(self): + logging.debug("got statfs call") + return os.statvfs(self.sourceRoot) + + def symlink(self, target, source): + logging.debug("got symlink call") + return os.symlink(self.sourcePath(source), self.rawSourcePath(target)) + + def truncate(self, path, length, fh=None): + logging.debug("got truncate call") + with open(self.sourcePath(path), 'r+') as f: + return f.truncate(length) + + def unlink(self, path): + logging.debug("got unlink call") + return os.unlink(self.sourcePath(path)) + + def utimens(self, path, ts_acc, ts_mod): + logging.debug("got utimens call") + times = (ts_acc.tv_sec, ts_mod.tv_sec) + return os.utime(self.sourcePath(path), times) + + def write(self, path, data, offset): + logging.debug("got write call") + with self.rwlock: + fh = os.open(self.sourcePath(path), os.O_WRONLY) + os.lseek(fh, offset, 0) + x = os.write(fh, data) + os.close(fh) + return x + + +if __name__ == "__main__": + mergefs = MergeFS() + fuse_opts = mergefs.parse(['-o', 'fsname=mergefs'] + sys.argv[1:]) + mergefs.main() + diff --git a/tabs b/tabs @@ -2,3 +2,7 @@ https://github.com/xolox/dedupfs/blob/master/dedupfs.py http://docs.python.org/2/library/shutil.html http://docs.python.org/2/library/os.path.html http://docs.python.org/2/library/os.html#os.lstat +https://github.com/skorokithakis/python-fuse-sample but does not work for some reason +https://thepythoncorner.com/posts/2017-02-27-writing-a-fuse-filesystem-in-python/ +https://stackoverflow.com/questions/52925566/which-module-is-the-actual-interface-to-fuse-from-python-3 +apparently python fuse bindings are unmaintained