3 bup_python="$(dirname "$0")/bup-python" || exit $?
4 exec "$bup_python" "$0" ${1+"$@"}
8 from __future__ import absolute_import
14 log('error: cannot find the python "fuse" module; please install it\n')
16 if not hasattr(fuse, '__version__'):
17 raise RuntimeError('your fuse module is too old for fuse.__version__')
18 fuse.fuse_python_api = (0, 2)
20 from bup import options, git, vfs, xstat
21 from bup.helpers import log
22 from bup.repo import LocalRepo
24 # FIXME: self.meta and want_meta?
26 class BupFs(fuse.Fuse):
27 def __init__(self, repo, verbose=0, fake_metadata=False):
28 fuse.Fuse.__init__(self)
30 self.verbose = verbose
31 self.fake_metadata = fake_metadata
33 def getattr(self, path):
36 log('--getattr(%r)\n' % path)
37 res = vfs.resolve(self.repo, path, want_meta=(not self.fake_metadata),
42 if self.fake_metadata:
43 item = vfs.augment_item_meta(self.repo, item, include_size=True)
45 item = vfs.ensure_item_has_metadata(self.repo, item,
48 # FIXME: do we want/need to do anything more with nlink?
49 st = fuse.Stat(st_mode=meta.mode, st_nlink=1, st_size=meta.size)
50 st.st_mode = meta.mode
53 st.st_atime = max(0, xstat.fstime_floor_secs(meta.atime))
54 st.st_mtime = max(0, xstat.fstime_floor_secs(meta.mtime))
55 st.st_ctime = max(0, xstat.fstime_floor_secs(meta.ctime))
58 def readdir(self, path, offset):
59 assert not offset # We don't return offsets, so offset should be unused
60 res = vfs.resolve(self.repo, path, follow=False)
61 dir_name, dir_item = res[-1]
64 yield fuse.Direntry('..')
65 # FIXME: make sure want_meta=False is being completely respected
66 for ent_name, ent_item in vfs.contents(repo, dir_item, want_meta=False):
67 yield fuse.Direntry(ent_name.replace('/', '-'))
69 def readlink(self, path):
71 log('--readlink(%r)\n' % path)
72 res = vfs.resolve(self.repo, path, follow=False)
76 return vfs.readlink(repo, item)
78 def open(self, path, flags):
80 log('--open(%r)\n' % path)
81 res = vfs.resolve(self.repo, path, follow=False)
85 accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
86 if (flags & accmode) != os.O_RDONLY:
88 # Return None since read doesn't need the file atm...
89 # If we *do* return the file, it'll show up as the last argument
90 #return vfs.fopen(repo, item)
92 def read(self, path, size, offset):
94 log('--read(%r)\n' % path)
95 res = vfs.resolve(self.repo, path, follow=False)
99 with vfs.fopen(repo, item) as f:
104 bup fuse [-d] [-f] <mountpoint>
106 f,foreground run in foreground
107 d,debug run in the foreground and display FUSE debug information
108 o,allow-other allow other users to access the filesystem
109 meta report original metadata for paths when available
110 v,verbose increase log output (can be used more than once)
112 o = options.Options(optspec)
113 opt, flags, extra = o.parse(sys.argv[1:])
116 o.fatal('only one mount point argument expected')
118 git.check_repo_or_die()
120 f = BupFs(repo=repo, verbose=opt.verbose, fake_metadata=(not opt.meta))
121 f.fuse_args.mountpoint = extra[0]
123 f.fuse_args.add('debug')
125 f.fuse_args.setmod('foreground')
126 f.multithreaded = False
128 f.fuse_args.add('allow_other')