]> arthur.barton.de Git - bup.git/blob - lib/bup/cmd/fuse.py
test: add pylint and test imports
[bup.git] / lib / bup / cmd / fuse.py
1
2 from __future__ import absolute_import, print_function
3 import errno, os, sys
4
5 try:
6     import fuse
7 except ImportError:
8     print('error: cannot find the python "fuse" module; please install it',
9           file=sys.stderr)
10     sys.exit(2)
11 if not hasattr(fuse, '__version__'):
12     print('error: fuse module is too old for fuse.__version__', file=sys.stderr)
13     sys.exit(2)
14 fuse.fuse_python_api = (0, 2)
15
16 if sys.version_info[0] > 2:
17     try:
18         fuse_ver = fuse.__version__.split('.')
19         fuse_ver_maj = int(fuse_ver[0])
20     except:
21         log('error: cannot determine the fuse major version; please report',
22             file=sys.stderr)
23         sys.exit(2)
24     if len(fuse_ver) < 3 or fuse_ver_maj < 1:
25         print("error: fuse module can't handle binary data; please upgrade to 1.0+\n",
26               file=sys.stderr)
27         sys.exit(2)
28
29 from bup import options, git, vfs, xstat
30 from bup.compat import argv_bytes, fsdecode
31 from bup.helpers import log
32 from bup.repo import LocalRepo
33
34
35 # FIXME: self.meta and want_meta?
36
37 # The path handling is just wrong, but the current fuse module can't
38 # handle bytes paths.
39
40 class BupFs(fuse.Fuse):
41     def __init__(self, repo, verbose=0, fake_metadata=False):
42         fuse.Fuse.__init__(self)
43         self.repo = repo
44         self.verbose = verbose
45         self.fake_metadata = fake_metadata
46     
47     def getattr(self, path):
48         path = argv_bytes(path)
49         if self.verbose > 0:
50             log('--getattr(%r)\n' % path)
51         res = vfs.resolve(self.repo, path, want_meta=(not self.fake_metadata),
52                           follow=False)
53         name, item = res[-1]
54         if not item:
55             return -errno.ENOENT
56         if self.fake_metadata:
57             item = vfs.augment_item_meta(self.repo, item, include_size=True)
58         else:
59             item = vfs.ensure_item_has_metadata(self.repo, item,
60                                                 include_size=True)
61         meta = item.meta
62         # FIXME: do we want/need to do anything more with nlink?
63         st = fuse.Stat(st_mode=meta.mode, st_nlink=1, st_size=meta.size)
64         st.st_mode = meta.mode
65         st.st_uid = meta.uid or 0
66         st.st_gid = meta.gid or 0
67         st.st_atime = max(0, xstat.fstime_floor_secs(meta.atime))
68         st.st_mtime = max(0, xstat.fstime_floor_secs(meta.mtime))
69         st.st_ctime = max(0, xstat.fstime_floor_secs(meta.ctime))
70         return st
71
72     def readdir(self, path, offset):
73         path = argv_bytes(path)
74         assert not offset  # We don't return offsets, so offset should be unused
75         res = vfs.resolve(self.repo, path, follow=False)
76         dir_name, dir_item = res[-1]
77         if not dir_item:
78             yield -errno.ENOENT
79         yield fuse.Direntry('..')
80         # FIXME: make sure want_meta=False is being completely respected
81         for ent_name, ent_item in vfs.contents(self.repo, dir_item, want_meta=False):
82             fusename = fsdecode(ent_name.replace(b'/', b'-'))
83             yield fuse.Direntry(fusename)
84
85     def readlink(self, path):
86         path = argv_bytes(path)
87         if self.verbose > 0:
88             log('--readlink(%r)\n' % path)
89         res = vfs.resolve(self.repo, path, follow=False)
90         name, item = res[-1]
91         if not item:
92             return -errno.ENOENT
93         return fsdecode(vfs.readlink(self.repo, item))
94
95     def open(self, path, flags):
96         path = argv_bytes(path)
97         if self.verbose > 0:
98             log('--open(%r)\n' % path)
99         res = vfs.resolve(self.repo, path, follow=False)
100         name, item = res[-1]
101         if not item:
102             return -errno.ENOENT
103         accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
104         if (flags & accmode) != os.O_RDONLY:
105             return -errno.EACCES
106         # Return None since read doesn't need the file atm...
107         # If we *do* return the file, it'll show up as the last argument
108         #return vfs.fopen(repo, item)
109
110     def read(self, path, size, offset):
111         path = argv_bytes(path)
112         if self.verbose > 0:
113             log('--read(%r)\n' % path)
114         res = vfs.resolve(self.repo, path, follow=False)
115         name, item = res[-1]
116         if not item:
117             return -errno.ENOENT
118         with vfs.fopen(self.repo, item) as f:
119             f.seek(offset)
120             return f.read(size)
121
122
123 optspec = """
124 bup fuse [-d] [-f] <mountpoint>
125 --
126 f,foreground  run in foreground
127 d,debug       run in the foreground and display FUSE debug information
128 o,allow-other allow other users to access the filesystem
129 meta          report original metadata for paths when available
130 v,verbose     increase log output (can be used more than once)
131 """
132
133 def main(argv):
134     o = options.Options(optspec)
135     opt, flags, extra = o.parse_bytes(argv[1:])
136     if not opt.verbose:
137         opt.verbose = 0
138
139     # Set stderr to be line buffered, even if it's not connected to the console
140     # so that we'll be able to see diagnostics in a timely fashion.
141     errfd = sys.stderr.fileno()
142     sys.stderr.flush()
143     sys.stderr = os.fdopen(errfd, 'w', 1)
144
145     if len(extra) != 1:
146         o.fatal('only one mount point argument expected')
147
148     git.check_repo_or_die()
149     repo = LocalRepo()
150     f = BupFs(repo=repo, verbose=opt.verbose, fake_metadata=(not opt.meta))
151
152     # This is likely wrong, but the fuse module doesn't currently accept bytes
153     f.fuse_args.mountpoint = extra[0]
154
155     if opt.debug:
156         f.fuse_args.add('debug')
157     if opt.foreground:
158         f.fuse_args.setmod('foreground')
159     f.multithreaded = False
160     if opt.allow_other:
161         f.fuse_args.add('allow_other')
162     f.main()