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