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