fusetoys

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

mergefs.py (5720B)


      1 #!/usr/bin/python
      2 
      3 """MergeFS FUSE filesystem"""
      4 # Does not really work! just an experiment
      5 
      6 import fuse
      7 import errno
      8 import os
      9 import sys
     10 import threading
     11 import logging
     12 
     13 logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s', level=logging.DEBUG)
     14 logging.debug("test debug log")
     15 
     16 fuse.fuse_python_api = (0, 2)
     17 
     18 class MergeFS(fuse.Fuse):
     19     def __init__(self, *args, **kw):
     20         fuse.Fuse.__init__(self, *args, **kw)
     21 
     22         self.rwlock = threading.Lock()
     23 
     24     def fsinit(self):
     25         self.source = self.cmdline[1][0]
     26 
     27     def baseSourcePath(self, path):
     28         return os.path.join(self.source, path[1:])
     29 
     30     def rawSourcePath(self, path):
     31         # used for new files
     32         return os.path.join(self.source, path[1:])
     33 
     34     def sourcePath(self, path):
     35         logging.debug("going into sourcepath")
     36         rawSource = self.rawSourcePath(path)
     37         if path.endswith("glibc-hwcaps"):
     38             return rawSource
     39         if '.so.' in path:
     40             return rawSource
     41         if os.sep in path[1:]:
     42             # not at root level
     43             return rawSource
     44         if len(path) <= 2:
     45             # '.' ?
     46             return rawSource
     47         # ok, try to find the file in a subdirectory
     48         if os.path.exists(rawSource):
     49             return rawSource
     50         logging.debug("%s does not exist" % rawSource)
     51         dirs = []
     52         for name in os.listdir(self.source):
     53             if os.path.isdir(os.path.join(self.source, name)):
     54                 logging.debug("found dir: %s" % name)
     55                 dirs.append(name)
     56         for dadir in dirs:
     57             cand = os.path.join(self.source, os.path.join(dadir, path[1:]))
     58             logging.debug("try cand: %s" % cand)
     59             if os.path.exists(cand):
     60                 return cand
     61         # does not exist anywhere
     62         return rawSource
     63 
     64     def access(self, path, mode):
     65         logging.debug("got access call")
     66         if not os.access(self.sourcePath(path), mode):
     67             return -errno.EACCES
     68 
     69     def chmod(self, path, mode):
     70         logging.debug("got chmod call")
     71         return os.chmod(self.sourcePath(path), mode)
     72 
     73     def chown(self, path, mode):
     74         logging.debug("got chown call")
     75         return os.chown(self.sourcePath(path), mode)
     76 
     77     def create(self, path, flags, mode):
     78         logging.debug("got create call")
     79         return os.open(self.rawSourcePath(path), os.O_WRONLY | os.O_CREAT, mode)
     80 
     81     def getattr(self, path):
     82         logging.debug("got getattr call")
     83         return os.lstat(self.sourcePath(path))
     84 
     85     def link(self, target, source):
     86         logging.debug("got link call")
     87         os.link(self.sourcePath(source), self.rawSourcePath(target))
     88 
     89     def mkdir(self, path, mode):
     90         logging.debug("got mkdir call")
     91         return os.mkdir(self.sourcePath(path), mode)
     92 
     93     def mknod(self, path, mode, rdev):
     94         logging.debug("got mknod call")
     95         return os.mknod(self.sourcePath(path), mode, rdev)
     96 
     97     def open(self, path, flags):
     98         logging.debug("got open call")
     99         return 0
    100 
    101     def read(self, path, size, offset):
    102         logging.debug("got read call")
    103         with self.rwlock:
    104             fh = os.open(self.sourcePath(path), os.O_RDONLY)
    105             os.lseek(fh, offset, 0)
    106             x = os.read(fh, size)
    107             os.close(fh)
    108             return x
    109 
    110     def readdir(self, path, offset):
    111         logging.debug("got readdir call")
    112         path = self.sourcePath(path)
    113         myIno = os.lstat(path).st_ino
    114         print ("will yield dot")
    115         yield fuse.Direntry('.', ino=myIno)
    116         print ("will yield parent")
    117         try:
    118             parentIno = os.lstat(os.path.join(path, "..")).st_ino
    119             print ("yielded parent")
    120         except OSError as e:
    121             parentIno = myIno # root
    122             print ("faked parent")
    123         yield fuse.Direntry('..', ino=parentIno)
    124         print ("will yield children")
    125         for name in os.listdir(path):
    126             print ("yield %s" % os.path.join(path, name))
    127             ino = os.lstat(os.path.join(path, name)).st_ino
    128             yield fuse.Direntry(name, ino=ino)
    129         print ("alldone")
    130 
    131     def readlink(self, path):
    132         logging.debug("got readlink call")
    133         return os.readlink(self.sourcePath(path))
    134 
    135     def rename(self, old, new):
    136         logging.debug("got rename call")
    137         return os.rename(self.sourcePath(old), self.rawSourcePath(new))
    138 
    139     def rmdir(self, path):
    140         logging.debug("got rmdir call")
    141         return os.rmdir(self.sourcePath(path))
    142 
    143     def statfs(self):
    144         logging.debug("got statfs call")
    145         return os.statvfs(self.sourceRoot)
    146 
    147     def symlink(self, target, source):
    148         logging.debug("got symlink call")
    149         return os.symlink(self.sourcePath(source), self.rawSourcePath(target))
    150 
    151     def truncate(self, path, length, fh=None):
    152         logging.debug("got truncate call")
    153         with open(self.sourcePath(path), 'r+') as f:
    154             return f.truncate(length)
    155 
    156     def unlink(self, path):
    157         logging.debug("got unlink call")
    158         return os.unlink(self.sourcePath(path))
    159 
    160     def utimens(self, path, ts_acc, ts_mod):
    161         logging.debug("got utimens call")
    162         times = (ts_acc.tv_sec, ts_mod.tv_sec)
    163         return os.utime(self.sourcePath(path), times)
    164 
    165     def write(self, path, data, offset):
    166         logging.debug("got write call")
    167         with self.rwlock:
    168             fh = os.open(self.sourcePath(path), os.O_WRONLY)
    169             os.lseek(fh, offset, 0)
    170             x = os.write(fh, data)
    171             os.close(fh)
    172             return x
    173 
    174 
    175 if __name__ == "__main__":
    176     mergefs = MergeFS()
    177     fuse_opts = mergefs.parse(['-o', 'fsname=mergefs'] + sys.argv[1:])
    178     mergefs.main()
    179