3 # https://sourceware.org/bugzilla/show_bug.cgi?id=26034
4 export "BUP_ARGV_0"="$0"
7 export "BUP_ARGV_${arg_i}"="$arg"
11 # Here to end of preamble replaced during install
12 bup_python="$(dirname "$0")/../../config/bin/python" || exit $?
13 exec "$bup_python" "$0"
17 from __future__ import absolute_import, print_function
19 # Intentionally replace the dirname "$0" that python prepends
21 sys.path[0] = os.path.dirname(os.path.realpath(__file__)) + '/..'
28 print('error: cannot find the python "fuse" module; please install it',
31 if not hasattr(fuse, '__version__'):
32 print('error: fuse module is too old for fuse.__version__', file=sys.stderr)
34 fuse.fuse_python_api = (0, 2)
36 if sys.version_info[0] > 2:
38 fuse_ver = fuse.__version__.split('.')
39 fuse_ver_maj = int(fuse_ver[0])
41 log('error: cannot determine the fuse major version; please report',
44 if len(fuse_ver) < 3 or fuse_ver_maj < 1:
45 print("error: fuse module can't handle binary data; please upgrade to 1.0+\n",
49 from bup import compat, options, git, vfs, xstat
50 from bup.compat import argv_bytes, fsdecode, py_maj
51 from bup.helpers import log
52 from bup.repo import LocalRepo
55 # FIXME: self.meta and want_meta?
57 # The path handling is just wrong, but the current fuse module can't
60 class BupFs(fuse.Fuse):
61 def __init__(self, repo, verbose=0, fake_metadata=False):
62 fuse.Fuse.__init__(self)
64 self.verbose = verbose
65 self.fake_metadata = fake_metadata
67 def getattr(self, path):
68 path = argv_bytes(path)
71 log('--getattr(%r)\n' % path)
72 res = vfs.resolve(self.repo, path, want_meta=(not self.fake_metadata),
77 if self.fake_metadata:
78 item = vfs.augment_item_meta(self.repo, item, include_size=True)
80 item = vfs.ensure_item_has_metadata(self.repo, item,
83 # FIXME: do we want/need to do anything more with nlink?
84 st = fuse.Stat(st_mode=meta.mode, st_nlink=1, st_size=meta.size)
85 st.st_mode = meta.mode
86 st.st_uid = meta.uid or 0
87 st.st_gid = meta.gid or 0
88 st.st_atime = max(0, xstat.fstime_floor_secs(meta.atime))
89 st.st_mtime = max(0, xstat.fstime_floor_secs(meta.mtime))
90 st.st_ctime = max(0, xstat.fstime_floor_secs(meta.ctime))
93 def readdir(self, path, offset):
94 path = argv_bytes(path)
95 assert not offset # We don't return offsets, so offset should be unused
96 res = vfs.resolve(self.repo, path, follow=False)
97 dir_name, dir_item = res[-1]
100 yield fuse.Direntry('..')
101 # FIXME: make sure want_meta=False is being completely respected
102 for ent_name, ent_item in vfs.contents(repo, dir_item, want_meta=False):
103 fusename = fsdecode(ent_name.replace(b'/', b'-'))
104 yield fuse.Direntry(fusename)
106 def readlink(self, path):
107 path = argv_bytes(path)
109 log('--readlink(%r)\n' % path)
110 res = vfs.resolve(self.repo, path, follow=False)
114 return fsdecode(vfs.readlink(repo, item))
116 def open(self, path, flags):
117 path = argv_bytes(path)
119 log('--open(%r)\n' % path)
120 res = vfs.resolve(self.repo, path, follow=False)
124 accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
125 if (flags & accmode) != os.O_RDONLY:
127 # Return None since read doesn't need the file atm...
128 # If we *do* return the file, it'll show up as the last argument
129 #return vfs.fopen(repo, item)
131 def read(self, path, size, offset):
132 path = argv_bytes(path)
134 log('--read(%r)\n' % path)
135 res = vfs.resolve(self.repo, path, follow=False)
139 with vfs.fopen(repo, item) as f:
145 bup fuse [-d] [-f] <mountpoint>
147 f,foreground run in foreground
148 d,debug run in the foreground and display FUSE debug information
149 o,allow-other allow other users to access the filesystem
150 meta report original metadata for paths when available
151 v,verbose increase log output (can be used more than once)
153 o = options.Options(optspec)
154 opt, flags, extra = o.parse(compat.argv[1:])
158 # Set stderr to be line buffered, even if it's not connected to the console
159 # so that we'll be able to see diagnostics in a timely fashion.
160 errfd = sys.stderr.fileno()
162 sys.stderr = os.fdopen(errfd, 'w', 1)
165 o.fatal('only one mount point argument expected')
167 git.check_repo_or_die()
169 f = BupFs(repo=repo, verbose=opt.verbose, fake_metadata=(not opt.meta))
171 # This is likely wrong, but the fuse module doesn't currently accept bytes
172 f.fuse_args.mountpoint = extra[0]
175 f.fuse_args.add('debug')
177 f.fuse_args.setmod('foreground')
178 f.multithreaded = False
180 f.fuse_args.add('allow_other')