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

     1  # Copyright (c) 2010 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, wraps
    21  from inspect import getmembers, ismethod
    22  from platform import machine, system
    23  from stat import S_IFDIR, S_IFREG
    24  
    25  
    26  _system = system()
    27  _machine = machine()
    28  
    29  class LibFUSE(CDLL):
    30      def __init__(self):
    31          if _system == 'Darwin':
    32              self.libiconv = CDLL(find_library('iconv'), RTLD_GLOBAL)
    33          super(LibFUSE, self).__init__(find_library('fuse'))
    34          
    35          self.fuse_mount.argtypes = (c_char_p, POINTER(fuse_args))
    36          self.fuse_mount.restype = c_void_p
    37          self.fuse_lowlevel_new.argtypes = (POINTER(fuse_args), POINTER(fuse_lowlevel_ops),
    38                                              c_size_t, c_void_p)
    39          self.fuse_lowlevel_new.restype = c_void_p
    40          self.fuse_set_signal_handlers.argtypes = (c_void_p,)
    41          self.fuse_session_add_chan.argtypes = (c_void_p, c_void_p)
    42          self.fuse_session_loop.argtypes = (c_void_p,)
    43          self.fuse_remove_signal_handlers.argtypes = (c_void_p,)
    44          self.fuse_session_remove_chan.argtypes = (c_void_p,)
    45          self.fuse_session_destroy.argtypes = (c_void_p,)
    46          self.fuse_unmount.argtypes = (c_char_p, c_void_p)
    47          
    48          self.fuse_req_ctx.restype = POINTER(fuse_ctx)
    49          self.fuse_req_ctx.argtypes = (fuse_req_t,)
    50          
    51          self.fuse_reply_err.argtypes = (fuse_req_t, c_int)
    52          self.fuse_reply_attr.argtypes = (fuse_req_t, c_void_p, c_double)
    53          self.fuse_reply_entry.argtypes = (fuse_req_t, c_void_p)
    54          self.fuse_reply_open.argtypes = (fuse_req_t, c_void_p)
    55          self.fuse_reply_buf.argtypes = (fuse_req_t, c_char_p, c_size_t)
    56          self.fuse_reply_write.argtypes = (fuse_req_t, c_size_t)
    57          
    58          self.fuse_add_direntry.argtypes = (c_void_p, c_char_p, c_size_t, c_char_p,
    59                                              c_stat_p, c_off_t)
    60  
    61  class fuse_args(Structure):
    62      _fields_ = [('argc', c_int), ('argv', POINTER(c_char_p)), ('allocated', c_int)]
    63  
    64  class c_timespec(Structure):
    65      _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]
    66  
    67  class c_stat(Structure):
    68      pass    # Platform dependent
    69  
    70  if _system == 'Darwin':
    71      ENOTSUP = 45
    72      c_dev_t = c_int32
    73      c_fsblkcnt_t = c_ulong
    74      c_fsfilcnt_t = c_ulong
    75      c_gid_t = c_uint32
    76      c_mode_t = c_uint16
    77      c_off_t = c_int64
    78      c_pid_t = c_int32
    79      c_uid_t = c_uint32
    80      c_stat._fields_ = [
    81          ('st_dev', c_dev_t),
    82          ('st_ino', c_uint32),
    83          ('st_mode', c_mode_t),
    84          ('st_nlink', c_uint16),
    85          ('st_uid', c_uid_t),
    86          ('st_gid', c_gid_t),
    87          ('st_rdev', c_dev_t),
    88          ('st_atimespec', c_timespec),
    89          ('st_mtimespec', c_timespec),
    90          ('st_ctimespec', c_timespec),
    91          ('st_size', c_off_t),
    92          ('st_blocks', c_int64),
    93          ('st_blksize', c_int32)]
    94  elif _system == 'Linux':
    95      ENOTSUP = 95
    96      c_dev_t = c_ulonglong
    97      c_fsblkcnt_t = c_ulonglong
    98      c_fsfilcnt_t = c_ulonglong
    99      c_gid_t = c_uint
   100      c_mode_t = c_uint
   101      c_off_t = c_longlong
   102      c_pid_t = c_int
   103      c_uid_t = c_uint
   104      
   105      if _machine == 'x86_64':
   106          c_stat._fields_ = [
   107              ('st_dev', c_dev_t),
   108              ('st_ino', c_ulong),
   109              ('st_nlink', c_ulong),
   110              ('st_mode', c_mode_t),
   111              ('st_uid', c_uid_t),
   112              ('st_gid', c_gid_t),
   113              ('__pad0', c_int),
   114              ('st_rdev', c_dev_t),
   115              ('st_size', c_off_t),
   116              ('st_blksize', c_long),
   117              ('st_blocks', c_long),
   118              ('st_atimespec', c_timespec),
   119              ('st_mtimespec', c_timespec),
   120              ('st_ctimespec', c_timespec)]
   121      elif _machine == 'ppc':
   122          c_stat._fields_ = [
   123              ('st_dev', c_dev_t),
   124              ('st_ino', c_ulonglong),
   125              ('st_mode', c_mode_t),
   126              ('st_nlink', c_uint),
   127              ('st_uid', c_uid_t),
   128              ('st_gid', c_gid_t),
   129              ('st_rdev', c_dev_t),
   130              ('__pad2', c_ushort),
   131              ('st_size', c_off_t),
   132              ('st_blksize', c_long),
   133              ('st_blocks', c_longlong),
   134              ('st_atimespec', c_timespec),
   135              ('st_mtimespec', c_timespec),
   136              ('st_ctimespec', c_timespec)]
   137      else:
   138          # i686, use as fallback for everything else
   139          c_stat._fields_ = [
   140              ('st_dev', c_dev_t),
   141              ('__pad1', c_ushort),
   142              ('__st_ino', c_ulong),
   143              ('st_mode', c_mode_t),
   144              ('st_nlink', c_uint),
   145              ('st_uid', c_uid_t),
   146              ('st_gid', c_gid_t),
   147              ('st_rdev', c_dev_t),
   148              ('__pad2', c_ushort),
   149              ('st_size', c_off_t),
   150              ('st_blksize', c_long),
   151              ('st_blocks', c_longlong),
   152              ('st_atimespec', c_timespec),
   153              ('st_mtimespec', c_timespec),
   154              ('st_ctimespec', c_timespec),
   155              ('st_ino', c_ulonglong)]
   156  else:
   157      raise NotImplementedError('%s is not supported.' % _system)
   158  
   159  class c_statvfs(Structure):
   160      _fields_ = [
   161          ('f_bsize', c_ulong),
   162          ('f_frsize', c_ulong),
   163          ('f_blocks', c_fsblkcnt_t),
   164          ('f_bfree', c_fsblkcnt_t),
   165          ('f_bavail', c_fsblkcnt_t),
   166          ('f_files', c_fsfilcnt_t),
   167          ('f_ffree', c_fsfilcnt_t),
   168          ('f_favail', c_fsfilcnt_t)]
   169  
   170  class fuse_file_info(Structure):
   171      _fields_ = [
   172          ('flags', c_int),
   173          ('fh_old', c_ulong),
   174          ('writepage', c_int),
   175          ('direct_io', c_uint, 1),
   176          ('keep_cache', c_uint, 1),
   177          ('flush', c_uint, 1),
   178          ('padding', c_uint, 29),
   179          ('fh', c_uint64),
   180          ('lock_owner', c_uint64)]
   181  
   182  class fuse_ctx(Structure):
   183      _fields_ = [('uid', c_uid_t), ('gid', c_gid_t), ('pid', c_pid_t)]
   184  
   185  fuse_ino_t = c_ulong
   186  fuse_req_t = c_void_p
   187  c_stat_p = POINTER(c_stat)
   188  fuse_file_info_p = POINTER(fuse_file_info)
   189  
   190  FUSE_SET_ATTR = ('st_mode', 'st_uid', 'st_gid', 'st_size', 'st_atime', 'st_mtime')
   191  
   192  class fuse_entry_param(Structure):
   193      _fields_ = [
   194          ('ino', fuse_ino_t),
   195          ('generation', c_ulong),
   196          ('attr', c_stat),
   197          ('attr_timeout', c_double),
   198          ('entry_timeout', c_double)]
   199  
   200  class fuse_lowlevel_ops(Structure):
   201      _fields_ = [
   202          ('init', CFUNCTYPE(None, c_void_p, c_void_p)),
   203          ('destroy', CFUNCTYPE(None, c_void_p)),
   204          ('lookup', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p)),
   205          ('forget', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_ulong)),
   206          ('getattr', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
   207          ('setattr', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_stat_p, c_int, fuse_file_info_p)),
   208          ('readlink', CFUNCTYPE(None, fuse_req_t, fuse_ino_t)),
   209          ('mknod', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, c_mode_t, c_dev_t)),
   210          ('mkdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, c_mode_t)),
   211          ('unlink', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p)),
   212          ('rmdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p)),
   213          ('symlink', CFUNCTYPE(None, fuse_req_t, c_char_p, fuse_ino_t, c_char_p)),
   214          ('rename', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, fuse_ino_t, c_char_p)),
   215          ('link', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_ino_t, c_char_p)),
   216          ('open', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
   217          ('read', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_size_t, c_off_t, fuse_file_info_p)),
   218          ('write', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, c_size_t, c_off_t,
   219                                  fuse_file_info_p)),
   220          ('flush', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
   221          ('release', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
   222          ('fsync', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_int, fuse_file_info_p)),
   223          ('opendir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
   224          ('readdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_size_t, c_off_t, fuse_file_info_p)),
   225          ('releasedir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
   226          ('fsyncdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_int, fuse_file_info_p))]
   227  
   228  
   229  def struct_to_dict(p):
   230      try:
   231          x = p.contents
   232          return dict((key, getattr(x, key)) for key, type in x._fields_)
   233      except ValueError:
   234          return {}
   235  
   236  def stat_to_dict(p):
   237      try:
   238          d = {}
   239          x = p.contents
   240          for key, type in x._fields_:
   241              if key in ('st_atimespec', 'st_mtimespec', 'st_ctimespec'):
   242                  ts = getattr(x, key)
   243                  key = key[:-4]      # Lose the "spec"
   244                  d[key] = ts.tv_sec + ts.tv_nsec / 10 ** 9
   245              else:
   246                  d[key] = getattr(x, key)
   247          return d
   248      except ValueError:
   249          return {}
   250  
   251  def dict_to_stat(d):
   252      for key in ('st_atime', 'st_mtime', 'st_ctime'):
   253          if key in d:
   254              val = d[key]
   255              sec = int(val)
   256              nsec = int((val - sec) * 10 ** 9)
   257              d[key + 'spec'] = c_timespec(sec, nsec)
   258      return c_stat(**d)
   259  
   260  def setattr_mask_to_list(mask):
   261      return [FUSE_SET_ATTR[i] for i in range(len(FUSE_SET_ATTR)) if mask & (1 << i)]
   262  
   263  class FUSELL(object):
   264      def __init__(self, mountpoint):
   265          self.libfuse = LibFUSE()       
   266          
   267          fuse_ops = fuse_lowlevel_ops()
   268          
   269          for name, prototype in fuse_lowlevel_ops._fields_:
   270              method = getattr(self, 'fuse_' + name, None) or getattr(self, name, None)
   271              if method:
   272                  setattr(fuse_ops, name, prototype(method))
   273          
   274          args = ['fuse']
   275          argv = fuse_args(len(args), (c_char_p * len(args))(*args), 0)
   276          
   277          # TODO: handle initialization errors
   278          
   279          chan = self.libfuse.fuse_mount(mountpoint, argv)
   280          assert chan
   281          
   282          session = self.libfuse.fuse_lowlevel_new(argv, byref(fuse_ops), sizeof(fuse_ops), None)
   283          assert session
   284          
   285          err = self.libfuse.fuse_set_signal_handlers(session)
   286          assert err == 0
   287          
   288          self.libfuse.fuse_session_add_chan(session, chan)
   289          
   290          err = self.libfuse.fuse_session_loop(session)
   291          assert err == 0
   292          
   293          err = self.libfuse.fuse_remove_signal_handlers(session)
   294          assert err == 0
   295          
   296          self.libfuse.fuse_session_remove_chan(chan)
   297          self.libfuse.fuse_session_destroy(session)
   298          self.libfuse.fuse_unmount(mountpoint, chan)
   299      
   300      def reply_err(self, req, err):
   301          return self.libfuse.fuse_reply_err(req, err)
   302      
   303      def reply_none(self, req):
   304          self.libfuse.fuse_reply_none(req)
   305      
   306      def reply_entry(self, req, entry):
   307          entry['attr'] = c_stat(**entry['attr'])
   308          e = fuse_entry_param(**entry)
   309          self.libfuse.fuse_reply_entry(req, byref(e))
   310      
   311      def reply_create(self, req, *args):
   312          pass    # XXX
   313      
   314      def reply_attr(self, req, attr, attr_timeout):
   315          st = dict_to_stat(attr)
   316          return self.libfuse.fuse_reply_attr(req, byref(st), c_double(attr_timeout))
   317      
   318      def reply_readlink(self, req, *args):
   319          pass    # XXX
   320      
   321      def reply_open(self, req, d):
   322          fi = fuse_file_info(**d)
   323          return self.libfuse.fuse_reply_open(req, byref(fi))
   324      
   325      def reply_write(self, req, count):
   326          return self.libfuse.fuse_reply_write(req, count)
   327      
   328      def reply_buf(self, req, buf):
   329          return self.libfuse.fuse_reply_buf(req, buf, len(buf))
   330      
   331      def reply_readdir(self, req, size, off, entries):
   332          bufsize = 0
   333          sized_entries = []
   334          for name, attr in entries:
   335              entsize = self.libfuse.fuse_add_direntry(req, None, 0, name, None, 0)
   336              sized_entries.append((name, attr, entsize))
   337              bufsize += entsize
   338  
   339          next = 0
   340          buf = create_string_buffer(bufsize)
   341          for name, attr, entsize in sized_entries:
   342              entbuf = cast(addressof(buf) + next, c_char_p)
   343              st = c_stat(**attr)
   344              next += entsize
   345              self.libfuse.fuse_add_direntry(req, entbuf, entsize, name, byref(st), next)
   346  
   347          if off < bufsize:
   348              buf = cast(addressof(buf) + off, c_char_p) if off else buf
   349              return self.libfuse.fuse_reply_buf(req, buf, min(bufsize - off, size))
   350          else:
   351              return self.libfuse.fuse_reply_buf(req, None, 0)
   352      
   353      
   354      # If you override the following methods you should reply directly
   355      # with the self.libfuse.fuse_reply_* methods.
   356      
   357      def fuse_getattr(self, req, ino, fi):
   358          self.getattr(req, ino, struct_to_dict(fi))
   359      
   360      def fuse_setattr(self, req, ino, attr, to_set, fi):
   361          attr_dict = stat_to_dict(attr)
   362          to_set_list = setattr_mask_to_list(to_set)
   363          fi_dict = struct_to_dict(fi)
   364          self.setattr(req, ino, attr_dict, to_set_list, fi_dict)
   365          
   366      def fuse_open(self, req, ino, fi):
   367          self.open(req, ino, struct_to_dict(fi))
   368      
   369      def fuse_read(self, req, ino, size, off, fi):
   370          self.read(req, ino, size, off, fi)
   371      
   372      def fuse_write(self, req, ino, buf, size, off, fi):
   373          buf_str = string_at(buf, size)
   374          fi_dict = struct_to_dict(fi)
   375          self.write(req, ino, buf_str, off, fi_dict)
   376  
   377      def fuse_flush(self, req, ino, fi):
   378          self.flush(req, ino, struct_to_dict(fi))
   379      
   380      def fuse_release(self, req, ino, fi):
   381          self.release(req, ino, struct_to_dict(fi))
   382      
   383      def fuse_fsync(self, req, ino, datasync, fi):
   384          self.fsyncdir(req, ino, datasync, struct_to_dict(fi))
   385      
   386      def fuse_opendir(self, req, ino, fi):
   387          self.opendir(req, ino, struct_to_dict(fi))
   388      
   389      def fuse_readdir(self, req, ino, size, off, fi):
   390          self.readdir(req, ino, size, off, struct_to_dict(fi))
   391      
   392      def fuse_releasedir(self, req, ino, fi):
   393          self.releasedir(req, ino, struct_to_dict(fi))
   394      
   395      def fuse_fsyncdir(self, req, ino, datasync, fi):
   396          self.fsyncdir(req, ino, datasync, struct_to_dict(fi))
   397      
   398      
   399      # Utility methods
   400      
   401      def req_ctx(self, req):
   402          ctx = self.libfuse.fuse_req_ctx(req)
   403          return struct_to_dict(ctx)
   404      
   405      
   406      # Methods to be overridden in subclasses.
   407      # Reply with the self.reply_* methods.
   408      
   409      def init(self, userdata, conn):
   410          """Initialize filesystem
   411          
   412          There's no reply to this method
   413          """
   414          pass
   415  
   416      def destroy(self, userdata):
   417          """Clean up filesystem
   418          
   419          There's no reply to this method
   420          """
   421          pass
   422  
   423      def lookup(self, req, parent, name):
   424          """Look up a directory entry by name and get its attributes.
   425          
   426          Valid replies:
   427              reply_entry
   428              reply_err
   429          """
   430          self.reply_err(req, ENOENT)
   431      
   432      def forget(self, req, ino, nlookup):
   433          """Forget about an inode
   434          
   435          Valid replies:
   436              reply_none
   437          """
   438          self.reply_none(req)
   439  
   440      def getattr(self, req, ino, fi):
   441          """Get file attributes
   442          
   443          Valid replies:
   444              reply_attr
   445              reply_err
   446          """
   447          if ino == 1:
   448              attr = {'st_ino': 1, 'st_mode': S_IFDIR | 0755, 'st_nlink': 2}
   449              self.reply_attr(req, attr, 1.0)
   450          else:
   451              self.reply_err(req, ENOENT)        
   452      
   453      def setattr(self, req, ino, attr, to_set, fi):
   454          """Set file attributes
   455          
   456          Valid replies:
   457              reply_attr
   458              reply_err
   459          """
   460          self.reply_err(req, EROFS)
   461          
   462      def readlink(self, req, ino):
   463          """Read symbolic link
   464          
   465          Valid replies:
   466              reply_readlink
   467              reply_err
   468          """
   469          self.reply_err(req, ENOENT)
   470      
   471      def mknod(self, req, parent, name, mode, rdev):
   472          """Create file node
   473          
   474          Valid replies:
   475              reply_entry
   476              reply_err
   477          """
   478          self.reply_err(req, EROFS)
   479      
   480      def mkdir(self, req, parent, name, mode):
   481          """Create a directory
   482          
   483          Valid replies:
   484              reply_entry
   485              reply_err
   486          """
   487          self.reply_err(req, EROFS)
   488  
   489      def unlink(self, req, parent, name):
   490          """Remove a file
   491          
   492          Valid replies:
   493              reply_err
   494          """
   495          self.reply_err(req, EROFS)
   496      
   497      def rmdir(self, req, parent, name):
   498          """Remove a directory
   499          
   500          Valid replies:
   501              reply_err
   502          """
   503          self.reply_err(req, EROFS)
   504      
   505      def symlink(self, req, link, parent, name):
   506          """Create a symbolic link
   507          
   508          Valid replies:
   509              reply_entry
   510              reply_err
   511          """
   512          self.reply_err(req, EROFS)
   513      
   514      def rename(self, req, parent, name, newparent, newname):
   515          """Rename a file
   516          
   517          Valid replies:
   518              reply_err
   519          """
   520          self.reply_err(req, EROFS)
   521      
   522      def link(self, req, ino, newparent, newname):
   523          """Create a hard link
   524          
   525          Valid replies:
   526              reply_entry
   527              reply_err
   528          """
   529          self.reply_err(req, EROFS)
   530      
   531      def open(self, req, ino, fi):
   532          """Open a file
   533          
   534          Valid replies:
   535              reply_open
   536              reply_err
   537          """
   538          self.reply_open(req, fi)
   539      
   540      def read(self, req, ino, size, off, fi):
   541          """Read data
   542          
   543          Valid replies:
   544              reply_buf
   545              reply_err
   546          """
   547          self.reply_err(req, EIO)
   548          
   549      def write(self, req, ino, buf, off, fi):
   550          """Write data
   551          
   552          Valid replies:
   553              reply_write
   554              reply_err
   555          """
   556          self.reply_err(req, EROFS)
   557      
   558      def flush(self, req, ino, fi):
   559          """Flush method
   560          
   561          Valid replies:
   562              reply_err
   563          """
   564          self.reply_err(req, 0)
   565      
   566      def release(self, req, ino, fi):
   567          """Release an open file
   568          
   569          Valid replies:
   570              reply_err
   571          """
   572          self.reply_err(req, 0)
   573      
   574      def fsync(self, req, ino, datasync, fi):
   575          """Synchronize file contents
   576          
   577          Valid replies:
   578              reply_err
   579          """
   580          self.reply_err(req, 0)
   581  
   582      def opendir(self, req, ino, fi):
   583          """Open a directory
   584          
   585          Valid replies:
   586              reply_open
   587              reply_err
   588          """
   589          self.reply_open(req, fi)
   590      
   591      def readdir(self, req, ino, size, off, fi):
   592          """Read directory
   593          
   594          Valid replies:
   595              reply_readdir
   596              reply_err
   597          """
   598          if ino == 1:
   599              attr = {'st_ino': 1, 'st_mode': S_IFDIR}
   600              entries = [('.', attr), ('..', attr)]
   601              self.reply_readdir(req, size, off, entries)
   602          else:
   603              self.reply_err(req, ENOENT)
   604      
   605      def releasedir(self, req, ino, fi):
   606          """Release an open directory
   607          
   608          Valid replies:
   609              reply_err
   610          """
   611          self.reply_err(req, 0)
   612  
   613      def fsyncdir(self, req, ino, datasync, fi):
   614          """Synchronize directory contents
   615          
   616          Valid replies:
   617              reply_err
   618          """
   619          self.reply_err(req, 0)