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