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