|
1 |
# Copyright (c) 2008 Giorgos Verigakis <verigak@gmail.com>
|
|
2 |
#
|
|
3 |
# Permission to use, copy, modify, and distribute this software for any
|
|
4 |
# purpose with or without fee is hereby granted, provided that the above
|
|
5 |
# copyright notice and this permission notice appear in all copies.
|
|
6 |
#
|
|
7 |
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
8 |
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
9 |
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
10 |
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
11 |
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
12 |
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
13 |
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
14 |
|
|
15 |
from __future__ import division
|
|
16 |
|
|
17 |
from ctypes import *
|
|
18 |
from ctypes.util import find_library
|
|
19 |
from errno import *
|
|
20 |
from functools import partial
|
|
21 |
from os import strerror
|
|
22 |
from platform import machine, system
|
|
23 |
from stat import S_IFDIR
|
|
24 |
from traceback import print_exc
|
|
25 |
|
|
26 |
|
|
27 |
class c_timespec(Structure):
|
|
28 |
_fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]
|
|
29 |
|
|
30 |
class c_utimbuf(Structure):
|
|
31 |
_fields_ = [('actime', c_timespec), ('modtime', c_timespec)]
|
|
32 |
|
|
33 |
class c_stat(Structure):
|
|
34 |
pass # Platform dependent
|
|
35 |
|
|
36 |
_system = system()
|
|
37 |
if _system in ('Darwin', 'FreeBSD'):
|
|
38 |
_libiconv = CDLL(find_library("iconv"), RTLD_GLOBAL) # libfuse dependency
|
|
39 |
ENOTSUP = 45
|
|
40 |
c_dev_t = c_int32
|
|
41 |
c_fsblkcnt_t = c_ulong
|
|
42 |
c_fsfilcnt_t = c_ulong
|
|
43 |
c_gid_t = c_uint32
|
|
44 |
c_mode_t = c_uint16
|
|
45 |
c_off_t = c_int64
|
|
46 |
c_pid_t = c_int32
|
|
47 |
c_uid_t = c_uint32
|
|
48 |
setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
|
|
49 |
c_size_t, c_int, c_uint32)
|
|
50 |
getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
|
|
51 |
c_size_t, c_uint32)
|
|
52 |
c_stat._fields_ = [
|
|
53 |
('st_dev', c_dev_t),
|
|
54 |
('st_ino', c_uint32),
|
|
55 |
('st_mode', c_mode_t),
|
|
56 |
('st_nlink', c_uint16),
|
|
57 |
('st_uid', c_uid_t),
|
|
58 |
('st_gid', c_gid_t),
|
|
59 |
('st_rdev', c_dev_t),
|
|
60 |
('st_atimespec', c_timespec),
|
|
61 |
('st_mtimespec', c_timespec),
|
|
62 |
('st_ctimespec', c_timespec),
|
|
63 |
('st_size', c_off_t),
|
|
64 |
('st_blocks', c_int64),
|
|
65 |
('st_blksize', c_int32)]
|
|
66 |
elif _system == 'Linux':
|
|
67 |
ENOTSUP = 95
|
|
68 |
c_dev_t = c_ulonglong
|
|
69 |
c_fsblkcnt_t = c_ulonglong
|
|
70 |
c_fsfilcnt_t = c_ulonglong
|
|
71 |
c_gid_t = c_uint
|
|
72 |
c_mode_t = c_uint
|
|
73 |
c_off_t = c_longlong
|
|
74 |
c_pid_t = c_int
|
|
75 |
c_uid_t = c_uint
|
|
76 |
setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte), c_size_t, c_int)
|
|
77 |
getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte), c_size_t)
|
|
78 |
|
|
79 |
_machine = machine()
|
|
80 |
if _machine == 'x86_64':
|
|
81 |
c_stat._fields_ = [
|
|
82 |
('st_dev', c_dev_t),
|
|
83 |
('st_ino', c_ulong),
|
|
84 |
('st_nlink', c_ulong),
|
|
85 |
('st_mode', c_mode_t),
|
|
86 |
('st_uid', c_uid_t),
|
|
87 |
('st_gid', c_gid_t),
|
|
88 |
('__pad0', c_int),
|
|
89 |
('st_rdev', c_dev_t),
|
|
90 |
('st_size', c_off_t),
|
|
91 |
('st_blksize', c_long),
|
|
92 |
('st_blocks', c_long),
|
|
93 |
('st_atimespec', c_timespec),
|
|
94 |
('st_mtimespec', c_timespec),
|
|
95 |
('st_ctimespec', c_timespec)]
|
|
96 |
elif _machine == 'ppc':
|
|
97 |
c_stat._fields_ = [
|
|
98 |
('st_dev', c_dev_t),
|
|
99 |
('st_ino', c_ulonglong),
|
|
100 |
('st_mode', c_mode_t),
|
|
101 |
('st_nlink', c_uint),
|
|
102 |
('st_uid', c_uid_t),
|
|
103 |
('st_gid', c_gid_t),
|
|
104 |
('st_rdev', c_dev_t),
|
|
105 |
('__pad2', c_ushort),
|
|
106 |
('st_size', c_off_t),
|
|
107 |
('st_blksize', c_long),
|
|
108 |
('st_blocks', c_longlong),
|
|
109 |
('st_atimespec', c_timespec),
|
|
110 |
('st_mtimespec', c_timespec),
|
|
111 |
('st_ctimespec', c_timespec)]
|
|
112 |
else:
|
|
113 |
# i686, use as fallback for everything else
|
|
114 |
c_stat._fields_ = [
|
|
115 |
('st_dev', c_dev_t),
|
|
116 |
('__pad1', c_ushort),
|
|
117 |
('__st_ino', c_ulong),
|
|
118 |
('st_mode', c_mode_t),
|
|
119 |
('st_nlink', c_uint),
|
|
120 |
('st_uid', c_uid_t),
|
|
121 |
('st_gid', c_gid_t),
|
|
122 |
('st_rdev', c_dev_t),
|
|
123 |
('__pad2', c_ushort),
|
|
124 |
('st_size', c_off_t),
|
|
125 |
('st_blksize', c_long),
|
|
126 |
('st_blocks', c_longlong),
|
|
127 |
('st_atimespec', c_timespec),
|
|
128 |
('st_mtimespec', c_timespec),
|
|
129 |
('st_ctimespec', c_timespec),
|
|
130 |
('st_ino', c_ulonglong)]
|
|
131 |
else:
|
|
132 |
raise NotImplementedError('%s is not supported.' % _system)
|
|
133 |
|
|
134 |
|
|
135 |
class c_statvfs(Structure):
|
|
136 |
_fields_ = [
|
|
137 |
('f_bsize', c_ulong),
|
|
138 |
('f_frsize', c_ulong),
|
|
139 |
('f_blocks', c_fsblkcnt_t),
|
|
140 |
('f_bfree', c_fsblkcnt_t),
|
|
141 |
('f_bavail', c_fsblkcnt_t),
|
|
142 |
('f_files', c_fsfilcnt_t),
|
|
143 |
('f_ffree', c_fsfilcnt_t),
|
|
144 |
('f_favail', c_fsfilcnt_t)]
|
|
145 |
|
|
146 |
if _system == 'FreeBSD':
|
|
147 |
c_fsblkcnt_t = c_uint64
|
|
148 |
c_fsfilcnt_t = c_uint64
|
|
149 |
setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte), c_size_t, c_int)
|
|
150 |
getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte), c_size_t)
|
|
151 |
class c_statvfs(Structure):
|
|
152 |
_fields_ = [
|
|
153 |
('f_bavail', c_fsblkcnt_t),
|
|
154 |
('f_bfree', c_fsblkcnt_t),
|
|
155 |
('f_blocks', c_fsblkcnt_t),
|
|
156 |
('f_favail', c_fsfilcnt_t),
|
|
157 |
('f_ffree', c_fsfilcnt_t),
|
|
158 |
('f_files', c_fsfilcnt_t),
|
|
159 |
('f_bsize', c_ulong),
|
|
160 |
('f_flag', c_ulong),
|
|
161 |
('f_frsize', c_ulong)]
|
|
162 |
|
|
163 |
class fuse_file_info(Structure):
|
|
164 |
_fields_ = [
|
|
165 |
('flags', c_int),
|
|
166 |
('fh_old', c_ulong),
|
|
167 |
('writepage', c_int),
|
|
168 |
('direct_io', c_uint, 1),
|
|
169 |
('keep_cache', c_uint, 1),
|
|
170 |
('flush', c_uint, 1),
|
|
171 |
('padding', c_uint, 29),
|
|
172 |
('fh', c_uint64),
|
|
173 |
('lock_owner', c_uint64)]
|
|
174 |
|
|
175 |
class fuse_context(Structure):
|
|
176 |
_fields_ = [
|
|
177 |
('fuse', c_voidp),
|
|
178 |
('uid', c_uid_t),
|
|
179 |
('gid', c_gid_t),
|
|
180 |
('pid', c_pid_t),
|
|
181 |
('private_data', c_voidp)]
|
|
182 |
|
|
183 |
class fuse_operations(Structure):
|
|
184 |
_fields_ = [
|
|
185 |
('getattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat))),
|
|
186 |
('readlink', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
|
|
187 |
('getdir', c_voidp), # Deprecated, use readdir
|
|
188 |
('mknod', CFUNCTYPE(c_int, c_char_p, c_mode_t, c_dev_t)),
|
|
189 |
('mkdir', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
|
|
190 |
('unlink', CFUNCTYPE(c_int, c_char_p)),
|
|
191 |
('rmdir', CFUNCTYPE(c_int, c_char_p)),
|
|
192 |
('symlink', CFUNCTYPE(c_int, c_char_p, c_char_p)),
|
|
193 |
('rename', CFUNCTYPE(c_int, c_char_p, c_char_p)),
|
|
194 |
('link', CFUNCTYPE(c_int, c_char_p, c_char_p)),
|
|
195 |
('chmod', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
|
|
196 |
('chown', CFUNCTYPE(c_int, c_char_p, c_uid_t, c_gid_t)),
|
|
197 |
('truncate', CFUNCTYPE(c_int, c_char_p, c_off_t)),
|
|
198 |
('utime', c_voidp), # Deprecated, use utimens
|
|
199 |
('open', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
200 |
('read', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t, c_off_t,
|
|
201 |
POINTER(fuse_file_info))),
|
|
202 |
('write', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t, c_off_t,
|
|
203 |
POINTER(fuse_file_info))),
|
|
204 |
('statfs', CFUNCTYPE(c_int, c_char_p, POINTER(c_statvfs))),
|
|
205 |
('flush', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
206 |
('release', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
207 |
('fsync', CFUNCTYPE(c_int, c_char_p, c_int, POINTER(fuse_file_info))),
|
|
208 |
('setxattr', setxattr_t),
|
|
209 |
('getxattr', getxattr_t),
|
|
210 |
('listxattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
|
|
211 |
('removexattr', CFUNCTYPE(c_int, c_char_p, c_char_p)),
|
|
212 |
('opendir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
213 |
('readdir', CFUNCTYPE(c_int, c_char_p, c_voidp, CFUNCTYPE(c_int, c_voidp,
|
|
214 |
c_char_p, POINTER(c_stat), c_off_t), c_off_t, POINTER(fuse_file_info))),
|
|
215 |
('releasedir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
216 |
('fsyncdir', CFUNCTYPE(c_int, c_char_p, c_int, POINTER(fuse_file_info))),
|
|
217 |
('init', CFUNCTYPE(c_voidp, c_voidp)),
|
|
218 |
('destroy', CFUNCTYPE(c_voidp, c_voidp)),
|
|
219 |
('access', CFUNCTYPE(c_int, c_char_p, c_int)),
|
|
220 |
('create', CFUNCTYPE(c_int, c_char_p, c_mode_t, POINTER(fuse_file_info))),
|
|
221 |
('ftruncate', CFUNCTYPE(c_int, c_char_p, c_off_t, POINTER(fuse_file_info))),
|
|
222 |
('fgetattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat),
|
|
223 |
POINTER(fuse_file_info))),
|
|
224 |
('lock', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info), c_int, c_voidp)),
|
|
225 |
('utimens', CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf))),
|
|
226 |
('bmap', CFUNCTYPE(c_int, c_char_p, c_size_t, POINTER(c_ulonglong)))]
|
|
227 |
|
|
228 |
|
|
229 |
def time_of_timespec(ts):
|
|
230 |
return ts.tv_sec + ts.tv_nsec / 10 ** 9
|
|
231 |
|
|
232 |
def set_st_attrs(st, attrs):
|
|
233 |
for key, val in attrs.items():
|
|
234 |
if key in ('st_atime', 'st_mtime', 'st_ctime'):
|
|
235 |
timespec = getattr(st, key + 'spec')
|
|
236 |
timespec.tv_sec = int(val)
|
|
237 |
timespec.tv_nsec = int((val - timespec.tv_sec) * 10 ** 9)
|
|
238 |
elif hasattr(st, key):
|
|
239 |
setattr(st, key, val)
|
|
240 |
|
|
241 |
|
|
242 |
_libfuse_path = find_library('fuse')
|
|
243 |
if not _libfuse_path:
|
|
244 |
raise EnvironmentError('Unable to find libfuse')
|
|
245 |
_libfuse = CDLL(_libfuse_path)
|
|
246 |
_libfuse.fuse_get_context.restype = POINTER(fuse_context)
|
|
247 |
|
|
248 |
|
|
249 |
def fuse_get_context():
|
|
250 |
"""Returns a (uid, gid, pid) tuple"""
|
|
251 |
ctxp = _libfuse.fuse_get_context()
|
|
252 |
ctx = ctxp.contents
|
|
253 |
return ctx.uid, ctx.gid, ctx.pid
|
|
254 |
|
|
255 |
|
|
256 |
class FuseOSError(OSError):
|
|
257 |
def __init__(self, errno):
|
|
258 |
super(FuseOSError, self).__init__(errno, strerror(errno))
|
|
259 |
|
|
260 |
|
|
261 |
class FUSE(object):
|
|
262 |
"""This class is the lower level interface and should not be subclassed
|
|
263 |
under normal use. Its methods are called by fuse.
|
|
264 |
Assumes API version 2.6 or later."""
|
|
265 |
|
|
266 |
def __init__(self, operations, mountpoint, raw_fi=False, **kwargs):
|
|
267 |
"""Setting raw_fi to True will cause FUSE to pass the fuse_file_info
|
|
268 |
class as is to Operations, instead of just the fh field.
|
|
269 |
This gives you access to direct_io, keep_cache, etc."""
|
|
270 |
|
|
271 |
self.operations = operations
|
|
272 |
self.raw_fi = raw_fi
|
|
273 |
args = ['fuse']
|
|
274 |
if kwargs.pop('foreground', False):
|
|
275 |
args.append('-f')
|
|
276 |
if kwargs.pop('debug', False):
|
|
277 |
args.append('-d')
|
|
278 |
if kwargs.pop('nothreads', False):
|
|
279 |
args.append('-s')
|
|
280 |
kwargs.setdefault('fsname', operations.__class__.__name__)
|
|
281 |
args.append('-o')
|
|
282 |
args.append(','.join(key if val == True else '%s=%s' % (key, val)
|
|
283 |
for key, val in kwargs.items()))
|
|
284 |
args.append(mountpoint)
|
|
285 |
argv = (c_char_p * len(args))(*args)
|
|
286 |
|
|
287 |
fuse_ops = fuse_operations()
|
|
288 |
for name, prototype in fuse_operations._fields_:
|
|
289 |
if prototype != c_voidp and getattr(operations, name, None):
|
|
290 |
op = partial(self._wrapper_, getattr(self, name))
|
|
291 |
setattr(fuse_ops, name, prototype(op))
|
|
292 |
err = _libfuse.fuse_main_real(len(args), argv, pointer(fuse_ops),
|
|
293 |
sizeof(fuse_ops), None)
|
|
294 |
del self.operations # Invoke the destructor
|
|
295 |
if err:
|
|
296 |
raise RuntimeError(err)
|
|
297 |
|
|
298 |
def _wrapper_(self, func, *args, **kwargs):
|
|
299 |
"""Decorator for the methods that follow"""
|
|
300 |
try:
|
|
301 |
return func(*args, **kwargs) or 0
|
|
302 |
except OSError, e:
|
|
303 |
return -(e.errno or EFAULT)
|
|
304 |
except:
|
|
305 |
print_exc()
|
|
306 |
return -EFAULT
|
|
307 |
|
|
308 |
def getattr(self, path, buf):
|
|
309 |
return self.fgetattr(path, buf, None)
|
|
310 |
|
|
311 |
def readlink(self, path, buf, bufsize):
|
|
312 |
ret = self.operations('readlink', path)
|
|
313 |
data = create_string_buffer(ret[:bufsize - 1])
|
|
314 |
memmove(buf, data, len(data))
|
|
315 |
return 0
|
|
316 |
|
|
317 |
def mknod(self, path, mode, dev):
|
|
318 |
return self.operations('mknod', path, mode, dev)
|
|
319 |
|
|
320 |
def mkdir(self, path, mode):
|
|
321 |
return self.operations('mkdir', path, mode)
|
|
322 |
|
|
323 |
def unlink(self, path):
|
|
324 |
return self.operations('unlink', path)
|
|
325 |
|
|
326 |
def rmdir(self, path):
|
|
327 |
return self.operations('rmdir', path)
|
|
328 |
|
|
329 |
def symlink(self, source, target):
|
|
330 |
return self.operations('symlink', target, source)
|
|
331 |
|
|
332 |
def rename(self, old, new):
|
|
333 |
return self.operations('rename', old, new)
|
|
334 |
|
|
335 |
def link(self, source, target):
|
|
336 |
return self.operations('link', target, source)
|
|
337 |
|
|
338 |
def chmod(self, path, mode):
|
|
339 |
return self.operations('chmod', path, mode)
|
|
340 |
|
|
341 |
def chown(self, path, uid, gid):
|
|
342 |
# Check if any of the arguments is a -1 that has overflowed
|
|
343 |
if c_uid_t(uid + 1).value == 0:
|
|
344 |
uid = -1
|
|
345 |
if c_gid_t(gid + 1).value == 0:
|
|
346 |
gid = -1
|
|
347 |
return self.operations('chown', path, uid, gid)
|
|
348 |
|
|
349 |
def truncate(self, path, length):
|
|
350 |
return self.operations('truncate', path, length)
|
|
351 |
|
|
352 |
def open(self, path, fip):
|
|
353 |
fi = fip.contents
|
|
354 |
if self.raw_fi:
|
|
355 |
return self.operations('open', path, fi)
|
|
356 |
else:
|
|
357 |
fi.fh = self.operations('open', path, fi.flags)
|
|
358 |
return 0
|
|
359 |
|
|
360 |
def read(self, path, buf, size, offset, fip):
|
|
361 |
fh = fip.contents if self.raw_fi else fip.contents.fh
|
|
362 |
ret = self.operations('read', path, size, offset, fh)
|
|
363 |
if not ret:
|
|
364 |
return 0
|
|
365 |
data = create_string_buffer(ret[:size], size)
|
|
366 |
memmove(buf, data, size)
|
|
367 |
return size
|
|
368 |
|
|
369 |
def write(self, path, buf, size, offset, fip):
|
|
370 |
data = string_at(buf, size)
|
|
371 |
fh = fip.contents if self.raw_fi else fip.contents.fh
|
|
372 |
return self.operations('write', path, data, offset, fh)
|
|
373 |
|
|
374 |
def statfs(self, path, buf):
|
|
375 |
stv = buf.contents
|
|
376 |
attrs = self.operations('statfs', path)
|
|
377 |
for key, val in attrs.items():
|
|
378 |
if hasattr(stv, key):
|
|
379 |
setattr(stv, key, val)
|
|
380 |
return 0
|
|
381 |
|
|
382 |
def flush(self, path, fip):
|
|
383 |
fh = fip.contents if self.raw_fi else fip.contents.fh
|
|
384 |
return self.operations('flush', path, fh)
|
|
385 |
|
|
386 |
def release(self, path, fip):
|
|
387 |
fh = fip.contents if self.raw_fi else fip.contents.fh
|
|
388 |
return self.operations('release', path, fh)
|
|
389 |
|
|
390 |
def fsync(self, path, datasync, fip):
|
|
391 |
fh = fip.contents if self.raw_fi else fip.contents.fh
|
|
392 |
return self.operations('fsync', path, datasync, fh)
|
|
393 |
|
|
394 |
def setxattr(self, path, name, value, size, options, *args):
|
|
395 |
data = string_at(value, size)
|
|
396 |
return self.operations('setxattr', path, name, data, options, *args)
|
|
397 |
|
|
398 |
def getxattr(self, path, name, value, size, *args):
|
|
399 |
ret = self.operations('getxattr', path, name, *args)
|
|
400 |
retsize = len(ret)
|
|
401 |
buf = create_string_buffer(ret, retsize) # Does not add trailing 0
|
|
402 |
if bool(value):
|
|
403 |
if retsize > size:
|
|
404 |
return -ERANGE
|
|
405 |
memmove(value, buf, retsize)
|
|
406 |
return retsize
|
|
407 |
|
|
408 |
def listxattr(self, path, namebuf, size):
|
|
409 |
ret = self.operations('listxattr', path)
|
|
410 |
buf = create_string_buffer('\x00'.join(ret)) if ret else ''
|
|
411 |
bufsize = len(buf)
|
|
412 |
if bool(namebuf):
|
|
413 |
if bufsize > size:
|
|
414 |
return -ERANGE
|
|
415 |
memmove(namebuf, buf, bufsize)
|
|
416 |
return bufsize
|
|
417 |
|
|
418 |
def removexattr(self, path, name):
|
|
419 |
return self.operations('removexattr', path, name)
|
|
420 |
|
|
421 |
def opendir(self, path, fip):
|
|
422 |
# Ignore raw_fi
|
|
423 |
fip.contents.fh = self.operations('opendir', path)
|
|
424 |
return 0
|
|
425 |
|
|
426 |
def readdir(self, path, buf, filler, offset, fip):
|
|
427 |
# Ignore raw_fi
|
|
428 |
for item in self.operations('readdir', path, fip.contents.fh):
|
|
429 |
if isinstance(item, (str, unicode)):
|
|
430 |
name, st, offset = item, None, 0
|
|
431 |
else:
|
|
432 |
name, attrs, offset = item
|
|
433 |
if attrs:
|
|
434 |
st = c_stat()
|
|
435 |
set_st_attrs(st, attrs)
|
|
436 |
else:
|
|
437 |
st = None
|
|
438 |
if filler(buf, name, st, offset) != 0:
|
|
439 |
break
|
|
440 |
return 0
|
|
441 |
|
|
442 |
def releasedir(self, path, fip):
|
|
443 |
# Ignore raw_fi
|
|
444 |
return self.operations('releasedir', path, fip.contents.fh)
|
|
445 |
|
|
446 |
def fsyncdir(self, path, datasync, fip):
|
|
447 |
# Ignore raw_fi
|
|
448 |
return self.operations('fsyncdir', path, datasync, fip.contents.fh)
|
|
449 |
|
|
450 |
def init(self, conn):
|
|
451 |
return self.operations('init', '/')
|
|
452 |
|
|
453 |
def destroy(self, private_data):
|
|
454 |
return self.operations('destroy', '/')
|
|
455 |
|
|
456 |
def access(self, path, amode):
|
|
457 |
return self.operations('access', path, amode)
|
|
458 |
|
|
459 |
def create(self, path, mode, fip):
|
|
460 |
fi = fip.contents
|
|
461 |
if self.raw_fi:
|
|
462 |
return self.operations('create', path, mode, fi)
|
|
463 |
else:
|
|
464 |
fi.fh = self.operations('create', path, mode)
|
|
465 |
return 0
|
|
466 |
|
|
467 |
def ftruncate(self, path, length, fip):
|
|
468 |
fh = fip.contents if self.raw_fi else fip.contents.fh
|
|
469 |
return self.operations('truncate', path, length, fh)
|
|
470 |
|
|
471 |
def fgetattr(self, path, buf, fip):
|
|
472 |
memset(buf, 0, sizeof(c_stat))
|
|
473 |
st = buf.contents
|
|
474 |
fh = fip and (fip.contents if self.raw_fi else fip.contents.fh)
|
|
475 |
attrs = self.operations('getattr', path, fh)
|
|
476 |
set_st_attrs(st, attrs)
|
|
477 |
return 0
|
|
478 |
|
|
479 |
def lock(self, path, fip, cmd, lock):
|
|
480 |
fh = fip.contents if self.raw_fi else fip.contents.fh
|
|
481 |
return self.operations('lock', path, fh, cmd, lock)
|
|
482 |
|
|
483 |
def utimens(self, path, buf):
|
|
484 |
if buf:
|
|
485 |
atime = time_of_timespec(buf.contents.actime)
|
|
486 |
mtime = time_of_timespec(buf.contents.modtime)
|
|
487 |
times = (atime, mtime)
|
|
488 |
else:
|
|
489 |
times = None
|
|
490 |
return self.operations('utimens', path, times)
|
|
491 |
|
|
492 |
def bmap(self, path, blocksize, idx):
|
|
493 |
return self.operations('bmap', path, blocksize, idx)
|
|
494 |
|
|
495 |
|
|
496 |
class Operations(object):
|
|
497 |
"""This class should be subclassed and passed as an argument to FUSE on
|
|
498 |
initialization. All operations should raise a FuseOSError exception
|
|
499 |
on error.
|
|
500 |
|
|
501 |
When in doubt of what an operation should do, check the FUSE header
|
|
502 |
file or the corresponding system call man page."""
|
|
503 |
|
|
504 |
def __call__(self, op, *args):
|
|
505 |
if not hasattr(self, op):
|
|
506 |
raise FuseOSError(EFAULT)
|
|
507 |
return getattr(self, op)(*args)
|
|
508 |
|
|
509 |
def access(self, path, amode):
|
|
510 |
return 0
|
|
511 |
|
|
512 |
bmap = None
|
|
513 |
|
|
514 |
def chmod(self, path, mode):
|
|
515 |
raise FuseOSError(EROFS)
|
|
516 |
|
|
517 |
def chown(self, path, uid, gid):
|
|
518 |
raise FuseOSError(EROFS)
|
|
519 |
|
|
520 |
def create(self, path, mode, fi=None):
|
|
521 |
"""When raw_fi is False (default case), fi is None and create should
|
|
522 |
return a numerical file handle.
|
|
523 |
When raw_fi is True the file handle should be set directly by create
|
|
524 |
and return 0."""
|
|
525 |
raise FuseOSError(EROFS)
|
|
526 |
|
|
527 |
def destroy(self, path):
|
|
528 |
"""Called on filesystem destruction. Path is always /"""
|
|
529 |
pass
|
|
530 |
|
|
531 |
def flush(self, path, fh):
|
|
532 |
return 0
|
|
533 |
|
|
534 |
def fsync(self, path, datasync, fh):
|
|
535 |
return 0
|
|
536 |
|
|
537 |
def fsyncdir(self, path, datasync, fh):
|
|
538 |
return 0
|
|
539 |
|
|
540 |
def getattr(self, path, fh=None):
|
|
541 |
"""Returns a dictionary with keys identical to the stat C structure
|
|
542 |
of stat(2).
|
|
543 |
st_atime, st_mtime and st_ctime should be floats.
|
|
544 |
NOTE: There is an incombatibility between Linux and Mac OS X concerning
|
|
545 |
st_nlink of directories. Mac OS X counts all files inside the directory,
|
|
546 |
while Linux counts only the subdirectories."""
|
|
547 |
|
|
548 |
if path != '/':
|
|
549 |
raise FuseOSError(ENOENT)
|
|
550 |
return dict(st_mode=(S_IFDIR | 0755), st_nlink=2)
|
|
551 |
|
|
552 |
def getxattr(self, path, name, position=0):
|
|
553 |
raise FuseOSError(ENOTSUP)
|
|
554 |
|
|
555 |
def init(self, path):
|
|
556 |
"""Called on filesystem initialization. Path is always /
|
|
557 |
Use it instead of __init__ if you start threads on initialization."""
|
|
558 |
pass
|
|
559 |
|
|
560 |
def link(self, target, source):
|
|
561 |
raise FuseOSError(EROFS)
|
|
562 |
|
|
563 |
def listxattr(self, path):
|
|
564 |
return []
|
|
565 |
|
|
566 |
lock = None
|
|
567 |
|
|
568 |
def mkdir(self, path, mode):
|
|
569 |
raise FuseOSError(EROFS)
|
|
570 |
|
|
571 |
def mknod(self, path, mode, dev):
|
|
572 |
raise FuseOSError(EROFS)
|
|
573 |
|
|
574 |
def open(self, path, flags):
|
|
575 |
"""When raw_fi is False (default case), open should return a numerical
|
|
576 |
file handle.
|
|
577 |
When raw_fi is True the signature of open becomes:
|
|
578 |
open(self, path, fi)
|
|
579 |
and the file handle should be set directly."""
|
|
580 |
return 0
|
|
581 |
|
|
582 |
def opendir(self, path):
|
|
583 |
"""Returns a numerical file handle."""
|
|
584 |
return 0
|
|
585 |
|
|
586 |
def read(self, path, size, offset, fh):
|
|
587 |
"""Returns a string containing the data requested."""
|
|
588 |
raise FuseOSError(EIO)
|
|
589 |
|
|
590 |
def readdir(self, path, fh):
|
|
591 |
"""Can return either a list of names, or a list of (name, attrs, offset)
|
|
592 |
tuples. attrs is a dict as in getattr."""
|
|
593 |
return ['.', '..']
|
|
594 |
|
|
595 |
def readlink(self, path):
|
|
596 |
raise FuseOSError(ENOENT)
|
|
597 |
|
|
598 |
def release(self, path, fh):
|
|
599 |
return 0
|
|
600 |
|
|
601 |
def releasedir(self, path, fh):
|
|
602 |
return 0
|
|
603 |
|
|
604 |
def removexattr(self, path, name):
|
|
605 |
raise FuseOSError(ENOTSUP)
|
|
606 |
|
|
607 |
def rename(self, old, new):
|
|
608 |
raise FuseOSError(EROFS)
|
|
609 |
|
|
610 |
def rmdir(self, path):
|
|
611 |
raise FuseOSError(EROFS)
|
|
612 |
|
|
613 |
def setxattr(self, path, name, value, options, position=0):
|
|
614 |
raise FuseOSError(ENOTSUP)
|
|
615 |
|
|
616 |
def statfs(self, path):
|
|
617 |
"""Returns a dictionary with keys identical to the statvfs C structure
|
|
618 |
of statvfs(3).
|
|
619 |
On Mac OS X f_bsize and f_frsize must be a power of 2 (minimum 512)."""
|
|
620 |
return {}
|
|
621 |
|
|
622 |
def symlink(self, target, source):
|
|
623 |
raise FuseOSError(EROFS)
|
|
624 |
|
|
625 |
def truncate(self, path, length, fh=None):
|
|
626 |
raise FuseOSError(EROFS)
|
|
627 |
|
|
628 |
def unlink(self, path):
|
|
629 |
raise FuseOSError(EROFS)
|
|
630 |
|
|
631 |
def utimens(self, path, times=None):
|
|
632 |
"""Times is a (atime, mtime) tuple. If None use current time."""
|
|
633 |
return 0
|
|
634 |
|
|
635 |
def write(self, path, data, offset, fh):
|
|
636 |
raise FuseOSError(EROFS)
|
|
637 |
|
|
638 |
|
|
639 |
class LoggingMixIn:
|
|
640 |
def __call__(self, op, path, *args):
|
|
641 |
print '->', op, path, repr(args)
|
|
642 |
ret = '[Unhandled Exception]'
|
|
643 |
try:
|
|
644 |
ret = getattr(self, op)(path, *args)
|
|
645 |
return ret
|
|
646 |
except OSError, e:
|
|
647 |
ret = str(e)
|
|
648 |
raise
|
|
649 |
finally:
|
|
650 |
print '<-', op, repr(ret)
|