root / pithos / lib / fuse.py @ a2defd86
History | View | Annotate | Download (22.3 kB)
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) |