Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-tools / pithos / tools / lib / fuse.py @ 6e147ecc

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)