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