Package list python-fusepy / 08796b3
New upstream version 2.0.4 Sascha Steinbiss 4 years ago
11 changed file(s) with 1195 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 include README*
0 Metadata-Version: 1.1
1 Name: fusepy
2 Version: 2.0.4
3 Summary: Simple ctypes bindings for FUSE
4 Home-page: http://github.com/terencehonles/fusepy
5 Author: Terence Honles
6 Author-email: terence@honles.com
7 License: ISC
8 Description: fusepy
9 ======
10
11 ``fusepy`` is a Python module that provides a simple interface to FUSE_ and
12 MacFUSE_. It's just one file and is implemented using ctypes.
13
14 The original version of ``fusepy`` was hosted on `Google Code`_, but is now
15 `officially hosted on GitHub`_.
16
17 ``fusepy`` is written in 2x syntax, but trying to pay attention to bytes and
18 other changes 3x would care about.
19
20 examples
21 --------
22 See some examples of how you can use fusepy:
23
24 :memory_: A simple memory filesystem
25 :loopback_: A loopback filesystem
26 :context_: Sample usage of fuse_get_context()
27 :sftp_: A simple SFTP filesystem (requires paramiko)
28
29 To get started download_ fusepy or just browse the source_.
30
31 fusepy requires FUSE 2.6 (or later) and runs on:
32
33 - Linux (i386, x86_64, PPC, arm64, MIPS)
34 - Mac OS X (Intel, PowerPC)
35 - FreeBSD (i386, amd64)
36
37
38 .. _FUSE: http://fuse.sourceforge.net/
39 .. _MacFUSE: http://code.google.com/p/macfuse/
40 .. _`Google Code`: http://code.google.com/p/fusepy/
41
42 .. _officially hosted on GitHub: source_
43 .. _download: https://github.com/terencehonles/fusepy/zipball/master
44 .. _source: http://github.com/terencehonles/fusepy
45
46 .. examples
47 .. _memory: http://github.com/terencehonles/fusepy/blob/master/examples/memory.py
48 .. _loopback: http://github.com/terencehonles/fusepy/blob/master/examples/loopback.py
49 .. _context: http://github.com/terencehonles/fusepy/blob/master/examples/context.py
50 .. _sftp: http://github.com/terencehonles/fusepy/blob/master/examples/sftp.py
51
52 Platform: UNKNOWN
53 Classifier: Intended Audience :: Developers
54 Classifier: License :: OSI Approved :: ISC License (ISCL)
55 Classifier: Operating System :: MacOS
56 Classifier: Operating System :: POSIX
57 Classifier: Operating System :: Unix
58 Classifier: Programming Language :: Python :: 2.6
59 Classifier: Programming Language :: Python :: 3
60 Classifier: Topic :: System :: Filesystems
0 README.rst
0 fusepy
1 ======
2
3 ``fusepy`` is a Python module that provides a simple interface to FUSE_ and
4 MacFUSE_. It's just one file and is implemented using ctypes.
5
6 The original version of ``fusepy`` was hosted on `Google Code`_, but is now
7 `officially hosted on GitHub`_.
8
9 ``fusepy`` is written in 2x syntax, but trying to pay attention to bytes and
10 other changes 3x would care about.
11
12 examples
13 --------
14 See some examples of how you can use fusepy:
15
16 :memory_: A simple memory filesystem
17 :loopback_: A loopback filesystem
18 :context_: Sample usage of fuse_get_context()
19 :sftp_: A simple SFTP filesystem (requires paramiko)
20
21 To get started download_ fusepy or just browse the source_.
22
23 fusepy requires FUSE 2.6 (or later) and runs on:
24
25 - Linux (i386, x86_64, PPC, arm64, MIPS)
26 - Mac OS X (Intel, PowerPC)
27 - FreeBSD (i386, amd64)
28
29
30 .. _FUSE: http://fuse.sourceforge.net/
31 .. _MacFUSE: http://code.google.com/p/macfuse/
32 .. _`Google Code`: http://code.google.com/p/fusepy/
33
34 .. _officially hosted on GitHub: source_
35 .. _download: https://github.com/terencehonles/fusepy/zipball/master
36 .. _source: http://github.com/terencehonles/fusepy
37
38 .. examples
39 .. _memory: http://github.com/terencehonles/fusepy/blob/master/examples/memory.py
40 .. _loopback: http://github.com/terencehonles/fusepy/blob/master/examples/loopback.py
41 .. _context: http://github.com/terencehonles/fusepy/blob/master/examples/context.py
42 .. _sftp: http://github.com/terencehonles/fusepy/blob/master/examples/sftp.py
0 # Copyright (c) 2012 Terence Honles <terence@honles.com> (maintainer)
1 # Copyright (c) 2008 Giorgos Verigakis <verigak@gmail.com> (author)
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 print_function, absolute_import, division
16
17 from ctypes import *
18 from ctypes.util import find_library
19 from errno import *
20 from os import strerror
21 from platform import machine, system
22 from signal import signal, SIGINT, SIG_DFL
23 from stat import S_IFDIR
24 from traceback import print_exc
25
26 import logging
27
28 try:
29 from functools import partial
30 except ImportError:
31 # http://docs.python.org/library/functools.html#functools.partial
32 def partial(func, *args, **keywords):
33 def newfunc(*fargs, **fkeywords):
34 newkeywords = keywords.copy()
35 newkeywords.update(fkeywords)
36 return func(*(args + fargs), **newkeywords)
37
38 newfunc.func = func
39 newfunc.args = args
40 newfunc.keywords = keywords
41 return newfunc
42
43 try:
44 basestring
45 except NameError:
46 basestring = str
47
48 class c_timespec(Structure):
49 _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]
50
51 class c_utimbuf(Structure):
52 _fields_ = [('actime', c_timespec), ('modtime', c_timespec)]
53
54 class c_stat(Structure):
55 pass # Platform dependent
56
57 _system = system()
58 _machine = machine()
59
60 if _system == 'Darwin':
61 _libiconv = CDLL(find_library('iconv'), RTLD_GLOBAL) # libfuse dependency
62 _libfuse_path = (find_library('fuse4x') or find_library('osxfuse') or
63 find_library('fuse'))
64 else:
65 _libfuse_path = find_library('fuse')
66
67 if not _libfuse_path:
68 raise EnvironmentError('Unable to find libfuse')
69 else:
70 _libfuse = CDLL(_libfuse_path)
71
72 if _system == 'Darwin' and hasattr(_libfuse, 'macfuse_version'):
73 _system = 'Darwin-MacFuse'
74
75
76 if _system in ('Darwin', 'Darwin-MacFuse', 'FreeBSD'):
77 ENOTSUP = 45
78 c_dev_t = c_int32
79 c_fsblkcnt_t = c_ulong
80 c_fsfilcnt_t = c_ulong
81 c_gid_t = c_uint32
82 c_mode_t = c_uint16
83 c_off_t = c_int64
84 c_pid_t = c_int32
85 c_uid_t = c_uint32
86 setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
87 c_size_t, c_int, c_uint32)
88 getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
89 c_size_t, c_uint32)
90 if _system == 'Darwin':
91 c_stat._fields_ = [
92 ('st_dev', c_dev_t),
93 ('st_mode', c_mode_t),
94 ('st_nlink', c_uint16),
95 ('st_ino', c_uint64),
96 ('st_uid', c_uid_t),
97 ('st_gid', c_gid_t),
98 ('st_rdev', c_dev_t),
99 ('st_atimespec', c_timespec),
100 ('st_mtimespec', c_timespec),
101 ('st_ctimespec', c_timespec),
102 ('st_birthtimespec', c_timespec),
103 ('st_size', c_off_t),
104 ('st_blocks', c_int64),
105 ('st_blksize', c_int32),
106 ('st_flags', c_int32),
107 ('st_gen', c_int32),
108 ('st_lspare', c_int32),
109 ('st_qspare', c_int64)]
110 else:
111 c_stat._fields_ = [
112 ('st_dev', c_dev_t),
113 ('st_ino', c_uint32),
114 ('st_mode', c_mode_t),
115 ('st_nlink', c_uint16),
116 ('st_uid', c_uid_t),
117 ('st_gid', c_gid_t),
118 ('st_rdev', c_dev_t),
119 ('st_atimespec', c_timespec),
120 ('st_mtimespec', c_timespec),
121 ('st_ctimespec', c_timespec),
122 ('st_size', c_off_t),
123 ('st_blocks', c_int64),
124 ('st_blksize', c_int32)]
125 elif _system == 'Linux':
126 ENOTSUP = 95
127 c_dev_t = c_ulonglong
128 c_fsblkcnt_t = c_ulonglong
129 c_fsfilcnt_t = c_ulonglong
130 c_gid_t = c_uint
131 c_mode_t = c_uint
132 c_off_t = c_longlong
133 c_pid_t = c_int
134 c_uid_t = c_uint
135 setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
136 c_size_t, c_int)
137
138 getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
139 c_size_t)
140
141 if _machine == 'x86_64':
142 c_stat._fields_ = [
143 ('st_dev', c_dev_t),
144 ('st_ino', c_ulong),
145 ('st_nlink', c_ulong),
146 ('st_mode', c_mode_t),
147 ('st_uid', c_uid_t),
148 ('st_gid', c_gid_t),
149 ('__pad0', c_int),
150 ('st_rdev', c_dev_t),
151 ('st_size', c_off_t),
152 ('st_blksize', c_long),
153 ('st_blocks', c_long),
154 ('st_atimespec', c_timespec),
155 ('st_mtimespec', c_timespec),
156 ('st_ctimespec', c_timespec)]
157 elif _machine == 'mips':
158 c_stat._fields_ = [
159 ('st_dev', c_dev_t),
160 ('__pad1_1', c_ulong),
161 ('__pad1_2', c_ulong),
162 ('__pad1_3', c_ulong),
163 ('st_ino', c_ulong),
164 ('st_mode', c_mode_t),
165 ('st_nlink', c_ulong),
166 ('st_uid', c_uid_t),
167 ('st_gid', c_gid_t),
168 ('st_rdev', c_dev_t),
169 ('__pad2_1', c_ulong),
170 ('__pad2_2', c_ulong),
171 ('st_size', c_off_t),
172 ('__pad3', c_ulong),
173 ('st_atimespec', c_timespec),
174 ('__pad4', c_ulong),
175 ('st_mtimespec', c_timespec),
176 ('__pad5', c_ulong),
177 ('st_ctimespec', c_timespec),
178 ('__pad6', c_ulong),
179 ('st_blksize', c_long),
180 ('st_blocks', c_long),
181 ('__pad7_1', c_ulong),
182 ('__pad7_2', c_ulong),
183 ('__pad7_3', c_ulong),
184 ('__pad7_4', c_ulong),
185 ('__pad7_5', c_ulong),
186 ('__pad7_6', c_ulong),
187 ('__pad7_7', c_ulong),
188 ('__pad7_8', c_ulong),
189 ('__pad7_9', c_ulong),
190 ('__pad7_10', c_ulong),
191 ('__pad7_11', c_ulong),
192 ('__pad7_12', c_ulong),
193 ('__pad7_13', c_ulong),
194 ('__pad7_14', c_ulong)]
195 elif _machine == 'ppc':
196 c_stat._fields_ = [
197 ('st_dev', c_dev_t),
198 ('st_ino', c_ulonglong),
199 ('st_mode', c_mode_t),
200 ('st_nlink', c_uint),
201 ('st_uid', c_uid_t),
202 ('st_gid', c_gid_t),
203 ('st_rdev', c_dev_t),
204 ('__pad2', c_ushort),
205 ('st_size', c_off_t),
206 ('st_blksize', c_long),
207 ('st_blocks', c_longlong),
208 ('st_atimespec', c_timespec),
209 ('st_mtimespec', c_timespec),
210 ('st_ctimespec', c_timespec)]
211 elif _machine == 'aarch64':
212 c_stat._fields_ = [
213 ('st_dev', c_dev_t),
214 ('st_ino', c_ulong),
215 ('st_mode', c_mode_t),
216 ('st_nlink', c_uint),
217 ('st_uid', c_uid_t),
218 ('st_gid', c_gid_t),
219 ('st_rdev', c_dev_t),
220 ('__pad1', c_ulong),
221 ('st_size', c_off_t),
222 ('st_blksize', c_int),
223 ('__pad2', c_int),
224 ('st_blocks', c_long),
225 ('st_atimespec', c_timespec),
226 ('st_mtimespec', c_timespec),
227 ('st_ctimespec', c_timespec)]
228 else:
229 # i686, use as fallback for everything else
230 c_stat._fields_ = [
231 ('st_dev', c_dev_t),
232 ('__pad1', c_ushort),
233 ('__st_ino', c_ulong),
234 ('st_mode', c_mode_t),
235 ('st_nlink', c_uint),
236 ('st_uid', c_uid_t),
237 ('st_gid', c_gid_t),
238 ('st_rdev', c_dev_t),
239 ('__pad2', c_ushort),
240 ('st_size', c_off_t),
241 ('st_blksize', c_long),
242 ('st_blocks', c_longlong),
243 ('st_atimespec', c_timespec),
244 ('st_mtimespec', c_timespec),
245 ('st_ctimespec', c_timespec),
246 ('st_ino', c_ulonglong)]
247 else:
248 raise NotImplementedError('%s is not supported.' % _system)
249
250
251 class c_statvfs(Structure):
252 _fields_ = [
253 ('f_bsize', c_ulong),
254 ('f_frsize', c_ulong),
255 ('f_blocks', c_fsblkcnt_t),
256 ('f_bfree', c_fsblkcnt_t),
257 ('f_bavail', c_fsblkcnt_t),
258 ('f_files', c_fsfilcnt_t),
259 ('f_ffree', c_fsfilcnt_t),
260 ('f_favail', c_fsfilcnt_t),
261 ('f_fsid', c_ulong),
262 #('unused', c_int),
263 ('f_flag', c_ulong),
264 ('f_namemax', c_ulong)]
265
266 if _system == 'FreeBSD':
267 c_fsblkcnt_t = c_uint64
268 c_fsfilcnt_t = c_uint64
269 setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
270 c_size_t, c_int)
271
272 getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
273 c_size_t)
274
275 class c_statvfs(Structure):
276 _fields_ = [
277 ('f_bavail', c_fsblkcnt_t),
278 ('f_bfree', c_fsblkcnt_t),
279 ('f_blocks', c_fsblkcnt_t),
280 ('f_favail', c_fsfilcnt_t),
281 ('f_ffree', c_fsfilcnt_t),
282 ('f_files', c_fsfilcnt_t),
283 ('f_bsize', c_ulong),
284 ('f_flag', c_ulong),
285 ('f_frsize', c_ulong)]
286
287 class fuse_file_info(Structure):
288 _fields_ = [
289 ('flags', c_int),
290 ('fh_old', c_ulong),
291 ('writepage', c_int),
292 ('direct_io', c_uint, 1),
293 ('keep_cache', c_uint, 1),
294 ('flush', c_uint, 1),
295 ('padding', c_uint, 29),
296 ('fh', c_uint64),
297 ('lock_owner', c_uint64)]
298
299 class fuse_context(Structure):
300 _fields_ = [
301 ('fuse', c_voidp),
302 ('uid', c_uid_t),
303 ('gid', c_gid_t),
304 ('pid', c_pid_t),
305 ('private_data', c_voidp)]
306
307 _libfuse.fuse_get_context.restype = POINTER(fuse_context)
308
309
310 class fuse_operations(Structure):
311 _fields_ = [
312 ('getattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat))),
313 ('readlink', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
314 ('getdir', c_voidp), # Deprecated, use readdir
315 ('mknod', CFUNCTYPE(c_int, c_char_p, c_mode_t, c_dev_t)),
316 ('mkdir', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
317 ('unlink', CFUNCTYPE(c_int, c_char_p)),
318 ('rmdir', CFUNCTYPE(c_int, c_char_p)),
319 ('symlink', CFUNCTYPE(c_int, c_char_p, c_char_p)),
320 ('rename', CFUNCTYPE(c_int, c_char_p, c_char_p)),
321 ('link', CFUNCTYPE(c_int, c_char_p, c_char_p)),
322 ('chmod', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
323 ('chown', CFUNCTYPE(c_int, c_char_p, c_uid_t, c_gid_t)),
324 ('truncate', CFUNCTYPE(c_int, c_char_p, c_off_t)),
325 ('utime', c_voidp), # Deprecated, use utimens
326 ('open', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
327
328 ('read', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t,
329 c_off_t, POINTER(fuse_file_info))),
330
331 ('write', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t,
332 c_off_t, POINTER(fuse_file_info))),
333
334 ('statfs', CFUNCTYPE(c_int, c_char_p, POINTER(c_statvfs))),
335 ('flush', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
336 ('release', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
337 ('fsync', CFUNCTYPE(c_int, c_char_p, c_int, POINTER(fuse_file_info))),
338 ('setxattr', setxattr_t),
339 ('getxattr', getxattr_t),
340 ('listxattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
341 ('removexattr', CFUNCTYPE(c_int, c_char_p, c_char_p)),
342 ('opendir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
343
344 ('readdir', CFUNCTYPE(c_int, c_char_p, c_voidp,
345 CFUNCTYPE(c_int, c_voidp, c_char_p,
346 POINTER(c_stat), c_off_t),
347 c_off_t, POINTER(fuse_file_info))),
348
349 ('releasedir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
350
351 ('fsyncdir', CFUNCTYPE(c_int, c_char_p, c_int,
352 POINTER(fuse_file_info))),
353
354 ('init', CFUNCTYPE(c_voidp, c_voidp)),
355 ('destroy', CFUNCTYPE(c_voidp, c_voidp)),
356 ('access', CFUNCTYPE(c_int, c_char_p, c_int)),
357
358 ('create', CFUNCTYPE(c_int, c_char_p, c_mode_t,
359 POINTER(fuse_file_info))),
360
361 ('ftruncate', CFUNCTYPE(c_int, c_char_p, c_off_t,
362 POINTER(fuse_file_info))),
363
364 ('fgetattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat),
365 POINTER(fuse_file_info))),
366
367 ('lock', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info),
368 c_int, c_voidp)),
369
370 ('utimens', CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf))),
371 ('bmap', CFUNCTYPE(c_int, c_char_p, c_size_t, POINTER(c_ulonglong))),
372 ('flag_nullpath_ok', c_uint, 1),
373 ('flag_nopath', c_uint, 1),
374 ('flag_utime_omit_ok', c_uint, 1),
375 ('flag_reserved', c_uint, 29),
376 ]
377
378
379 def time_of_timespec(ts):
380 return ts.tv_sec + ts.tv_nsec / 10 ** 9
381
382 def set_st_attrs(st, attrs):
383 for key, val in attrs.items():
384 if key in ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime'):
385 timespec = getattr(st, key + 'spec', None)
386 if timespec is None:
387 continue
388 timespec.tv_sec = int(val)
389 timespec.tv_nsec = int((val - timespec.tv_sec) * 10 ** 9)
390 elif hasattr(st, key):
391 setattr(st, key, val)
392
393
394 def fuse_get_context():
395 'Returns a (uid, gid, pid) tuple'
396
397 ctxp = _libfuse.fuse_get_context()
398 ctx = ctxp.contents
399 return ctx.uid, ctx.gid, ctx.pid
400
401
402 class FuseOSError(OSError):
403 def __init__(self, errno):
404 super(FuseOSError, self).__init__(errno, strerror(errno))
405
406
407 class FUSE(object):
408 '''
409 This class is the lower level interface and should not be subclassed under
410 normal use. Its methods are called by fuse.
411
412 Assumes API version 2.6 or later.
413 '''
414
415 OPTIONS = (
416 ('foreground', '-f'),
417 ('debug', '-d'),
418 ('nothreads', '-s'),
419 )
420
421 def __init__(self, operations, mountpoint, raw_fi=False, encoding='utf-8',
422 **kwargs):
423
424 '''
425 Setting raw_fi to True will cause FUSE to pass the fuse_file_info
426 class as is to Operations, instead of just the fh field.
427
428 This gives you access to direct_io, keep_cache, etc.
429 '''
430
431 self.operations = operations
432 self.raw_fi = raw_fi
433 self.encoding = encoding
434
435 args = ['fuse']
436
437 args.extend(flag for arg, flag in self.OPTIONS
438 if kwargs.pop(arg, False))
439
440 kwargs.setdefault('fsname', operations.__class__.__name__)
441 args.append('-o')
442 args.append(','.join(self._normalize_fuse_options(**kwargs)))
443 args.append(mountpoint)
444
445 args = [arg.encode(encoding) for arg in args]
446 argv = (c_char_p * len(args))(*args)
447
448 fuse_ops = fuse_operations()
449 for ent in fuse_operations._fields_:
450 name, prototype = ent[:2]
451
452 val = getattr(operations, name, None)
453 if val is None:
454 continue
455
456 # Function pointer members are tested for using the
457 # getattr(operations, name) above but are dynamically
458 # invoked using self.operations(name)
459 if hasattr(prototype, 'argtypes'):
460 val = prototype(partial(self._wrapper, getattr(self, name)))
461
462 setattr(fuse_ops, name, val)
463
464 try:
465 old_handler = signal(SIGINT, SIG_DFL)
466 except ValueError:
467 old_handler = SIG_DFL
468
469 err = _libfuse.fuse_main_real(len(args), argv, pointer(fuse_ops),
470 sizeof(fuse_ops), None)
471
472 try:
473 signal(SIGINT, old_handler)
474 except ValueError:
475 pass
476
477 del self.operations # Invoke the destructor
478 if err:
479 raise RuntimeError(err)
480
481 @staticmethod
482 def _normalize_fuse_options(**kargs):
483 for key, value in kargs.items():
484 if isinstance(value, bool):
485 if value is True: yield key
486 else:
487 yield '%s=%s' % (key, value)
488
489 @staticmethod
490 def _wrapper(func, *args, **kwargs):
491 'Decorator for the methods that follow'
492
493 try:
494 return func(*args, **kwargs) or 0
495 except OSError as e:
496 return -(e.errno or EFAULT)
497 except:
498 print_exc()
499 return -EFAULT
500
501 def _decode_optional_path(self, path):
502 # NB: this method is intended for fuse operations that
503 # allow the path argument to be NULL,
504 # *not* as a generic path decoding method
505 if path is None:
506 return None
507 return path.decode(self.encoding)
508
509 def getattr(self, path, buf):
510 return self.fgetattr(path, buf, None)
511
512 def readlink(self, path, buf, bufsize):
513 ret = self.operations('readlink', path.decode(self.encoding)) \
514 .encode(self.encoding)
515
516 # copies a string into the given buffer
517 # (null terminated and truncated if necessary)
518 data = create_string_buffer(ret[:bufsize - 1])
519 memmove(buf, data, len(data))
520 return 0
521
522 def mknod(self, path, mode, dev):
523 return self.operations('mknod', path.decode(self.encoding), mode, dev)
524
525 def mkdir(self, path, mode):
526 return self.operations('mkdir', path.decode(self.encoding), mode)
527
528 def unlink(self, path):
529 return self.operations('unlink', path.decode(self.encoding))
530
531 def rmdir(self, path):
532 return self.operations('rmdir', path.decode(self.encoding))
533
534 def symlink(self, source, target):
535 'creates a symlink `target -> source` (e.g. ln -s source target)'
536
537 return self.operations('symlink', target.decode(self.encoding),
538 source.decode(self.encoding))
539
540 def rename(self, old, new):
541 return self.operations('rename', old.decode(self.encoding),
542 new.decode(self.encoding))
543
544 def link(self, source, target):
545 'creates a hard link `target -> source` (e.g. ln source target)'
546
547 return self.operations('link', target.decode(self.encoding),
548 source.decode(self.encoding))
549
550 def chmod(self, path, mode):
551 return self.operations('chmod', path.decode(self.encoding), mode)
552
553 def chown(self, path, uid, gid):
554 # Check if any of the arguments is a -1 that has overflowed
555 if c_uid_t(uid + 1).value == 0:
556 uid = -1
557 if c_gid_t(gid + 1).value == 0:
558 gid = -1
559
560 return self.operations('chown', path.decode(self.encoding), uid, gid)
561
562 def truncate(self, path, length):
563 return self.operations('truncate', path.decode(self.encoding), length)
564
565 def open(self, path, fip):
566 fi = fip.contents
567 if self.raw_fi:
568 return self.operations('open', path.decode(self.encoding), fi)
569 else:
570 fi.fh = self.operations('open', path.decode(self.encoding),
571 fi.flags)
572
573 return 0
574
575 def read(self, path, buf, size, offset, fip):
576 if self.raw_fi:
577 fh = fip.contents
578 else:
579 fh = fip.contents.fh
580
581 ret = self.operations('read', self._decode_optional_path(path), size,
582 offset, fh)
583
584 if not ret: return 0
585
586 retsize = len(ret)
587 assert retsize <= size, \
588 'actual amount read %d greater than expected %d' % (retsize, size)
589
590 data = create_string_buffer(ret, retsize)
591 memmove(buf, data, retsize)
592 return retsize
593
594 def write(self, path, buf, size, offset, fip):
595 data = string_at(buf, size)
596
597 if self.raw_fi:
598 fh = fip.contents
599 else:
600 fh = fip.contents.fh
601
602 return self.operations('write', self._decode_optional_path(path), data,
603 offset, fh)
604
605 def statfs(self, path, buf):
606 stv = buf.contents
607 attrs = self.operations('statfs', path.decode(self.encoding))
608 for key, val in attrs.items():
609 if hasattr(stv, key):
610 setattr(stv, key, val)
611
612 return 0
613
614 def flush(self, path, fip):
615 if self.raw_fi:
616 fh = fip.contents
617 else:
618 fh = fip.contents.fh
619
620 return self.operations('flush', self._decode_optional_path(path), fh)
621
622 def release(self, path, fip):
623 if self.raw_fi:
624 fh = fip.contents
625 else:
626 fh = fip.contents.fh
627
628 return self.operations('release', self._decode_optional_path(path), fh)
629
630 def fsync(self, path, datasync, fip):
631 if self.raw_fi:
632 fh = fip.contents
633 else:
634 fh = fip.contents.fh
635
636 return self.operations('fsync', self._decode_optional_path(path), datasync,
637 fh)
638
639 def setxattr(self, path, name, value, size, options, *args):
640 return self.operations('setxattr', path.decode(self.encoding),
641 name.decode(self.encoding),
642 string_at(value, size), options, *args)
643
644 def getxattr(self, path, name, value, size, *args):
645 ret = self.operations('getxattr', path.decode(self.encoding),
646 name.decode(self.encoding), *args)
647
648 retsize = len(ret)
649 # allow size queries
650 if not value: return retsize
651
652 # do not truncate
653 if retsize > size: return -ERANGE
654
655 buf = create_string_buffer(ret, retsize) # Does not add trailing 0
656 memmove(value, buf, retsize)
657
658 return retsize
659
660 def listxattr(self, path, namebuf, size):
661 attrs = self.operations('listxattr', path.decode(self.encoding)) or ''
662 ret = '\x00'.join(attrs).encode(self.encoding)
663 if len(ret) > 0:
664 ret += '\x00'.encode(self.encoding)
665
666 retsize = len(ret)
667 # allow size queries
668 if not namebuf: return retsize
669
670 # do not truncate
671 if retsize > size: return -ERANGE
672
673 buf = create_string_buffer(ret, retsize)
674 memmove(namebuf, buf, retsize)
675
676 return retsize
677
678 def removexattr(self, path, name):
679 return self.operations('removexattr', path.decode(self.encoding),
680 name.decode(self.encoding))
681
682 def opendir(self, path, fip):
683 # Ignore raw_fi
684 fip.contents.fh = self.operations('opendir',
685 path.decode(self.encoding))
686
687 return 0
688
689 def readdir(self, path, buf, filler, offset, fip):
690 # Ignore raw_fi
691 for item in self.operations('readdir', self._decode_optional_path(path),
692 fip.contents.fh):
693
694 if isinstance(item, basestring):
695 name, st, offset = item, None, 0
696 else:
697 name, attrs, offset = item
698 if attrs:
699 st = c_stat()
700 set_st_attrs(st, attrs)
701 else:
702 st = None
703
704 if filler(buf, name.encode(self.encoding), st, offset) != 0:
705 break
706
707 return 0
708
709 def releasedir(self, path, fip):
710 # Ignore raw_fi
711 return self.operations('releasedir', self._decode_optional_path(path),
712 fip.contents.fh)
713
714 def fsyncdir(self, path, datasync, fip):
715 # Ignore raw_fi
716 return self.operations('fsyncdir', self._decode_optional_path(path),
717 datasync, fip.contents.fh)
718
719 def init(self, conn):
720 return self.operations('init', '/')
721
722 def destroy(self, private_data):
723 return self.operations('destroy', '/')
724
725 def access(self, path, amode):
726 return self.operations('access', path.decode(self.encoding), amode)
727
728 def create(self, path, mode, fip):
729 fi = fip.contents
730 path = path.decode(self.encoding)
731
732 if self.raw_fi:
733 return self.operations('create', path, mode, fi)
734 else:
735 fi.fh = self.operations('create', path, mode)
736 return 0
737
738 def ftruncate(self, path, length, fip):
739 if self.raw_fi:
740 fh = fip.contents
741 else:
742 fh = fip.contents.fh
743
744 return self.operations('truncate', self._decode_optional_path(path),
745 length, fh)
746
747 def fgetattr(self, path, buf, fip):
748 memset(buf, 0, sizeof(c_stat))
749
750 st = buf.contents
751 if not fip:
752 fh = fip
753 elif self.raw_fi:
754 fh = fip.contents
755 else:
756 fh = fip.contents.fh
757
758 attrs = self.operations('getattr', self._decode_optional_path(path), fh)
759 set_st_attrs(st, attrs)
760 return 0
761
762 def lock(self, path, fip, cmd, lock):
763 if self.raw_fi:
764 fh = fip.contents
765 else:
766 fh = fip.contents.fh
767
768 return self.operations('lock', self._decode_optional_path(path), fh, cmd,
769 lock)
770
771 def utimens(self, path, buf):
772 if buf:
773 atime = time_of_timespec(buf.contents.actime)
774 mtime = time_of_timespec(buf.contents.modtime)
775 times = (atime, mtime)
776 else:
777 times = None
778
779 return self.operations('utimens', path.decode(self.encoding), times)
780
781 def bmap(self, path, blocksize, idx):
782 return self.operations('bmap', path.decode(self.encoding), blocksize,
783 idx)
784
785
786 class Operations(object):
787 '''
788 This class should be subclassed and passed as an argument to FUSE on
789 initialization. All operations should raise a FuseOSError exception on
790 error.
791
792 When in doubt of what an operation should do, check the FUSE header file
793 or the corresponding system call man page.
794 '''
795
796 def __call__(self, op, *args):
797 if not hasattr(self, op):
798 raise FuseOSError(EFAULT)
799 return getattr(self, op)(*args)
800
801 def access(self, path, amode):
802 return 0
803
804 bmap = None
805
806 def chmod(self, path, mode):
807 raise FuseOSError(EROFS)
808
809 def chown(self, path, uid, gid):
810 raise FuseOSError(EROFS)
811
812 def create(self, path, mode, fi=None):
813 '''
814 When raw_fi is False (default case), fi is None and create should
815 return a numerical file handle.
816
817 When raw_fi is True the file handle should be set directly by create
818 and return 0.
819 '''
820
821 raise FuseOSError(EROFS)
822
823 def destroy(self, path):
824 'Called on filesystem destruction. Path is always /'
825
826 pass
827
828 def flush(self, path, fh):
829 return 0
830
831 def fsync(self, path, datasync, fh):
832 return 0
833
834 def fsyncdir(self, path, datasync, fh):
835 return 0
836
837 def getattr(self, path, fh=None):
838 '''
839 Returns a dictionary with keys identical to the stat C structure of
840 stat(2).
841
842 st_atime, st_mtime and st_ctime should be floats.
843
844 NOTE: There is an incombatibility between Linux and Mac OS X
845 concerning st_nlink of directories. Mac OS X counts all files inside
846 the directory, while Linux counts only the subdirectories.
847 '''
848
849 if path != '/':
850 raise FuseOSError(ENOENT)
851 return dict(st_mode=(S_IFDIR | 0o755), st_nlink=2)
852
853 def getxattr(self, path, name, position=0):
854 raise FuseOSError(ENOTSUP)
855
856 def init(self, path):
857 '''
858 Called on filesystem initialization. (Path is always /)
859
860 Use it instead of __init__ if you start threads on initialization.
861 '''
862
863 pass
864
865 def link(self, target, source):
866 'creates a hard link `target -> source` (e.g. ln source target)'
867
868 raise FuseOSError(EROFS)
869
870 def listxattr(self, path):
871 return []
872
873 lock = None
874
875 def mkdir(self, path, mode):
876 raise FuseOSError(EROFS)
877
878 def mknod(self, path, mode, dev):
879 raise FuseOSError(EROFS)
880
881 def open(self, path, flags):
882 '''
883 When raw_fi is False (default case), open should return a numerical
884 file handle.
885
886 When raw_fi is True the signature of open becomes:
887 open(self, path, fi)
888
889 and the file handle should be set directly.
890 '''
891
892 return 0
893
894 def opendir(self, path):
895 'Returns a numerical file handle.'
896
897 return 0
898
899 def read(self, path, size, offset, fh):
900 'Returns a string containing the data requested.'
901
902 raise FuseOSError(EIO)
903
904 def readdir(self, path, fh):
905 '''
906 Can return either a list of names, or a list of (name, attrs, offset)
907 tuples. attrs is a dict as in getattr.
908 '''
909
910 return ['.', '..']
911
912 def readlink(self, path):
913 raise FuseOSError(ENOENT)
914
915 def release(self, path, fh):
916 return 0
917
918 def releasedir(self, path, fh):
919 return 0
920
921 def removexattr(self, path, name):
922 raise FuseOSError(ENOTSUP)
923
924 def rename(self, old, new):
925 raise FuseOSError(EROFS)
926
927 def rmdir(self, path):
928 raise FuseOSError(EROFS)
929
930 def setxattr(self, path, name, value, options, position=0):
931 raise FuseOSError(ENOTSUP)
932
933 def statfs(self, path):
934 '''
935 Returns a dictionary with keys identical to the statvfs C structure of
936 statvfs(3).
937
938 On Mac OS X f_bsize and f_frsize must be a power of 2
939 (minimum 512).
940 '''
941
942 return {}
943
944 def symlink(self, target, source):
945 'creates a symlink `target -> source` (e.g. ln -s source target)'
946
947 raise FuseOSError(EROFS)
948
949 def truncate(self, path, length, fh=None):
950 raise FuseOSError(EROFS)
951
952 def unlink(self, path):
953 raise FuseOSError(EROFS)
954
955 def utimens(self, path, times=None):
956 'Times is a (atime, mtime) tuple. If None use current time.'
957
958 return 0
959
960 def write(self, path, data, offset, fh):
961 raise FuseOSError(EROFS)
962
963
964 class LoggingMixIn:
965 log = logging.getLogger('fuse.log-mixin')
966
967 def __call__(self, op, path, *args):
968 self.log.debug('-> %s %s %s', op, path, repr(args))
969 ret = '[Unhandled Exception]'
970 try:
971 ret = getattr(self, op)(path, *args)
972 return ret
973 except OSError as e:
974 ret = str(e)
975 raise
976 finally:
977 self.log.debug('<- %s %s', op, repr(ret))
0 Metadata-Version: 1.1
1 Name: fusepy
2 Version: 2.0.4
3 Summary: Simple ctypes bindings for FUSE
4 Home-page: http://github.com/terencehonles/fusepy
5 Author: Terence Honles
6 Author-email: terence@honles.com
7 License: ISC
8 Description: fusepy
9 ======
10
11 ``fusepy`` is a Python module that provides a simple interface to FUSE_ and
12 MacFUSE_. It's just one file and is implemented using ctypes.
13
14 The original version of ``fusepy`` was hosted on `Google Code`_, but is now
15 `officially hosted on GitHub`_.
16
17 ``fusepy`` is written in 2x syntax, but trying to pay attention to bytes and
18 other changes 3x would care about.
19
20 examples
21 --------
22 See some examples of how you can use fusepy:
23
24 :memory_: A simple memory filesystem
25 :loopback_: A loopback filesystem
26 :context_: Sample usage of fuse_get_context()
27 :sftp_: A simple SFTP filesystem (requires paramiko)
28
29 To get started download_ fusepy or just browse the source_.
30
31 fusepy requires FUSE 2.6 (or later) and runs on:
32
33 - Linux (i386, x86_64, PPC, arm64, MIPS)
34 - Mac OS X (Intel, PowerPC)
35 - FreeBSD (i386, amd64)
36
37
38 .. _FUSE: http://fuse.sourceforge.net/
39 .. _MacFUSE: http://code.google.com/p/macfuse/
40 .. _`Google Code`: http://code.google.com/p/fusepy/
41
42 .. _officially hosted on GitHub: source_
43 .. _download: https://github.com/terencehonles/fusepy/zipball/master
44 .. _source: http://github.com/terencehonles/fusepy
45
46 .. examples
47 .. _memory: http://github.com/terencehonles/fusepy/blob/master/examples/memory.py
48 .. _loopback: http://github.com/terencehonles/fusepy/blob/master/examples/loopback.py
49 .. _context: http://github.com/terencehonles/fusepy/blob/master/examples/context.py
50 .. _sftp: http://github.com/terencehonles/fusepy/blob/master/examples/sftp.py
51
52 Platform: UNKNOWN
53 Classifier: Intended Audience :: Developers
54 Classifier: License :: OSI Approved :: ISC License (ISCL)
55 Classifier: Operating System :: MacOS
56 Classifier: Operating System :: POSIX
57 Classifier: Operating System :: Unix
58 Classifier: Programming Language :: Python :: 2.6
59 Classifier: Programming Language :: Python :: 3
60 Classifier: Topic :: System :: Filesystems
0 MANIFEST.in
1 README
2 README.rst
3 fuse.py
4 setup.py
5 fusepy.egg-info/PKG-INFO
6 fusepy.egg-info/SOURCES.txt
7 fusepy.egg-info/dependency_links.txt
8 fusepy.egg-info/top_level.txt
0 [egg_info]
1 tag_build =
2 tag_date = 0
3 tag_svn_revision = 0
4
0 #!/usr/bin/env python
1
2 from __future__ import with_statement
3
4 from setuptools import setup
5
6 with open('README') as readme:
7 documentation = readme.read()
8
9 setup(
10 name = 'fusepy',
11 version = '2.0.4',
12
13 description = 'Simple ctypes bindings for FUSE',
14 long_description = documentation,
15 author = 'Giorgos Verigakis',
16 author_email = 'verigak@gmail.com',
17 maintainer = 'Terence Honles',
18 maintainer_email = 'terence@honles.com',
19 license = 'ISC',
20 py_modules=['fuse'],
21 url = 'http://github.com/terencehonles/fusepy',
22
23 classifiers = [
24 'Intended Audience :: Developers',
25 'License :: OSI Approved :: ISC License (ISCL)',
26 'Operating System :: MacOS',
27 'Operating System :: POSIX',
28 'Operating System :: Unix',
29 'Programming Language :: Python :: 2.6',
30 'Programming Language :: Python :: 3',
31 'Topic :: System :: Filesystems',
32 ]
33 )