summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJürg Billeter <j@bitron.ch>2020-04-29 09:25:01 +0200
committerJürg Billeter <j@bitron.ch>2020-06-03 13:49:39 +0200
commit3a0db49717cc4a41f61e724af3e824a6da8e60f8 (patch)
tree547fa0b3d18674dc7f344b65b5b47f484ed684d2
parent4162e8d8c71e7125305282a67f626c2ef9de4b9c (diff)
downloadbuildstream-3a0db49717cc4a41f61e724af3e824a6da8e60f8.tar.gz
Drop unused _fuse package
-rw-r--r--pyproject.toml1
-rw-r--r--setup.cfg2
-rw-r--r--src/buildstream/_fuse/__init__.py20
-rw-r--r--src/buildstream/_fuse/fuse.py1001
-rw-r--r--src/buildstream/_fuse/hardlinks.py220
-rw-r--r--src/buildstream/_fuse/mount.py217
6 files changed, 1 insertions, 1460 deletions
diff --git a/pyproject.toml b/pyproject.toml
index 29f5589b5..fefbbecd4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,7 +21,6 @@ exclude = '''
| build
| dist
)/
- | src/buildstream/_fuse
| src/buildstream/_protos
)
'''
diff --git a/setup.cfg b/setup.cfg
index 8f095fab1..5a3a75d67 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -36,5 +36,5 @@ ignore_missing_imports=True
ignore_missing_imports=True
# Ignore issues with generated files and vendored code
-[mypy-buildstream._protos.*,buildstream._fuse.*,buildstream._version]
+[mypy-buildstream._protos.*,buildstream._version]
ignore_errors = True
diff --git a/src/buildstream/_fuse/__init__.py b/src/buildstream/_fuse/__init__.py
deleted file mode 100644
index a5e882634..000000000
--- a/src/buildstream/_fuse/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Copyright (C) 2017 Codethink Limited
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-#
-# Authors:
-# Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
-
-from .hardlinks import SafeHardlinks
diff --git a/src/buildstream/_fuse/fuse.py b/src/buildstream/_fuse/fuse.py
deleted file mode 100644
index 41e126ef5..000000000
--- a/src/buildstream/_fuse/fuse.py
+++ /dev/null
@@ -1,1001 +0,0 @@
-# This is an embedded copy of fuse.py taken from the following upstream commit:
-#
-# https://github.com/terencehonles/fusepy/commit/0eafeb557e0e70926ed9450008ef17057d302391
-#
-# Our local modifications are recorded in the Git history of this repo.
-
-# Copyright (c) 2012 Terence Honles <terence@honles.com> (maintainer)
-# Copyright (c) 2008 Giorgos Verigakis <verigak@gmail.com> (author)
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-# pylint: skip-file
-
-from __future__ import print_function, absolute_import, division
-
-from ctypes import *
-from ctypes.util import find_library
-from errno import *
-from os import strerror
-from platform import machine, system
-from signal import signal, SIGINT, SIG_DFL
-from stat import S_IFDIR
-from traceback import print_exc
-
-import logging
-
-try:
- from functools import partial
-except ImportError:
- # http://docs.python.org/library/functools.html#functools.partial
- def partial(func, *args, **keywords):
- def newfunc(*fargs, **fkeywords):
- newkeywords = keywords.copy()
- newkeywords.update(fkeywords)
- return func(*(args + fargs), **newkeywords)
-
- newfunc.func = func
- newfunc.args = args
- newfunc.keywords = keywords
- return newfunc
-
-try:
- basestring
-except NameError:
- basestring = str
-
-class c_timespec(Structure):
- _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]
-
-class c_utimbuf(Structure):
- _fields_ = [('actime', c_timespec), ('modtime', c_timespec)]
-
-class c_stat(Structure):
- pass # Platform dependent
-
-_system = system()
-_machine = machine()
-
-if _system == 'Darwin':
- _libiconv = CDLL(find_library('iconv'), RTLD_GLOBAL) # libfuse dependency
- _libfuse_path = (find_library('fuse4x') or find_library('osxfuse') or
- find_library('fuse'))
-else:
- _libfuse_path = find_library('fuse')
-
-if not _libfuse_path:
- raise EnvironmentError('Unable to find libfuse')
-else:
- _libfuse = CDLL(_libfuse_path)
-
-if _system == 'Darwin' and hasattr(_libfuse, 'macfuse_version'):
- _system = 'Darwin-MacFuse'
-
-
-if _system in ('Darwin', 'Darwin-MacFuse', 'FreeBSD'):
- ENOTSUP = 45
- c_dev_t = c_int32
- c_fsblkcnt_t = c_ulong
- c_fsfilcnt_t = c_ulong
- c_gid_t = c_uint32
- c_mode_t = c_uint16
- c_off_t = c_int64
- c_pid_t = c_int32
- c_uid_t = c_uint32
- setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
- c_size_t, c_int, c_uint32)
- getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
- c_size_t, c_uint32)
- if _system == 'Darwin':
- c_stat._fields_ = [
- ('st_dev', c_dev_t),
- ('st_mode', c_mode_t),
- ('st_nlink', c_uint16),
- ('st_ino', c_uint64),
- ('st_uid', c_uid_t),
- ('st_gid', c_gid_t),
- ('st_rdev', c_dev_t),
- ('st_atimespec', c_timespec),
- ('st_mtimespec', c_timespec),
- ('st_ctimespec', c_timespec),
- ('st_birthtimespec', c_timespec),
- ('st_size', c_off_t),
- ('st_blocks', c_int64),
- ('st_blksize', c_int32),
- ('st_flags', c_int32),
- ('st_gen', c_int32),
- ('st_lspare', c_int32),
- ('st_qspare', c_int64)]
- else:
- c_stat._fields_ = [
- ('st_dev', c_dev_t),
- ('st_ino', c_uint32),
- ('st_mode', c_mode_t),
- ('st_nlink', c_uint16),
- ('st_uid', c_uid_t),
- ('st_gid', c_gid_t),
- ('st_rdev', c_dev_t),
- ('st_atimespec', c_timespec),
- ('st_mtimespec', c_timespec),
- ('st_ctimespec', c_timespec),
- ('st_size', c_off_t),
- ('st_blocks', c_int64),
- ('st_blksize', c_int32)]
-elif _system == 'Linux':
- ENOTSUP = 95
- c_dev_t = c_ulonglong
- c_fsblkcnt_t = c_ulonglong
- c_fsfilcnt_t = c_ulonglong
- c_gid_t = c_uint
- c_mode_t = c_uint
- c_off_t = c_longlong
- c_pid_t = c_int
- c_uid_t = c_uint
- setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
- c_size_t, c_int)
-
- getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
- c_size_t)
-
- if _machine == 'x86_64':
- c_stat._fields_ = [
- ('st_dev', c_dev_t),
- ('st_ino', c_ulong),
- ('st_nlink', c_ulong),
- ('st_mode', c_mode_t),
- ('st_uid', c_uid_t),
- ('st_gid', c_gid_t),
- ('__pad0', c_int),
- ('st_rdev', c_dev_t),
- ('st_size', c_off_t),
- ('st_blksize', c_long),
- ('st_blocks', c_long),
- ('st_atimespec', c_timespec),
- ('st_mtimespec', c_timespec),
- ('st_ctimespec', c_timespec)]
- elif _machine == 'mips':
- c_stat._fields_ = [
- ('st_dev', c_dev_t),
- ('__pad1_1', c_ulong),
- ('__pad1_2', c_ulong),
- ('__pad1_3', c_ulong),
- ('st_ino', c_ulong),
- ('st_mode', c_mode_t),
- ('st_nlink', c_ulong),
- ('st_uid', c_uid_t),
- ('st_gid', c_gid_t),
- ('st_rdev', c_dev_t),
- ('__pad2_1', c_ulong),
- ('__pad2_2', c_ulong),
- ('st_size', c_off_t),
- ('__pad3', c_ulong),
- ('st_atimespec', c_timespec),
- ('__pad4', c_ulong),
- ('st_mtimespec', c_timespec),
- ('__pad5', c_ulong),
- ('st_ctimespec', c_timespec),
- ('__pad6', c_ulong),
- ('st_blksize', c_long),
- ('st_blocks', c_long),
- ('__pad7_1', c_ulong),
- ('__pad7_2', c_ulong),
- ('__pad7_3', c_ulong),
- ('__pad7_4', c_ulong),
- ('__pad7_5', c_ulong),
- ('__pad7_6', c_ulong),
- ('__pad7_7', c_ulong),
- ('__pad7_8', c_ulong),
- ('__pad7_9', c_ulong),
- ('__pad7_10', c_ulong),
- ('__pad7_11', c_ulong),
- ('__pad7_12', c_ulong),
- ('__pad7_13', c_ulong),
- ('__pad7_14', c_ulong)]
- elif _machine == 'ppc':
- c_stat._fields_ = [
- ('st_dev', c_dev_t),
- ('st_ino', c_ulonglong),
- ('st_mode', c_mode_t),
- ('st_nlink', c_uint),
- ('st_uid', c_uid_t),
- ('st_gid', c_gid_t),
- ('st_rdev', c_dev_t),
- ('__pad2', c_ushort),
- ('st_size', c_off_t),
- ('st_blksize', c_long),
- ('st_blocks', c_longlong),
- ('st_atimespec', c_timespec),
- ('st_mtimespec', c_timespec),
- ('st_ctimespec', c_timespec)]
- elif _machine == 'ppc64' or _machine == 'ppc64le':
- c_stat._fields_ = [
- ('st_dev', c_dev_t),
- ('st_ino', c_ulong),
- ('st_nlink', c_ulong),
- ('st_mode', c_mode_t),
- ('st_uid', c_uid_t),
- ('st_gid', c_gid_t),
- ('__pad', c_uint),
- ('st_rdev', c_dev_t),
- ('st_size', c_off_t),
- ('st_blksize', c_long),
- ('st_blocks', c_long),
- ('st_atimespec', c_timespec),
- ('st_mtimespec', c_timespec),
- ('st_ctimespec', c_timespec)]
- elif _machine == 'aarch64':
- c_stat._fields_ = [
- ('st_dev', c_dev_t),
- ('st_ino', c_ulong),
- ('st_mode', c_mode_t),
- ('st_nlink', c_uint),
- ('st_uid', c_uid_t),
- ('st_gid', c_gid_t),
- ('st_rdev', c_dev_t),
- ('__pad1', c_ulong),
- ('st_size', c_off_t),
- ('st_blksize', c_int),
- ('__pad2', c_int),
- ('st_blocks', c_long),
- ('st_atimespec', c_timespec),
- ('st_mtimespec', c_timespec),
- ('st_ctimespec', c_timespec)]
- else:
- # i686, use as fallback for everything else
- c_stat._fields_ = [
- ('st_dev', c_dev_t),
- ('__pad1', c_ushort),
- ('__st_ino', c_ulong),
- ('st_mode', c_mode_t),
- ('st_nlink', c_uint),
- ('st_uid', c_uid_t),
- ('st_gid', c_gid_t),
- ('st_rdev', c_dev_t),
- ('__pad2', c_ushort),
- ('st_size', c_off_t),
- ('st_blksize', c_long),
- ('st_blocks', c_longlong),
- ('st_atimespec', c_timespec),
- ('st_mtimespec', c_timespec),
- ('st_ctimespec', c_timespec),
- ('st_ino', c_ulonglong)]
-else:
- raise NotImplementedError('{} is not supported.'.format(_system))
-
-
-class c_statvfs(Structure):
- _fields_ = [
- ('f_bsize', c_ulong),
- ('f_frsize', c_ulong),
- ('f_blocks', c_fsblkcnt_t),
- ('f_bfree', c_fsblkcnt_t),
- ('f_bavail', c_fsblkcnt_t),
- ('f_files', c_fsfilcnt_t),
- ('f_ffree', c_fsfilcnt_t),
- ('f_favail', c_fsfilcnt_t),
- ('f_fsid', c_ulong),
- #('unused', c_int),
- ('f_flag', c_ulong),
- ('f_namemax', c_ulong)]
-
-if _system == 'FreeBSD':
- c_fsblkcnt_t = c_uint64
- c_fsfilcnt_t = c_uint64
- setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
- c_size_t, c_int)
-
- getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
- c_size_t)
-
- class c_statvfs(Structure):
- _fields_ = [
- ('f_bavail', c_fsblkcnt_t),
- ('f_bfree', c_fsblkcnt_t),
- ('f_blocks', c_fsblkcnt_t),
- ('f_favail', c_fsfilcnt_t),
- ('f_ffree', c_fsfilcnt_t),
- ('f_files', c_fsfilcnt_t),
- ('f_bsize', c_ulong),
- ('f_flag', c_ulong),
- ('f_frsize', c_ulong)]
-
-class fuse_file_info(Structure):
- _fields_ = [
- ('flags', c_int),
- ('fh_old', c_ulong),
- ('writepage', c_int),
- ('direct_io', c_uint, 1),
- ('keep_cache', c_uint, 1),
- ('flush', c_uint, 1),
- ('padding', c_uint, 29),
- ('fh', c_uint64),
- ('lock_owner', c_uint64)]
-
-class fuse_context(Structure):
- _fields_ = [
- ('fuse', c_voidp),
- ('uid', c_uid_t),
- ('gid', c_gid_t),
- ('pid', c_pid_t),
- ('private_data', c_voidp)]
-
-_libfuse.fuse_get_context.restype = POINTER(fuse_context)
-
-
-class fuse_operations(Structure):
- _fields_ = [
- ('getattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat))),
- ('readlink', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
- ('getdir', c_voidp), # Deprecated, use readdir
- ('mknod', CFUNCTYPE(c_int, c_char_p, c_mode_t, c_dev_t)),
- ('mkdir', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
- ('unlink', CFUNCTYPE(c_int, c_char_p)),
- ('rmdir', CFUNCTYPE(c_int, c_char_p)),
- ('symlink', CFUNCTYPE(c_int, c_char_p, c_char_p)),
- ('rename', CFUNCTYPE(c_int, c_char_p, c_char_p)),
- ('link', CFUNCTYPE(c_int, c_char_p, c_char_p)),
- ('chmod', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
- ('chown', CFUNCTYPE(c_int, c_char_p, c_uid_t, c_gid_t)),
- ('truncate', CFUNCTYPE(c_int, c_char_p, c_off_t)),
- ('utime', c_voidp), # Deprecated, use utimens
- ('open', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
-
- ('read', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t,
- c_off_t, POINTER(fuse_file_info))),
-
- ('write', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t,
- c_off_t, POINTER(fuse_file_info))),
-
- ('statfs', CFUNCTYPE(c_int, c_char_p, POINTER(c_statvfs))),
- ('flush', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
- ('release', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
- ('fsync', CFUNCTYPE(c_int, c_char_p, c_int, POINTER(fuse_file_info))),
- ('setxattr', setxattr_t),
- ('getxattr', getxattr_t),
- ('listxattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
- ('removexattr', CFUNCTYPE(c_int, c_char_p, c_char_p)),
- ('opendir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
-
- ('readdir', CFUNCTYPE(c_int, c_char_p, c_voidp,
- CFUNCTYPE(c_int, c_voidp, c_char_p,
- POINTER(c_stat), c_off_t),
- c_off_t, POINTER(fuse_file_info))),
-
- ('releasedir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
-
- ('fsyncdir', CFUNCTYPE(c_int, c_char_p, c_int,
- POINTER(fuse_file_info))),
-
- ('init', CFUNCTYPE(c_voidp, c_voidp)),
- ('destroy', CFUNCTYPE(c_voidp, c_voidp)),
- ('access', CFUNCTYPE(c_int, c_char_p, c_int)),
-
- ('create', CFUNCTYPE(c_int, c_char_p, c_mode_t,
- POINTER(fuse_file_info))),
-
- ('ftruncate', CFUNCTYPE(c_int, c_char_p, c_off_t,
- POINTER(fuse_file_info))),
-
- ('fgetattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat),
- POINTER(fuse_file_info))),
-
- ('lock', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info),
- c_int, c_voidp)),
-
- ('utimens', CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf))),
- ('bmap', CFUNCTYPE(c_int, c_char_p, c_size_t, POINTER(c_ulonglong))),
- ('flag_nullpath_ok', c_uint, 1),
- ('flag_nopath', c_uint, 1),
- ('flag_utime_omit_ok', c_uint, 1),
- ('flag_reserved', c_uint, 29),
- ]
-
-
-def time_of_timespec(ts):
- return ts.tv_sec + ts.tv_nsec / 10 ** 9
-
-def set_st_attrs(st, attrs):
- for key, val in attrs.items():
- if key in ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime'):
- timespec = getattr(st, key + 'spec', None)
- if timespec is None:
- continue
- timespec.tv_sec = int(val)
- timespec.tv_nsec = int((val - timespec.tv_sec) * 10 ** 9)
- elif hasattr(st, key):
- setattr(st, key, val)
-
-
-def fuse_get_context():
- 'Returns a (uid, gid, pid) tuple'
-
- ctxp = _libfuse.fuse_get_context()
- ctx = ctxp.contents
- return ctx.uid, ctx.gid, ctx.pid
-
-
-class FuseOSError(OSError):
- def __init__(self, errno):
- super().__init__(errno, strerror(errno))
-
-
-class FUSE(object):
- '''
- This class is the lower level interface and should not be subclassed under
- normal use. Its methods are called by fuse.
-
- Assumes API version 2.6 or later.
- '''
-
- OPTIONS = (
- ('foreground', '-f'),
- ('debug', '-d'),
- ('nothreads', '-s'),
- )
-
- def __init__(self, operations, mountpoint, raw_fi=False, encoding='utf-8',
- **kwargs):
-
- '''
- Setting raw_fi to True will cause FUSE to pass the fuse_file_info
- class as is to Operations, instead of just the fh field.
-
- This gives you access to direct_io, keep_cache, etc.
- '''
-
- # Note that in BuildStream we're assuming that raw_fi is always False.
- assert not raw_fi, "raw_fi is not supported in BuildStream."
-
- self.operations = operations
- self.raw_fi = raw_fi
- self.encoding = encoding
-
- args = ['fuse']
-
- args.extend(flag for arg, flag in self.OPTIONS
- if kwargs.pop(arg, False))
-
- kwargs.setdefault('fsname', operations.__class__.__name__)
- args.append('-o')
- args.append(','.join(self._normalize_fuse_options(**kwargs)))
- args.append(mountpoint)
-
- args = [arg.encode(encoding) for arg in args]
- argv = (c_char_p * len(args))(*args)
-
- fuse_ops = fuse_operations()
- for ent in fuse_operations._fields_:
- name, prototype = ent[:2]
-
- val = getattr(operations, name, None)
- if val is None:
- continue
-
- # Function pointer members are tested for using the
- # getattr(operations, name) above but are dynamically
- # invoked using self.operations(name)
- if hasattr(prototype, 'argtypes'):
- val = prototype(partial(self._wrapper, getattr(self, name)))
-
- setattr(fuse_ops, name, val)
-
- try:
- old_handler = signal(SIGINT, SIG_DFL)
- except ValueError:
- old_handler = SIG_DFL
-
- err = _libfuse.fuse_main_real(len(args), argv, pointer(fuse_ops),
- sizeof(fuse_ops), None)
-
- try:
- signal(SIGINT, old_handler)
- except ValueError:
- pass
-
- del self.operations # Invoke the destructor
- if err:
- raise RuntimeError(err)
-
- @staticmethod
- def _normalize_fuse_options(**kargs):
- for key, value in kargs.items():
- if isinstance(value, bool):
- if value is True: yield key
- else:
- yield '{}={}'.format(key, value)
-
- @staticmethod
- def _wrapper(func, *args, **kwargs):
- 'Decorator for the methods that follow'
-
- try:
- return func(*args, **kwargs) or 0
- except OSError as e:
- return -(e.errno or EFAULT)
- except:
- print_exc()
- return -EFAULT
-
- def _decode_optional_path(self, path):
- # NB: this method is intended for fuse operations that
- # allow the path argument to be NULL,
- # *not* as a generic path decoding method
- if path is None:
- return None
- return path.decode(self.encoding)
-
- def getattr(self, path, buf):
- return self.fgetattr(path, buf, None)
-
- def readlink(self, path, buf, bufsize):
- ret = self.operations('readlink', path.decode(self.encoding)) \
- .encode(self.encoding)
-
- # copies a string into the given buffer
- # (null terminated and truncated if necessary)
- data = create_string_buffer(ret[:bufsize - 1])
- memmove(buf, data, len(data))
- return 0
-
- def mknod(self, path, mode, dev):
- return self.operations('mknod', path.decode(self.encoding), mode, dev)
-
- def mkdir(self, path, mode):
- return self.operations('mkdir', path.decode(self.encoding), mode)
-
- def unlink(self, path):
- return self.operations('unlink', path.decode(self.encoding))
-
- def rmdir(self, path):
- return self.operations('rmdir', path.decode(self.encoding))
-
- def symlink(self, source, target):
- 'creates a symlink `target -> source` (e.g. ln -s source target)'
-
- return self.operations('symlink', target.decode(self.encoding),
- source.decode(self.encoding))
-
- def rename(self, old, new):
- return self.operations('rename', old.decode(self.encoding),
- new.decode(self.encoding))
-
- def link(self, source, target):
- 'creates a hard link `target -> source` (e.g. ln source target)'
-
- return self.operations('link', target.decode(self.encoding),
- source.decode(self.encoding))
-
- def chmod(self, path, mode):
- return self.operations('chmod', path.decode(self.encoding), mode)
-
- def chown(self, path, uid, gid):
- # Check if any of the arguments is a -1 that has overflowed
- if c_uid_t(uid + 1).value == 0:
- uid = -1
- if c_gid_t(gid + 1).value == 0:
- gid = -1
-
- return self.operations('chown', path.decode(self.encoding), uid, gid)
-
- def truncate(self, path, length):
- return self.operations('truncate', path.decode(self.encoding), length)
-
- def open(self, path, fip):
- fi = fip.contents
- if self.raw_fi:
- return self.operations('open', path.decode(self.encoding), fi)
- else:
- fi.fh = self.operations('open', path.decode(self.encoding),
- fi.flags)
-
- return 0
-
- def read(self, path, buf, size, offset, fip):
- if self.raw_fi:
- fh = fip.contents
- else:
- fh = fip.contents.fh
-
- ret = self.operations('read', self._decode_optional_path(path), size,
- offset, fh)
-
- if not ret: return 0
-
- retsize = len(ret)
- assert retsize <= size, \
- 'actual amount read {:d} greater than expected {:d}'.format(retsize, size)
-
- data = create_string_buffer(ret, retsize)
- memmove(buf, data, retsize)
- return retsize
-
- def write(self, path, buf, size, offset, fip):
- data = string_at(buf, size)
-
- if self.raw_fi:
- fh = fip.contents
- else:
- fh = fip.contents.fh
-
- return self.operations('write', self._decode_optional_path(path), data,
- offset, fh)
-
- def statfs(self, path, buf):
- stv = buf.contents
- attrs = self.operations('statfs', path.decode(self.encoding))
- for key, val in attrs.items():
- if hasattr(stv, key):
- setattr(stv, key, val)
-
- return 0
-
- def flush(self, path, fip):
- if self.raw_fi:
- fh = fip.contents
- else:
- fh = fip.contents.fh
-
- return self.operations('flush', self._decode_optional_path(path), fh)
-
- def release(self, path, fip):
- if self.raw_fi:
- fh = fip.contents
- else:
- fh = fip.contents.fh
-
- return self.operations('release', self._decode_optional_path(path), fh)
-
- def fsync(self, path, datasync, fip):
- if self.raw_fi:
- fh = fip.contents
- else:
- fh = fip.contents.fh
-
- return self.operations('fsync', self._decode_optional_path(path), datasync,
- fh)
-
- def setxattr(self, path, name, value, size, options, *args):
- return self.operations('setxattr', path.decode(self.encoding),
- name.decode(self.encoding),
- string_at(value, size), options, *args)
-
- def getxattr(self, path, name, value, size, *args):
- ret = self.operations('getxattr', path.decode(self.encoding),
- name.decode(self.encoding), *args)
-
- retsize = len(ret)
- # allow size queries
- if not value: return retsize
-
- # do not truncate
- if retsize > size: return -ERANGE
-
- buf = create_string_buffer(ret, retsize) # Does not add trailing 0
- memmove(value, buf, retsize)
-
- return retsize
-
- def listxattr(self, path, namebuf, size):
- attrs = self.operations('listxattr', path.decode(self.encoding)) or ''
- ret = '\x00'.join(attrs).encode(self.encoding)
- if len(ret) > 0:
- ret += '\x00'.encode(self.encoding)
-
- retsize = len(ret)
- # allow size queries
- if not namebuf: return retsize
-
- # do not truncate
- if retsize > size: return -ERANGE
-
- buf = create_string_buffer(ret, retsize)
- memmove(namebuf, buf, retsize)
-
- return retsize
-
- def removexattr(self, path, name):
- return self.operations('removexattr', path.decode(self.encoding),
- name.decode(self.encoding))
-
- def opendir(self, path, fip):
- # Ignore raw_fi
- fip.contents.fh = self.operations('opendir',
- path.decode(self.encoding))
-
- return 0
-
- def readdir(self, path, buf, filler, offset, fip):
- # Ignore raw_fi
- for item in self.operations('readdir', self._decode_optional_path(path),
- fip.contents.fh):
-
- if isinstance(item, basestring):
- name, st, offset = item, None, 0
- else:
- name, attrs, offset = item
- if attrs:
- st = c_stat()
- set_st_attrs(st, attrs)
- else:
- st = None
-
- if filler(buf, name.encode(self.encoding), st, offset) != 0:
- break
-
- return 0
-
- def releasedir(self, path, fip):
- # Ignore raw_fi
- return self.operations('releasedir', self._decode_optional_path(path),
- fip.contents.fh)
-
- def fsyncdir(self, path, datasync, fip):
- # Ignore raw_fi
- return self.operations('fsyncdir', self._decode_optional_path(path),
- datasync, fip.contents.fh)
-
- def init(self, conn):
- return self.operations('init', '/')
-
- def destroy(self, private_data):
- return self.operations('destroy', '/')
-
- def access(self, path, amode):
- return self.operations('access', path.decode(self.encoding), amode)
-
- def create(self, path, mode, fip):
- fi = fip.contents
- path = path.decode(self.encoding)
-
- assert not self.raw_fi
-
- # This line is different from upstream to fix issues
- # reading file opened with O_CREAT|O_RDWR.
- # See issue #143.
- fi.fh = self.operations('create', path, mode, fi.flags)
- # END OF MODIFICATION
- return 0
-
- def ftruncate(self, path, length, fip):
- if self.raw_fi:
- fh = fip.contents
- else:
- fh = fip.contents.fh
-
- return self.operations('truncate', self._decode_optional_path(path),
- length, fh)
-
- def fgetattr(self, path, buf, fip):
- memset(buf, 0, sizeof(c_stat))
-
- st = buf.contents
- if not fip:
- fh = fip
- elif self.raw_fi:
- fh = fip.contents
- else:
- fh = fip.contents.fh
-
- attrs = self.operations('getattr', self._decode_optional_path(path), fh)
- set_st_attrs(st, attrs)
- return 0
-
- def lock(self, path, fip, cmd, lock):
- if self.raw_fi:
- fh = fip.contents
- else:
- fh = fip.contents.fh
-
- return self.operations('lock', self._decode_optional_path(path), fh, cmd,
- lock)
-
- def utimens(self, path, buf):
- if buf:
- atime = time_of_timespec(buf.contents.actime)
- mtime = time_of_timespec(buf.contents.modtime)
- times = (atime, mtime)
- else:
- times = None
-
- return self.operations('utimens', path.decode(self.encoding), times)
-
- def bmap(self, path, blocksize, idx):
- return self.operations('bmap', path.decode(self.encoding), blocksize,
- idx)
-
-
-class Operations(object):
- '''
- This class should be subclassed and passed as an argument to FUSE on
- initialization. All operations should raise a FuseOSError exception on
- error.
-
- When in doubt of what an operation should do, check the FUSE header file
- or the corresponding system call man page.
- '''
-
- def __call__(self, op, *args):
- if not hasattr(self, op):
- raise FuseOSError(EFAULT)
- return getattr(self, op)(*args)
-
- def access(self, path, amode):
- return 0
-
- bmap = None
-
- def chmod(self, path, mode):
- raise FuseOSError(EROFS)
-
- def chown(self, path, uid, gid):
- raise FuseOSError(EROFS)
-
- def create(self, path, mode, flags):
-
- raise FuseOSError(EROFS)
-
- def destroy(self, path):
- 'Called on filesystem destruction. Path is always /'
-
- pass
-
- def flush(self, path, fh):
- return 0
-
- def fsync(self, path, datasync, fh):
- return 0
-
- def fsyncdir(self, path, datasync, fh):
- return 0
-
- def getattr(self, path, fh=None):
- '''
- Returns a dictionary with keys identical to the stat C structure of
- stat(2).
-
- st_atime, st_mtime and st_ctime should be floats.
-
- NOTE: There is an incombatibility between Linux and Mac OS X
- concerning st_nlink of directories. Mac OS X counts all files inside
- the directory, while Linux counts only the subdirectories.
- '''
-
- if path != '/':
- raise FuseOSError(ENOENT)
- return dict(st_mode=(S_IFDIR | 0o755), st_nlink=2)
-
- def getxattr(self, path, name, position=0):
- raise FuseOSError(ENOTSUP)
-
- def init(self, path):
- '''
- Called on filesystem initialization. (Path is always /)
-
- Use it instead of __init__ if you start threads on initialization.
- '''
-
- pass
-
- def link(self, target, source):
- 'creates a hard link `target -> source` (e.g. ln source target)'
-
- raise FuseOSError(EROFS)
-
- def listxattr(self, path):
- return []
-
- lock = None
-
- def mkdir(self, path, mode):
- raise FuseOSError(EROFS)
-
- def mknod(self, path, mode, dev):
- raise FuseOSError(EROFS)
-
- def open(self, path, flags):
- '''
- When raw_fi is False (default case), open should return a numerical
- file handle.
-
- When raw_fi is True the signature of open becomes:
- open(self, path, fi)
-
- and the file handle should be set directly.
- '''
-
- return 0
-
- def opendir(self, path):
- 'Returns a numerical file handle.'
-
- return 0
-
- def read(self, path, size, offset, fh):
- 'Returns a string containing the data requested.'
-
- raise FuseOSError(EIO)
-
- def readdir(self, path, fh):
- '''
- Can return either a list of names, or a list of (name, attrs, offset)
- tuples. attrs is a dict as in getattr.
- '''
-
- return ['.', '..']
-
- def readlink(self, path):
- raise FuseOSError(ENOENT)
-
- def release(self, path, fh):
- return 0
-
- def releasedir(self, path, fh):
- return 0
-
- def removexattr(self, path, name):
- raise FuseOSError(ENOTSUP)
-
- def rename(self, old, new):
- raise FuseOSError(EROFS)
-
- def rmdir(self, path):
- raise FuseOSError(EROFS)
-
- def setxattr(self, path, name, value, options, position=0):
- raise FuseOSError(ENOTSUP)
-
- def statfs(self, path):
- '''
- Returns a dictionary with keys identical to the statvfs C structure of
- statvfs(3).
-
- On Mac OS X f_bsize and f_frsize must be a power of 2
- (minimum 512).
- '''
-
- return {}
-
- def symlink(self, target, source):
- 'creates a symlink `target -> source` (e.g. ln -s source target)'
-
- raise FuseOSError(EROFS)
-
- def truncate(self, path, length, fh=None):
- raise FuseOSError(EROFS)
-
- def unlink(self, path):
- raise FuseOSError(EROFS)
-
- def utimens(self, path, times=None):
- 'Times is a (atime, mtime) tuple. If None use current time.'
-
- return 0
-
- def write(self, path, data, offset, fh):
- raise FuseOSError(EROFS)
-
-
-class LoggingMixIn:
- log = logging.getLogger('fuse.log-mixin')
-
- def __call__(self, op, path, *args):
- self.log.debug('-> %s %s %s', op, path, repr(args))
- ret = '[Unhandled Exception]'
- try:
- ret = getattr(self, op)(path, *args)
- return ret
- except OSError as e:
- ret = str(e)
- raise
- finally:
- self.log.debug('<- %s %s', op, repr(ret))
diff --git a/src/buildstream/_fuse/hardlinks.py b/src/buildstream/_fuse/hardlinks.py
deleted file mode 100644
index 798e1c816..000000000
--- a/src/buildstream/_fuse/hardlinks.py
+++ /dev/null
@@ -1,220 +0,0 @@
-#
-# Copyright (C) 2016 Stavros Korokithakis
-# Copyright (C) 2017 Codethink Limited
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-#
-# Authors:
-# Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
-#
-# The filesystem operations implementation here is based
-# on some example code written by Stavros Korokithakis.
-
-import errno
-import os
-import shutil
-import stat
-import tempfile
-
-from .fuse import FuseOSError, Operations
-
-from .mount import Mount
-
-
-# SafeHardlinks()
-#
-# A FUSE mount which implements a copy on write hardlink experience.
-#
-# Args:
-# root (str): The underlying filesystem path to mirror
-# tmp (str): A directory on the same filesystem for creating temp files
-#
-class SafeHardlinks(Mount):
-
- def __init__(self, directory, tempdir, fuse_mount_options=None):
- self.directory = directory
- self.tempdir = tempdir
- if fuse_mount_options is None:
- fuse_mount_options = {}
- super().__init__(fuse_mount_options=fuse_mount_options)
-
- def create_operations(self):
- return SafeHardlinkOps(self.directory, self.tempdir)
-
-
-# SafeHardlinkOps()
-#
-# The actual FUSE Operations implementation below.
-#
-class SafeHardlinkOps(Operations):
-
- def __init__(self, root, tmp):
- self.root = root
- self.tmp = tmp
-
- def _full_path(self, partial):
- if partial.startswith("/"):
- partial = partial[1:]
- path = os.path.join(self.root, partial)
- return path
-
- def _ensure_copy(self, full_path):
- try:
- # Follow symbolic links manually here
- real_path = os.path.realpath(full_path)
- file_stat = os.stat(real_path)
-
- # Dont bother with files that cannot be hardlinked, oddly it
- # directories actually usually have st_nlink > 1 so just avoid
- # that.
- #
- # We already wont get symlinks here, and stat will throw
- # the FileNotFoundError below if a followed symlink did not exist.
- #
- if not stat.S_ISDIR(file_stat.st_mode) and file_stat.st_nlink > 1:
- with tempfile.TemporaryDirectory(dir=self.tmp) as tempdir:
- basename = os.path.basename(real_path)
- temp_path = os.path.join(tempdir, basename)
-
- # First copy, then unlink origin and rename
- shutil.copy2(real_path, temp_path)
- os.unlink(real_path)
- os.rename(temp_path, real_path)
-
- except FileNotFoundError:
- # This doesnt exist yet, assume we're about to create it
- # so it's not a problem.
- pass
-
- ###########################################################
- # Fuse Methods #
- ###########################################################
- def access(self, path, amode):
- full_path = self._full_path(path)
- if not os.access(full_path, amode):
- raise FuseOSError(errno.EACCES)
-
- def chmod(self, path, mode):
- full_path = self._full_path(path)
-
- # Ensure copies on chmod
- self._ensure_copy(full_path)
- return os.chmod(full_path, mode)
-
- def chown(self, path, uid, gid):
- full_path = self._full_path(path)
-
- # Ensure copies on chown
- self._ensure_copy(full_path)
- return os.chown(full_path, uid, gid)
-
- def getattr(self, path, fh=None):
- full_path = self._full_path(path)
- st = os.lstat(full_path)
- return dict((key, getattr(st, key)) for key in (
- 'st_atime', 'st_ctime', 'st_gid', 'st_mode',
- 'st_mtime', 'st_nlink', 'st_size', 'st_uid', 'st_rdev'))
-
- def readdir(self, path, fh):
- full_path = self._full_path(path)
-
- dirents = ['.', '..']
- if os.path.isdir(full_path):
- dirents.extend(os.listdir(full_path))
- for r in dirents:
- yield r
-
- def readlink(self, path):
- pathname = os.readlink(self._full_path(path))
- if pathname.startswith("/"):
- # Path name is absolute, sanitize it.
- return os.path.relpath(pathname, self.root)
- else:
- return pathname
-
- def mknod(self, path, mode, dev):
- return os.mknod(self._full_path(path), mode, dev)
-
- def rmdir(self, path):
- full_path = self._full_path(path)
- return os.rmdir(full_path)
-
- def mkdir(self, path, mode):
- return os.mkdir(self._full_path(path), mode)
-
- def statfs(self, path):
- full_path = self._full_path(path)
- stv = os.statvfs(full_path)
- return dict((key, getattr(stv, key)) for key in (
- 'f_bavail', 'f_bfree', 'f_blocks', 'f_bsize', 'f_favail',
- 'f_ffree', 'f_files', 'f_flag', 'f_frsize', 'f_namemax'))
-
- def unlink(self, path):
- os.unlink(self._full_path(path))
-
- def symlink(self, target, source):
- 'creates a symlink `target -> source` (e.g. ln -s source target)'
- return os.symlink(source, self._full_path(target))
-
- def rename(self, old, new):
- return os.rename(self._full_path(old), self._full_path(new))
-
- def link(self, target, source):
- 'creates a hard link `target -> source` (e.g. ln source target)'
-
- # When creating a hard link here, should we ensure the original
- # file is not a hardlink itself first ?
- #
- return os.link(self._full_path(source), self._full_path(target))
-
- def utimens(self, path, times=None):
- return os.utime(self._full_path(path), times)
-
- def open(self, path, flags):
- full_path = self._full_path(path)
-
- # If we're opening for writing, ensure it's a copy first
- if flags & os.O_WRONLY or flags & os.O_RDWR:
- self._ensure_copy(full_path)
-
- return os.open(full_path, flags)
-
- def create(self, path, mode, flags):
- full_path = self._full_path(path)
-
- # If it already exists, ensure it's a copy first
- self._ensure_copy(full_path)
- return os.open(full_path, flags, mode)
-
- def read(self, path, size, offset, fh):
- os.lseek(fh, offset, os.SEEK_SET)
- return os.read(fh, size)
-
- def write(self, path, data, offset, fh):
- os.lseek(fh, offset, os.SEEK_SET)
- return os.write(fh, data)
-
- def truncate(self, path, length, fh=None):
- full_path = self._full_path(path)
- with open(full_path, 'r+') as f:
- f.truncate(length)
-
- def flush(self, path, fh):
- return os.fsync(fh)
-
- def release(self, path, fh):
- return os.close(fh)
-
- def fsync(self, path, datasync, fh):
- return self.flush(path, fh)
diff --git a/src/buildstream/_fuse/mount.py b/src/buildstream/_fuse/mount.py
deleted file mode 100644
index 8cd968f0d..000000000
--- a/src/buildstream/_fuse/mount.py
+++ /dev/null
@@ -1,217 +0,0 @@
-#
-# Copyright (C) 2017 Codethink Limited
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-#
-# Authors:
-# Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
-
-import os
-import signal
-import time
-import sys
-
-from contextlib import contextmanager
-from multiprocessing import Process
-from .fuse import FUSE
-
-from .._exceptions import ImplError
-from .. import _signals, utils
-
-
-# Just a custom exception to raise here, for identifying possible
-# bugs with a fuse layer implementation
-#
-class FuseMountError(Exception):
- pass
-
-
-# This is a convenience class which takes care of synchronizing the
-# startup of FUSE and shutting it down.
-#
-# The implementations / subclasses should:
-#
-# - Overload the instance initializer to add any parameters
-# needed for their fuse Operations implementation
-#
-# - Implement create_operations() to create the Operations
-# instance on behalf of the superclass, using any additional
-# parameters collected in the initializer.
-#
-# Mount objects can be treated as contextmanagers, the volume
-# will be mounted during the context.
-#
-# UGLY CODE NOTE:
-#
-# This is a horrible little piece of code. The problem we face
-# here is that the highlevel libfuse API has fuse_main(), which
-# will either block in the foreground, or become a full daemon.
-#
-# With the daemon approach, we know that the fuse is mounted right
-# away when fuse_main() returns, then the daemon will go and handle
-# requests on its own, but then we have no way to shut down the
-# daemon.
-#
-# With the blocking approach, we still have it as a child process
-# so we can tell it to gracefully terminate; but it's impossible
-# to know when the mount is done, there is no callback for that
-#
-# The solution we use here without digging too deep into the
-# low level fuse API, is to start a child process which will
-# run the fuse loop in foreground, and we block the parent
-# process until the volume is mounted with a busy loop with timeouts.
-#
-class Mount():
-
- # These are not really class data, they are
- # just here for the sake of having None setup instead
- # of missing attributes, since we do not provide any
- # initializer and leave the initializer to the subclass.
- #
- __mountpoint = None
- __operations = None
- __process = None
- __logfile = None
-
- ################################################
- # User Facing API #
- ################################################
-
- def __init__(self, fuse_mount_options=None):
- self._fuse_mount_options = {} if fuse_mount_options is None else fuse_mount_options
-
- # _mount():
- #
- # Mount a fuse subclass implementation.
- #
- # Args:
- # (str): Location to mount this fuse fs
- #
- def _mount(self, mountpoint):
-
- assert self.__process is None
-
- self.__mountpoint = mountpoint
- self.__process = Process(target=self.__run_fuse, args=(self.__logfile.name,))
-
- # Ensure the child process does not inherit our signal handlers, if the
- # child wants to handle a signal then it will first set its own
- # handler, and then unblock it.
- with _signals.blocked([signal.SIGTERM, signal.SIGTSTP, signal.SIGINT], ignore=False):
- self.__process.start()
-
- while not os.path.ismount(mountpoint):
- if not self.__process.is_alive():
- self.__logfile.seek(0)
- stderr = self.__logfile.read()
- raise FuseMountError("Unable to mount {}: {}".format(mountpoint, stderr.decode().strip()))
-
- time.sleep(1 / 100)
-
- # _unmount():
- #
- # Unmount a fuse subclass implementation.
- #
- def _unmount(self):
-
- # Terminate child process and join
- if self.__process is not None:
- self.__process.terminate()
- self.__process.join()
-
- # Report an error if ever the underlying operations crashed for some reason.
- if self.__process.exitcode != 0:
- self.__logfile.seek(0)
- stderr = self.__logfile.read()
-
- raise FuseMountError("{} reported exit code {} when unmounting: {}"
- .format(type(self).__name__, self.__process.exitcode, stderr))
-
- self.__mountpoint = None
- self.__process = None
-
- # mounted():
- #
- # A context manager to run a code block with this fuse Mount
- # mounted, this will take care of automatically unmounting
- # in the case that the calling process is terminated.
- #
- # Args:
- # (str): Location to mount this fuse fs
- #
- @contextmanager
- def mounted(self, mountpoint):
-
- with utils._tempnamedfile() as logfile:
- self.__logfile = logfile
-
- self._mount(mountpoint)
- try:
- with _signals.terminator(self._unmount):
- yield
- finally:
- self._unmount()
-
- self.__logfile = None
-
- ################################################
- # Abstract Methods #
- ################################################
-
- # create_operations():
- #
- # Create an Operations class (from fusepy) and return it
- #
- # Returns:
- # (Operations): A FUSE Operations implementation
- def create_operations(self):
- raise ImplError("Mount subclass '{}' did not implement create_operations()"
- .format(type(self).__name__))
-
- ################################################
- # Child Process #
- ################################################
- def __run_fuse(self, filename):
- # Override stdout/stderr to the file given as a pointer, that way our parent process can get our output
- out = open(filename, "w")
- os.dup2(out.fileno(), sys.stdout.fileno())
- os.dup2(out.fileno(), sys.stderr.fileno())
-
- # First become session leader while signals are still blocked
- #
- # Then reset the SIGTERM handler to the default and finally
- # unblock SIGTERM.
- #
- os.setsid()
- signal.signal(signal.SIGTERM, signal.SIG_DFL)
- signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGTERM])
-
- # Ask the subclass to give us an Operations object
- #
- self.__operations = self.create_operations() # pylint: disable=assignment-from-no-return
-
- # Run fuse in foreground in this child process, internally libfuse
- # will handle SIGTERM and gracefully exit its own little main loop.
- #
- try:
- FUSE(self.__operations, self.__mountpoint, nothreads=True, foreground=True,
- **self._fuse_mount_options)
- except RuntimeError as exc:
- # FUSE will throw a RuntimeError with the exit code of libfuse as args[0]
- sys.exit(exc.args[0])
-
- # Explicit 0 exit code, if the operations crashed for some reason, the exit
- # code will not be 0, and we want to know about it.
- #
- sys.exit(0)