github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/lib/python/fusepy/fuse.py (about)

     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):
   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)