X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=bup.git;a=blobdiff_plain;f=cmd%2Ffuse-cmd.py;h=dbc0cd1f8db5d6a5738c08f2a2f3f25c87b29b40;hp=df9bf56fa399739cf9a70c3fe6216e1197ba9448;hb=dd759516e0cc0b37aa3ba7048d053647483988bc;hpb=7e1f05fe8b8d580d61c6a233ec1440adc248c97c diff --git a/cmd/fuse-cmd.py b/cmd/fuse-cmd.py index df9bf56..dbc0cd1 100755 --- a/cmd/fuse-cmd.py +++ b/cmd/fuse-cmd.py @@ -5,149 +5,163 @@ exec "$bup_python" "$0" ${1+"$@"} """ # end of bup preamble +from __future__ import absolute_import, print_function import sys, os, errno -from bup import options, git, vfs, xstat -from bup.helpers import log - try: import fuse except ImportError: - log('error: cannot find the python "fuse" module; please install it\n') - sys.exit(1) - - -class Stat(fuse.Stat): - def __init__(self): - self.st_mode = 0 - self.st_ino = 0 - self.st_dev = 0 - self.st_nlink = 0 - self.st_uid = 0 - self.st_gid = 0 - self.st_size = 0 - self.st_atime = 0 - self.st_mtime = 0 - self.st_ctime = 0 - self.st_blocks = 0 - self.st_blksize = 0 - self.st_rdev = 0 - - -cache = {} -def cache_get(top, path): - parts = path.split('/') - cache[('',)] = top - c = None - max = len(parts) - #log('cache: %r\n' % cache.keys()) - for i in range(max): - pre = parts[:max-i] - #log('cache trying: %r\n' % pre) - c = cache.get(tuple(pre)) - if c: - rest = parts[max-i:] - for r in rest: - #log('resolving %r from %r\n' % (r, c.fullname())) - c = c.lresolve(r) - key = tuple(pre + [r]) - #log('saving: %r\n' % (key,)) - cache[key] = c - break - assert(c) - return c - - + print('error: cannot find the python "fuse" module; please install it', + file=sys.stderr) + sys.exit(2) +if not hasattr(fuse, '__version__'): + print('error: fuse module is too old for fuse.__version__', file=sys.stderr) + sys.exit(2) +fuse.fuse_python_api = (0, 2) + +if sys.version_info[0] > 2: + try: + fuse_ver = fuse.__version__.split('.') + fuse_ver_maj = int(fuse_ver[0]) + except: + log('error: cannot determine the fuse major version; please report', + file=sys.stderr) + sys.exit(2) + if len(fuse_ver) < 3 or fuse_ver_maj < 1: + print("error: fuse module can't handle binary data; please upgrade to 1.0+\n", + file=sys.stderr) + sys.exit(2) + +from bup import options, git, vfs, xstat +from bup.compat import argv_bytes, fsdecode, py_maj +from bup.helpers import log +from bup.repo import LocalRepo + + +# FIXME: self.meta and want_meta? + +# The path handling is just wrong, but the current fuse module can't +# handle bytes paths. class BupFs(fuse.Fuse): - def __init__(self, top, meta=False): + def __init__(self, repo, verbose=0, fake_metadata=False): fuse.Fuse.__init__(self) - self.top = top - self.meta = meta + self.repo = repo + self.verbose = verbose + self.fake_metadata = fake_metadata def getattr(self, path): - log('--getattr(%r)\n' % path) - try: - node = cache_get(self.top, path) - st = Stat() - st.st_mode = node.mode - st.st_nlink = node.nlinks() - st.st_size = node.size() # Until/unless we store the size in m. - if self.meta: - m = node.metadata() - if m: - st.st_mode = m.mode - st.st_uid = m.uid - st.st_gid = m.gid - st.st_atime = max(0, xstat.fstime_floor_secs(m.atime)) - st.st_mtime = max(0, xstat.fstime_floor_secs(m.mtime)) - st.st_ctime = max(0, xstat.fstime_floor_secs(m.ctime)) - return st - except vfs.NoSuchFile: + path = argv_bytes(path) + global opt + if self.verbose > 0: + log('--getattr(%r)\n' % path) + res = vfs.resolve(self.repo, path, want_meta=(not self.fake_metadata), + follow=False) + name, item = res[-1] + if not item: return -errno.ENOENT + if self.fake_metadata: + item = vfs.augment_item_meta(self.repo, item, include_size=True) + else: + item = vfs.ensure_item_has_metadata(self.repo, item, + include_size=True) + meta = item.meta + # FIXME: do we want/need to do anything more with nlink? + st = fuse.Stat(st_mode=meta.mode, st_nlink=1, st_size=meta.size) + st.st_mode = meta.mode + st.st_uid = meta.uid + st.st_gid = meta.gid + st.st_atime = max(0, xstat.fstime_floor_secs(meta.atime)) + st.st_mtime = max(0, xstat.fstime_floor_secs(meta.mtime)) + st.st_ctime = max(0, xstat.fstime_floor_secs(meta.ctime)) + return st def readdir(self, path, offset): - log('--readdir(%r)\n' % path) - node = cache_get(self.top, path) - yield fuse.Direntry('.') + path = argv_bytes(path) + assert not offset # We don't return offsets, so offset should be unused + res = vfs.resolve(self.repo, path, follow=False) + dir_name, dir_item = res[-1] + if not dir_item: + yield -errno.ENOENT yield fuse.Direntry('..') - for sub in node.subs(): - yield fuse.Direntry(sub.name) + # FIXME: make sure want_meta=False is being completely respected + for ent_name, ent_item in vfs.contents(repo, dir_item, want_meta=False): + fusename = fsdecode(ent_name.replace(b'/', b'-')) + yield fuse.Direntry(fusename) def readlink(self, path): - log('--readlink(%r)\n' % path) - node = cache_get(self.top, path) - return node.readlink() + path = argv_bytes(path) + if self.verbose > 0: + log('--readlink(%r)\n' % path) + res = vfs.resolve(self.repo, path, follow=False) + name, item = res[-1] + if not item: + return -errno.ENOENT + return fsdecode(vfs.readlink(repo, item)) def open(self, path, flags): - log('--open(%r)\n' % path) - node = cache_get(self.top, path) + path = argv_bytes(path) + if self.verbose > 0: + log('--open(%r)\n' % path) + res = vfs.resolve(self.repo, path, follow=False) + name, item = res[-1] + if not item: + return -errno.ENOENT accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR if (flags & accmode) != os.O_RDONLY: return -errno.EACCES - node.open() - - def release(self, path, flags): - log('--release(%r)\n' % path) + # Return None since read doesn't need the file atm... + # If we *do* return the file, it'll show up as the last argument + #return vfs.fopen(repo, item) def read(self, path, size, offset): - log('--read(%r)\n' % path) - n = cache_get(self.top, path) - o = n.open() - o.seek(offset) - return o.read(size) - - -if not hasattr(fuse, '__version__'): - raise RuntimeError, "your fuse module is too old for fuse.__version__" -fuse.fuse_python_api = (0, 2) + path = argv_bytes(path) + if self.verbose > 0: + log('--read(%r)\n' % path) + res = vfs.resolve(self.repo, path, follow=False) + name, item = res[-1] + if not item: + return -errno.ENOENT + with vfs.fopen(repo, item) as f: + f.seek(offset) + return f.read(size) optspec = """ bup fuse [-d] [-f] -- -d,debug increase debug level f,foreground run in foreground +d,debug run in the foreground and display FUSE debug information o,allow-other allow other users to access the filesystem meta report original metadata for paths when available +v,verbose increase log output (can be used more than once) """ o = options.Options(optspec) -(opt, flags, extra) = o.parse(sys.argv[1:]) +opt, flags, extra = o.parse(sys.argv[1:]) +if not opt.verbose: + opt.verbose = 0 + +# Set stderr to be line buffered, even if it's not connected to the console +# so that we'll be able to see diagnostics in a timely fashion. +errfd = sys.stderr.fileno() +sys.stderr.flush() +sys.stderr = os.fdopen(errfd, 'w', 1) if len(extra) != 1: - o.fatal("exactly one argument expected") + o.fatal('only one mount point argument expected') git.check_repo_or_die() -top = vfs.RefList(None) -f = BupFs(top, meta=opt.meta) +repo = LocalRepo() +f = BupFs(repo=repo, verbose=opt.verbose, fake_metadata=(not opt.meta)) + +# This is likely wrong, but the fuse module doesn't currently accept bytes f.fuse_args.mountpoint = extra[0] + if opt.debug: f.fuse_args.add('debug') if opt.foreground: f.fuse_args.setmod('foreground') -print f.multithreaded f.multithreaded = False if opt.allow_other: f.fuse_args.add('allow_other') - f.main()