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