fusetoys

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

metacachefs.py (7019B)


      1 #!/usr/bin/python
      2 
      3 """FUSE filesystem to cache all metadata in memory for a hierarchy"""
      4 
      5 import fuse
      6 import errno
      7 import os
      8 import sys
      9 from loopfs import LoopFS
     10 import traceback
     11 import logging
     12 
     13 fuse.fuse_python_api = (0, 2)
     14 
     15 class MissingNode:
     16     def __init__(self):
     17         self.access = -errno.ENOENT
     18         self.getattr = -errno.ENOENT
     19         self.readlink = -errno.ENOENT
     20         self.readdir = -errno.ENOENT
     21 
     22 class Node:
     23     def __init__(self):
     24         self.flush()
     25         self.potentialChildren = set()
     26 
     27     def flush(self):
     28         self.access = None
     29         self.getattr = None
     30         self.readlink = None
     31         self.flushFolder()
     32 
     33     def flushFolder(self):
     34         self.readdir = None
     35 
     36     def registerChild(self, path):
     37         self.potentialChildren.add(path)
     38 
     39 
     40 class MetaCacheFS(LoopFS):
     41     def __init__(self, *args, **kw):
     42         LoopFS.__init__(self, *args, **kw)
     43         self.files = {}
     44 
     45     def flushIfPresent(self, path):
     46         if path in self.files.keys():
     47             self.doFlushFolder(path)
     48             self.files[path].flush()
     49     def deleteIfPresent(self, path):
     50         if path in self.files.keys():
     51             del self.files[path]
     52     def getFolder(self, path):
     53         head, tail = os.path.split(path)
     54         return head
     55 
     56     def flushFolder(self, path):
     57         logging.debug("flushing %s" % (self.getFolder(path)))
     58         if self.getFolder(path) in self.files.keys():
     59             return self.doFlushFolder(self.getFolder(path))
     60 
     61     def doFlushFolder(self, path):
     62         d = self.files[path]
     63         if d.readdir != None:
     64             for x in d.readdir:
     65                 logging.debug("flushing descendent %s" % (os.path.join(path, x.name)))
     66                 self.flushIfPresent(os.path.join(path, x.name))
     67         for x in d.potentialChildren:
     68             logging.debug("flushing potential descendent %s" % x)
     69             self.flushIfPresent(x)
     70         d.flushFolder()
     71 
     72     def exceptionToStatus(self, e):
     73         if isinstance(e, OSError):
     74             return -e.errno
     75         else:
     76             return -errno.ENOENT
     77 
     78     def getOrCreate(self, path):
     79         if path not in self.files.keys():
     80             head, tail = os.path.split(path)
     81             if head in self.files.keys():
     82                 d = self.files[head]
     83                 logging.debug("might get missingfile from %s, index is %s" % (
     84                   head, d.readdir))
     85                 if d.readdir != None and tail not in [x.name for x in
     86                         d.readdir]:
     87                     return MissingNode() # we know it can't exist
     88             logging.debug("created fresh node for %s" % path)
     89             self.files[path] = Node()
     90             if (head != path):
     91                 headFile = self.getOrCreate(head)
     92                 headFile.registerChild(path)
     93         return self.files[path]
     94 
     95     def access(self, path, mode):
     96         pathh = os.path.normpath(path)
     97         f = self.getOrCreate(pathh)
     98         if f.access == None:
     99             f.access = super(MetaCacheFS, self).access(path, mode)
    100         return f.access
    101 
    102     def chmod(self, path, mode):
    103         pathh = os.path.normpath(path)
    104         self.flushIfPresent(pathh)
    105         return super(MetaCacheFS, self).chmod(path, mode)
    106 
    107     def chown(self, path, mode):
    108         pathh = os.path.normpath(path)
    109         self.flushIfPresent(pathh)
    110         return super(MetaCacheFS, self).chown(path, mode)
    111 
    112     def create(self, path, flags, mode):
    113         pathh = os.path.normpath(path)
    114         self.flushFolder(pathh)
    115         return super(MetaCacheFS, self).create(path, flags, mode)
    116 
    117     def getattr(self, path):
    118         pathh = os.path.normpath(path)
    119         logging.debug("pathh is %s in cache" % "" if pathh in self.files.keys() else " not")
    120         f = self.getOrCreate(pathh)
    121         logging.debug("my file has %s" % f.getattr)
    122         if f.getattr == None:
    123             try:
    124                 x = super(MetaCacheFS, self).getattr(path)
    125             except Exception as e:
    126                 x = self.exceptionToStatus(e)
    127             f.getattr = x
    128         return f.getattr
    129 
    130     def link(self, target, source):
    131         target2 = os.path.normpath(target)
    132         self.flushFolder(target2)
    133 
    134         return super(MetaCacheFS, self).link(target, source)
    135 
    136     def mkdir(self, path, mode):
    137         pathh = os.path.normpath(path)
    138         self.flushFolder(pathh)
    139         return super(MetaCacheFS, self).mkdir(path, mode)
    140 
    141     def mknod(self, path, mode, rdev):
    142         pathh = os.path.normpath(path)
    143         self.flushFolder(pathh)
    144         return super(MetaCacheFS, self).mknod(path, mode, rdev)
    145 
    146     def open(self, path, flags):
    147         return super(MetaCacheFS, self).open(path, flags)
    148 
    149     def read(self, path, size, offset):
    150         return super(MetaCacheFS, self).read(path, size, offset)
    151 
    152     def readdir(self, path, offset):
    153         pathh = os.path.normpath(path)
    154         f = self.getOrCreate(pathh)
    155         if f.readdir == None:
    156             f.readdir = list(super(MetaCacheFS, self).readdir(path, offset))
    157         return f.readdir
    158 
    159     def readlink(self, path):
    160         pathh = os.path.normpath(path)
    161         f = self.getOrCreate(pathh)
    162         if f.readlink == None:
    163             try:
    164                 x = super(MetaCacheFS, self).readlink(path)
    165             except Exception as e:
    166                 x = self.except_to_status(e)
    167             f.readlink = x
    168         return f.readlink
    169 
    170     def rename(self, old, new):
    171         old2 = os.path.normpath(old)
    172         new2 = os.path.normpath(new)
    173         self.flushIfPresent(old2)
    174         self.flushIfPresent(new2)
    175         self.flushFolder(new2)
    176         self.flushFolder(old2)
    177         return super(MetaCacheFS, self).rename(old, new)
    178 
    179     def rmdir(self, path):
    180         pathh = os.path.normpath(path)
    181         self.deleteIfPresent(pathh)
    182         self.flushFolder(pathh)
    183         return super(MetaCacheFS, self).rmdir(path)
    184 
    185     def statfs(self):
    186         return super(MetaCacheFS, self).statfs()
    187 
    188     def symlink(self, target, source):
    189         target2 = os.path.normpath(target)
    190         self.flushFolder(target2)
    191 
    192         return super(MetaCacheFS, self).symlink(target, source)
    193 
    194     def truncate(self, path, length):
    195         pathh = os.path.normpath(path)
    196         self.flushIfPresent(pathh)
    197         return super(MetaCacheFS, self).truncate(path, length)
    198 
    199     def unlink(self, path):
    200         pathh = os.path.normpath(path)
    201         self.deleteIfPresent(pathh)
    202         self.flushFolder(pathh)
    203         return super(MetaCacheFS, self).unlink(path)
    204 
    205     def utimens(self, path, ts_acc, ts_mod):
    206         pathh = os.path.normpath(path)
    207         self.flushIfPresent(pathh)
    208         return super(MetaCacheFS, self).utimens(path, ts_acc, ts_mod)
    209 
    210     def write(self, path, data, offset):
    211         pathh = os.path.normpath(path)
    212         self.flushIfPresent(pathh)
    213         return super(MetaCacheFS, self).write(path, data, offset)
    214 
    215 
    216 if __name__ == "__main__":
    217     logging.basicConfig(level=logging.DEBUG)
    218     metacachefs = MetaCacheFS()
    219     fuse_opts = metacachefs.parse(['-o', 'fsname=metacachefs'] + sys.argv[1:])
    220     metacachefs.main()
    221