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)