1''' 2misc.pxi 3 4This file defines various functions that are used internally by 5LLFUSE. It is included by llfuse.pyx. 6 7Copyright © 2013 Nikolaus Rath <Nikolaus.org> 8 9This file is part of Python-LLFUSE. This work may be distributed under 10the terms of the GNU LGPL. 11''' 12 13cdef int handle_exc(fuse_req_t req): 14 '''Try to call fuse_reply_err and terminate main loop''' 15 16 cdef int res 17 global exc_info 18 19 res = pthread_mutex_lock(&exc_info_mutex) 20 if res != 0: 21 log.error('pthread_mutex_lock failed with %s', 22 strerror(res)) 23 if not exc_info: 24 exc_info = sys.exc_info() 25 log.info('handler raised %s exception (%s), terminating main loop.', 26 exc_info[0], exc_info[1]) 27 fuse_session_exit(session) 28 else: 29 log.exception('Only one exception can be re-raised in `llfuse.main`, ' 30 'the following exception will be lost') 31 32 pthread_mutex_unlock(&exc_info_mutex) 33 if res != 0: 34 log.error('pthread_mutex_ulock failed with %s', 35 strerror(res)) 36 37 if req is NULL: 38 return 0 39 else: 40 return fuse_reply_err(req, errno.EIO) 41 42cdef object get_request_context(fuse_req_t req): 43 '''Get RequestContext() object''' 44 45 cdef const_fuse_ctx* context 46 cdef RequestContext ctx 47 48 context = fuse_req_ctx(req) 49 ctx = RequestContext.__new__(RequestContext) 50 ctx.pid = context.pid 51 ctx.uid = context.uid 52 ctx.gid = context.gid 53 ctx.umask = context.umask 54 55 return ctx 56 57cdef void init_fuse_ops(): 58 '''Initialize fuse_lowlevel_ops structure''' 59 60 string.memset(&fuse_ops, 0, sizeof(fuse_lowlevel_ops)) 61 62 fuse_ops.init = fuse_init 63 fuse_ops.destroy = fuse_destroy 64 fuse_ops.lookup = fuse_lookup 65 fuse_ops.forget = fuse_forget 66 fuse_ops.getattr = fuse_getattr 67 fuse_ops.setattr = fuse_setattr 68 fuse_ops.readlink = fuse_readlink 69 fuse_ops.mknod = fuse_mknod 70 fuse_ops.mkdir = fuse_mkdir 71 fuse_ops.unlink = fuse_unlink 72 fuse_ops.rmdir = fuse_rmdir 73 fuse_ops.symlink = fuse_symlink 74 fuse_ops.rename = fuse_rename 75 fuse_ops.link = fuse_link 76 fuse_ops.open = fuse_open 77 fuse_ops.read = fuse_read 78 fuse_ops.write = fuse_write 79 fuse_ops.flush = fuse_flush 80 fuse_ops.release = fuse_release 81 fuse_ops.fsync = fuse_fsync 82 fuse_ops.opendir = fuse_opendir 83 fuse_ops.readdir = fuse_readdir 84 fuse_ops.releasedir = fuse_releasedir 85 fuse_ops.fsyncdir = fuse_fsyncdir 86 fuse_ops.statfs = fuse_statfs 87 ASSIGN_DARWIN(fuse_ops.setxattr, &fuse_setxattr_darwin) 88 ASSIGN_DARWIN(fuse_ops.getxattr, &fuse_getxattr_darwin) 89 ASSIGN_NOT_DARWIN(fuse_ops.setxattr, &fuse_setxattr) 90 ASSIGN_NOT_DARWIN(fuse_ops.getxattr, &fuse_getxattr) 91 fuse_ops.listxattr = fuse_listxattr 92 fuse_ops.removexattr = fuse_removexattr 93 fuse_ops.access = fuse_access 94 fuse_ops.create = fuse_create 95 fuse_ops.forget_multi = fuse_forget_multi 96 fuse_ops.write_buf = fuse_write_buf 97 98cdef make_fuse_args(args, fuse_args* f_args): 99 cdef char* arg 100 cdef int i 101 cdef ssize_t size_s 102 cdef size_t size 103 104 args_new = [ b'Python-LLFUSE' ] 105 for el in args: 106 args_new.append(b'-o') 107 args_new.append(el.encode('us-ascii')) 108 args = args_new 109 110 f_args.argc = <int> len(args) 111 if f_args.argc == 0: 112 f_args.argv = NULL 113 return 114 115 f_args.allocated = 1 116 f_args.argv = <char**> stdlib.calloc(<size_t> f_args.argc, sizeof(char*)) 117 118 if f_args.argv is NULL: 119 cpython.exc.PyErr_NoMemory() 120 121 try: 122 for (i, el) in enumerate(args): 123 PyBytes_AsStringAndSize(el, &arg, &size_s) 124 size = <size_t> size_s # guaranteed positive 125 f_args.argv[i] = <char*> stdlib.malloc((size+1)*sizeof(char)) 126 127 if f_args.argv[i] is NULL: 128 cpython.exc.PyErr_NoMemory() 129 130 string.strncpy(f_args.argv[i], arg, size+1) 131 except: 132 for i in range(f_args.argc): 133 # Freeing a NULL pointer (if this element has not been allocated 134 # yet) is fine. 135 stdlib.free(f_args.argv[i]) 136 stdlib.free(f_args.argv) 137 raise 138 139cdef class Lock: 140 ''' 141 This is the class of lock itself as well as a context manager to 142 execute code while the global lock is being held. 143 ''' 144 145 def __init__(self): 146 raise TypeError('You should not instantiate this class, use the ' 147 'provided instance instead.') 148 149 def acquire(self, timeout=None): 150 '''Acquire global lock 151 152 If *timeout* is not None, and the lock could not be acquired 153 after waiting for *timeout* seconds, return False. Otherwise 154 return True. 155 ''' 156 157 cdef int ret 158 cdef int timeout_c 159 160 if timeout is None: 161 timeout_c = 0 162 else: 163 timeout_c = timeout 164 165 with nogil: 166 ret = acquire(timeout_c) 167 168 if ret == 0: 169 return True 170 elif ret == ETIMEDOUT and timeout != 0: 171 return False 172 elif ret == EDEADLK: 173 raise RuntimeError("Global lock cannot be acquired more than once") 174 elif ret == EPROTO: 175 raise RuntimeError("Lock still taken after receiving unlock notification") 176 elif ret == EINVAL: 177 raise RuntimeError("Lock not initialized") 178 else: 179 raise RuntimeError(strerror(ret)) 180 181 def release(self): 182 '''Release global lock''' 183 184 cdef int ret 185 with nogil: 186 ret = release() 187 188 if ret == 0: 189 return 190 elif ret == EPERM: 191 raise RuntimeError("Lock can only be released by the holding thread") 192 elif ret == EINVAL: 193 raise RuntimeError("Lock not initialized") 194 else: 195 raise RuntimeError(strerror(ret)) 196 197 def yield_(self, count=1): 198 '''Yield global lock to a different thread 199 200 A call to `~Lock.yield_` is roughly similar to:: 201 202 for i in range(count): 203 if no_threads_waiting_for(lock): 204 break 205 lock.release() 206 lock.acquire() 207 208 However, when using `~Lock.yield_` it is guaranteed that the lock will 209 actually be passed to a different thread (the above pseude-code may 210 result in the same thread re-acquiring the lock *count* times). 211 ''' 212 213 cdef int ret 214 cdef int count_c 215 216 count_c = count 217 with nogil: 218 ret = c_yield(count_c) 219 220 if ret == 0: 221 return 222 elif ret == EPERM: 223 raise RuntimeError("Lock can only be released by the holding thread") 224 elif ret == EPROTO: 225 raise RuntimeError("Lock still taken after receiving unlock notification") 226 elif ret == ENOMSG: 227 raise RuntimeError("Other thread didn't take lock") 228 elif ret == EINVAL: 229 raise RuntimeError("Lock not initialized") 230 else: 231 raise RuntimeError(strerror(ret)) 232 233 def __enter__(self): 234 self.acquire() 235 236 def __exit__(self, exc_type, exc_val, exc_tb): 237 self.release() 238 239 def __getstate__(self): 240 raise PicklingError("Lock instances can't be pickled") 241 242cdef class NoLockManager: 243 '''Context manager to execute code while the global lock is released''' 244 245 def __init__(self): 246 raise TypeError('You should not instantiate this class, use the ' 247 'provided instance instead.') 248 249 def __enter__ (self): 250 lock.release() 251 252 def __exit__(self, *a): 253 lock.acquire() 254 255 def __getstate__(self): 256 raise PicklingError("NoLockManager instances can't be pickled") 257 258def _notify_loop(): 259 '''Read notifications from queue and send to FUSE kernel module''' 260 261 cdef ssize_t len_ 262 cdef char *cname 263 cdef NotifyRequest req 264 265 while True: 266 req = _notify_queue.get() 267 if req is None: 268 return 269 270 if req.kind == NOTIFY_INVAL_INODE: 271 if req.attr_only: 272 with nogil: 273 fuse_lowlevel_notify_inval_inode(channel, req.ino, -1, 0) 274 else: 275 with nogil: 276 fuse_lowlevel_notify_inval_inode(channel, req.ino, 0, 0) 277 elif req.kind == NOTIFY_INVAL_ENTRY: 278 PyBytes_AsStringAndSize(req.name, &cname, &len_) 279 with nogil: 280 # len_ is guaranteed positive 281 fuse_lowlevel_notify_inval_entry(channel, req.ino, cname, 282 <size_t> len_) 283 else: 284 raise RuntimeError("Weird request kind received: %d", req.kind) 285 286cdef str2bytes(s): 287 '''Convert *s* to bytes 288 289 Under Python 2.x, just returns *s*. Under Python 3.x, converts 290 to file system encoding using surrogateescape. 291 ''' 292 293 if PY_MAJOR_VERSION < 3: 294 return s 295 else: 296 return s.encode(fse, 'surrogateescape') 297 298cdef bytes2str(s): 299 '''Convert *s* to str 300 301 Under Python 2.x, just returns *s*. Under Python 3.x, converts 302 from file system encoding using surrogateescape. 303 ''' 304 305 if PY_MAJOR_VERSION < 3: 306 return s 307 else: 308 return s.decode(fse, 'surrogateescape') 309 310cdef strerror(int errno): 311 try: 312 return os.strerror(errno) 313 except ValueError: 314 return 'errno: %d' % errno 315 316@cython.freelist(10) 317cdef class RequestContext: 318 ''' 319 Instances of this class are passed to some `Operations` methods to 320 provide information about the caller of the syscall that initiated 321 the request. 322 ''' 323 324 cdef readonly uid_t uid 325 cdef readonly pid_t pid 326 cdef readonly gid_t gid 327 cdef readonly mode_t umask 328 329 def __getstate__(self): 330 raise PicklingError("RequestContext instances can't be pickled") 331 332@cython.freelist(10) 333cdef class SetattrFields: 334 ''' 335 `SetattrFields` instances are passed to the `~Operations.setattr` handler 336 to specify which attributes should be updated. 337 ''' 338 339 cdef readonly object update_atime 340 cdef readonly object update_mtime 341 cdef readonly object update_mode 342 cdef readonly object update_uid 343 cdef readonly object update_gid 344 cdef readonly object update_size 345 346 def __cinit__(self): 347 self.update_atime = False 348 self.update_mtime = False 349 self.update_mode = False 350 self.update_uid = False 351 self.update_gid = False 352 self.update_size = False 353 354 def __getstate__(self): 355 raise PicklingError("SetattrFields instances can't be pickled") 356 357@cython.freelist(30) 358cdef class EntryAttributes: 359 ''' 360 Instances of this class store attributes of directory entries. 361 Most of the attributes correspond to the elements of the ``stat`` 362 C struct as returned by e.g. ``fstat`` and should be 363 self-explanatory. 364 ''' 365 366 # Attributes are documented in rst/data.rst 367 368 cdef fuse_entry_param fuse_param 369 cdef struct_stat *attr 370 371 def __cinit__(self): 372 string.memset(&self.fuse_param, 0, sizeof(fuse_entry_param)) 373 self.attr = &self.fuse_param.attr 374 self.fuse_param.generation = 0 375 self.fuse_param.entry_timeout = 300 376 self.fuse_param.attr_timeout = 300 377 378 self.attr.st_mode = S_IFREG 379 self.attr.st_blksize = 4096 380 self.attr.st_nlink = 1 381 382 @property 383 def st_ino(self): 384 return self.fuse_param.ino 385 @st_ino.setter 386 def st_ino(self, val): 387 self.fuse_param.ino = val 388 self.attr.st_ino = val 389 390 @property 391 def generation(self): 392 '''The inode generation number''' 393 return self.fuse_param.generation 394 @generation.setter 395 def generation(self, val): 396 self.fuse_param.generation = val 397 398 @property 399 def attr_timeout(self): 400 '''Validity timeout for the attributes of the directory entry 401 402 Floating point numbers may be used. Units are seconds. 403 ''' 404 return self.fuse_param.attr_timeout 405 @attr_timeout.setter 406 def attr_timeout(self, val): 407 self.fuse_param.attr_timeout = val 408 409 @property 410 def entry_timeout(self): 411 '''Validity timeout for the name/existence of the directory entry 412 413 Floating point numbers may be used. Units are seconds. 414 ''' 415 return self.fuse_param.entry_timeout 416 @entry_timeout.setter 417 def entry_timeout(self, val): 418 self.fuse_param.entry_timeout = val 419 420 @property 421 def st_mode(self): 422 return self.attr.st_mode 423 @st_mode.setter 424 def st_mode(self, val): 425 self.attr.st_mode = val 426 427 @property 428 def st_nlink(self): 429 return self.attr.st_nlink 430 @st_nlink.setter 431 def st_nlink(self, val): 432 self.attr.st_nlink = val 433 434 @property 435 def st_uid(self): 436 return self.attr.st_uid 437 @st_uid.setter 438 def st_uid(self, val): 439 self.attr.st_uid = val 440 441 @property 442 def st_gid(self): 443 return self.attr.st_gid 444 @st_gid.setter 445 def st_gid(self, val): 446 self.attr.st_gid = val 447 448 @property 449 def st_rdev(self): 450 return self.attr.st_rdev 451 @st_rdev.setter 452 def st_rdev(self, val): 453 self.attr.st_rdev = val 454 455 @property 456 def st_size(self): 457 return self.attr.st_size 458 @st_size.setter 459 def st_size(self, val): 460 self.attr.st_size = val 461 462 @property 463 def st_blocks(self): 464 return self.attr.st_blocks 465 @st_blocks.setter 466 def st_blocks(self, val): 467 self.attr.st_blocks = val 468 469 @property 470 def st_blksize(self): 471 return self.attr.st_blksize 472 @st_blksize.setter 473 def st_blksize(self, val): 474 self.attr.st_blksize = val 475 476 @property 477 def st_atime_ns(self): 478 '''Time of last access in (integer) nanoseconds''' 479 return (int(self.attr.st_atime) * 10**9 + GET_ATIME_NS(self.attr)) 480 @st_atime_ns.setter 481 def st_atime_ns(self, val): 482 self.attr.st_atime = val / 10**9 483 SET_ATIME_NS(self.attr, val % 10**9) 484 485 @property 486 def st_mtime_ns(self): 487 '''Time of last modification in (integer) nanoseconds''' 488 return (int(self.attr.st_mtime) * 10**9 + GET_MTIME_NS(self.attr)) 489 @st_mtime_ns.setter 490 def st_mtime_ns(self, val): 491 self.attr.st_mtime = val / 10**9 492 SET_MTIME_NS(self.attr, val % 10**9) 493 494 @property 495 def st_ctime_ns(self): 496 '''Time of last inode modification in (integer) nanoseconds''' 497 return (int(self.attr.st_ctime) * 10**9 + GET_CTIME_NS(self.attr)) 498 @st_ctime_ns.setter 499 def st_ctime_ns(self, val): 500 self.attr.st_ctime = val / 10**9 501 SET_CTIME_NS(self.attr, val % 10**9) 502 503 @property 504 def st_birthtime_ns(self): 505 '''Time of inode creation in (integer) nanoseconds. 506 507 Only available under BSD and OS X. Will be zero on Linux. 508 ''' 509 510 # Use C macro to prevent compiler error on Linux 511 # (where st_birthtime does not exist) 512 return int(GET_BIRTHTIME(self.attr) * 10**9 513 + GET_BIRTHTIME_NS(self.attr)) 514 515 @st_birthtime_ns.setter 516 def st_birthtime_ns(self, val): 517 # Use C macro to prevent compiler error on Linux 518 # (where st_birthtime does not exist) 519 SET_BIRTHTIME(self.attr, val / 10**9) 520 SET_BIRTHTIME_NS(self.attr, val % 10**9) 521 522 # Pickling and copy support 523 def __getstate__(self): 524 state = dict() 525 for k in ('st_ino', 'generation', 'entry_timeout', 'attr_timeout', 526 'st_mode', 'st_nlink', 'st_uid', 'st_gid', 'st_rdev', 527 'st_size', 'st_blksize', 'st_blocks', 'st_atime_ns', 528 'st_ctime_ns', 'st_mtime_ns', 'st_birthtime_ns'): 529 state[k] = getattr(self, k) 530 return state 531 532 def __setstate__(self, state): 533 for (k,v) in state.items(): 534 setattr(self, k, v) 535 536 537@cython.freelist(1) 538cdef class StatvfsData: 539 ''' 540 Instances of this class store information about the file system. 541 The attributes correspond to the elements of the ``statvfs`` 542 struct, see :manpage:`statvfs(2)` for details. 543 ''' 544 545 cdef statvfs stat 546 547 def __cinit__(self): 548 string.memset(&self.stat, 0, sizeof(statvfs)) 549 550 @property 551 def f_bsize(self): 552 return self.stat.f_bsize 553 @f_bsize.setter 554 def f_bsize(self, val): 555 self.stat.f_bsize = val 556 557 @property 558 def f_frsize(self): 559 return self.stat.f_frsize 560 @f_frsize.setter 561 def f_frsize(self, val): 562 self.stat.f_frsize = val 563 564 @property 565 def f_blocks(self): 566 return self.stat.f_blocks 567 @f_blocks.setter 568 def f_blocks(self, val): 569 self.stat.f_blocks = val 570 571 @property 572 def f_bfree(self): 573 return self.stat.f_bfree 574 @f_bfree.setter 575 def f_bfree(self, val): 576 self.stat.f_bfree = val 577 578 @property 579 def f_bavail(self): 580 return self.stat.f_bavail 581 @f_bavail.setter 582 def f_bavail(self, val): 583 self.stat.f_bavail = val 584 585 @property 586 def f_files(self): 587 return self.stat.f_files 588 @f_files.setter 589 def f_files(self, val): 590 self.stat.f_files = val 591 592 @property 593 def f_ffree(self): 594 return self.stat.f_ffree 595 @f_ffree.setter 596 def f_ffree(self, val): 597 self.stat.f_ffree = val 598 599 @property 600 def f_favail(self): 601 return self.stat.f_favail 602 @f_favail.setter 603 def f_favail(self, val): 604 self.stat.f_favail = val 605 606 @property 607 def f_namemax(self): 608 return self.stat.f_namemax 609 @f_namemax.setter 610 def f_namemax(self, val): 611 self.stat.f_namemax = val 612 613 # Pickling and copy support 614 def __getstate__(self): 615 state = dict() 616 for k in ('f_bsize', 'f_frsize', 'f_blocks', 'f_bfree', 617 'f_bavail', 'f_files', 'f_ffree', 'f_favail', 618 'f_namemax'): 619 state[k] = getattr(self, k) 620 return state 621 622 def __setstate__(self, state): 623 for (k,v) in state.items(): 624 setattr(self, k, v) 625 626# As of Cython 0.23.1, @cython.freelist cannot be used for 627# classes that derive from a builtin type. 628cdef class FUSEError(Exception): 629 ''' 630 This exception may be raised by request handlers to indicate that 631 the requested operation could not be carried out. The system call 632 that resulted in the request (if any) will then fail with error 633 code *errno_*. 634 ''' 635 636 # If we call this variable "errno", we will get syntax errors 637 # during C compilation (maybe something else declares errno as 638 # a macro?) 639 cdef readonly int errno_ 640 641 @property 642 def errno(self): 643 '''Error code to return to client process''' 644 return self.errno_ 645 646 def __cinit__(self, errno): 647 self.errno_ = errno 648 649 def __str__(self): 650 return strerror(self.errno_) 651 652@cython.freelist(300) 653cdef class NotifyRequest: 654 cdef fuse_ino_t ino 655 cdef char attr_only 656 cdef object name 657 cdef int kind 658 659cdef PyBytes_from_bufvec(fuse_bufvec *src): 660 cdef fuse_bufvec dst 661 cdef size_t len_ 662 cdef ssize_t res 663 664 len_ = fuse_buf_size(src) - src.off 665 if len_ > PY_SSIZE_T_MAX: 666 raise OverflowError('Value too long to convert to Python') 667 buf = PyBytes_FromStringAndSize(NULL, <ssize_t> len_) 668 dst.count = 1 669 dst.idx = 0 670 dst.off = 0 671 dst.buf[0].mem = PyBytes_AS_STRING(buf) 672 dst.buf[0].size = len_ 673 dst.buf[0].flags = 0 674 res = fuse_buf_copy(&dst, src, 0) 675 if res < 0: 676 raise OSError(errno.errno, 'fuse_buf_copy failed with ' 677 + strerror(errno.errno)) 678 elif <size_t> res < len_: 679 # This is expected to be rare 680 return buf[:res] 681 else: 682 return buf 683 684cdef class VoidPtrCapsule: 685 cdef void* ptr 686 687cdef free_p(VoidPtrCapsule cap): 688 stdlib.free(cap.ptr) 689 690cdef inline encap_ptr(void *ptr): 691 cdef VoidPtrCapsule cap 692 cap = VoidPtrCapsule.__new__(VoidPtrCapsule) 693 cap.ptr = ptr 694 return cap 695 696cdef void signal_handler(int sig, siginfo_t *si, void* ctx) nogil: 697 global exit_reason 698 if session != NULL: 699 fuse_session_exit(session) 700 exit_reason = sig 701 702cdef void do_nothing(int sig, siginfo_t *si, void* ctx) nogil: 703 pass 704 705cdef int sigaction_p(int sig, sigaction_t *sa, 706 sigaction_t *old_sa) except -1: 707 cdef int res 708 res = sigaction(sig, sa, old_sa) 709 if res != 0: 710 raise OSError(errno.errno, 'sigaction failed with ' 711 + strerror(errno.errno)) 712 return 0 713 714cdef sigaction_t sa_backup[5] 715cdef set_signal_handlers(): 716 cdef sigaction_t sa 717 718 sigemptyset(&sa.sa_mask) 719 sa.sa_sigaction = &signal_handler 720 sa.sa_flags = SA_SIGINFO 721 sigaction_p(signal.SIGTERM, &sa, &sa_backup[0]) 722 sigaction_p(signal.SIGINT, &sa, &sa_backup[1]) 723 sigaction_p(signal.SIGHUP, &sa, &sa_backup[2]) 724 725 # This is used to interrupt system calls without 726 # doing anything else. 727 sa.sa_sigaction = &do_nothing 728 sa.sa_flags = SA_SIGINFO 729 sigaction_p(signal.SIGUSR1, &sa, &sa_backup[3]) 730 731 sa.sa_handler = signal.SIG_IGN 732 sa.sa_flags = 0 733 sigaction_p(signal.SIGPIPE, &sa, &sa_backup[4]) 734 735cdef restore_signal_handlers(): 736 sigaction_p(signal.SIGTERM, &sa_backup[0], NULL) 737 sigaction_p(signal.SIGINT, &sa_backup[1], NULL) 738 sigaction_p(signal.SIGHUP, &sa_backup[2], NULL) 739 sigaction_p(signal.SIGUSR1, &sa_backup[3], NULL) 740 sigaction_p(signal.SIGPIPE, &sa_backup[4], NULL) 741 742cdef void* calloc_or_raise(size_t nmemb, size_t size) except NULL: 743 cdef void* mem 744 mem = stdlib.calloc(nmemb, size) 745 if mem is NULL: 746 raise MemoryError() 747 return mem 748