From c7d12e3fc0de35946a044b9113de4b8a1657a22c Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 13 Apr 2018 02:06:18 +0200 Subject: [PATCH 1/2] borg mount: support uid= and gid= mount options also: refactor popping an option, converting an options value (cherry picked from commit f993f0fd491bf259dfc3d9289a5982360fe95c02) --- src/borg/fuse.py | 49 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/borg/fuse.py b/src/borg/fuse.py index a85684a00f..5e06e3a6e3 100644 --- a/src/borg/fuse.py +++ b/src/borg/fuse.py @@ -230,12 +230,14 @@ def __init__(self, key, manifest, repository, args, decrypted_repository): self.contents = defaultdict(dict) self.default_uid = os.getuid() self.default_gid = os.getgid() - self.default_dir = Item(mode=0o40755, mtime=int(time.time() * 1e9), uid=self.default_uid, gid=self.default_gid) + self.default_dir = None # Archives to be loaded when first accessed, mapped by their placeholder inode self.pending_archives = {} self.cache = ItemCache(decrypted_repository) self.allow_damaged_files = False self.versions = False + self.uid_forced = None + self.gid_forced = None def _create_filesystem(self): self._create_dir(parent=1) # first call, create root dir (inode == 1) @@ -443,19 +445,40 @@ def sig_info_handler(self, sig_no, stack): def mount(self, mountpoint, mount_options, foreground=False): """Mount filesystem on *mountpoint* with *mount_options*.""" + + def pop_option(options, key, present, not_present, wanted_type): + assert isinstance(options, list) # we mutate this + for idx, option in enumerate(options): + if option == key: + options.pop(idx) + return present + if option.startswith(key + '='): + options.pop(idx) + value = option.split('=', 1)[1] + if wanted_type is bool: + v = value.lower() + if v in ('y', 'yes', 'true', '1'): + return True + if v in ('n', 'no', 'false', '0'): + return False + raise ValueError('unsupported value in option: %s' % option) + try: + return wanted_type(value) + except ValueError: + raise ValueError('unsupported value in option: %s' % option) from None + else: + return not_present + options = ['fsname=borgfs', 'ro'] if mount_options: options.extend(mount_options.split(',')) - try: - options.remove('allow_damaged_files') - self.allow_damaged_files = True - except ValueError: - pass - try: - options.remove('versions') - self.versions = True - except ValueError: - pass + self.allow_damaged_files = pop_option(options, 'allow_damaged_files', True, False, bool) + self.versions = pop_option(options, 'versions', True, False, bool) + self.uid_forced = pop_option(options, 'uid', None, None, int) + self.gid_forced = pop_option(options, 'gid', None, None, int) + dir_uid = self.uid_forced if self.uid_forced is not None else self.default_uid + dir_gid = self.gid_forced if self.gid_forced is not None else self.default_gid + self.default_dir = Item(mode=0o40755, mtime=int(time.time() * 1e9), uid=dir_uid, gid=dir_gid) self._create_filesystem() llfuse.init(self, mountpoint, options) if not foreground: @@ -500,8 +523,8 @@ def getattr(self, inode, ctx=None): entry.attr_timeout = 300 entry.st_mode = item.mode entry.st_nlink = item.get('nlink', 1) - entry.st_uid = item.uid if item.uid >= 0 else self.default_uid - entry.st_gid = item.gid if item.gid >= 0 else self.default_gid + entry.st_uid = self.uid_forced if self.uid_forced is not None else item.uid if item.uid >= 0 else self.default_uid + entry.st_gid = self.gid_forced if self.gid_forced is not None else item.gid if item.gid >= 0 else self.default_gid entry.st_rdev = item.get('rdev', 0) entry.st_size = item.get_size() entry.st_blksize = 512 From cd70192beaf5a3786636501f0e20368e16b7af8c Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 13 Apr 2018 02:42:22 +0200 Subject: [PATCH 2/2] borg mount: support umask= mount option (cherry picked from commit eb61c2153bfd8ebc96cf77bfb50f1507f6544bfd) --- src/borg/fuse.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/borg/fuse.py b/src/borg/fuse.py index 5e06e3a6e3..df8c9afa43 100644 --- a/src/borg/fuse.py +++ b/src/borg/fuse.py @@ -238,6 +238,7 @@ def __init__(self, key, manifest, repository, args, decrypted_repository): self.versions = False self.uid_forced = None self.gid_forced = None + self.umask = 0 def _create_filesystem(self): self._create_dir(parent=1) # first call, create root dir (inode == 1) @@ -446,7 +447,7 @@ def sig_info_handler(self, sig_no, stack): def mount(self, mountpoint, mount_options, foreground=False): """Mount filesystem on *mountpoint* with *mount_options*.""" - def pop_option(options, key, present, not_present, wanted_type): + def pop_option(options, key, present, not_present, wanted_type, int_base=0): assert isinstance(options, list) # we mutate this for idx, option in enumerate(options): if option == key: @@ -462,6 +463,11 @@ def pop_option(options, key, present, not_present, wanted_type): if v in ('n', 'no', 'false', '0'): return False raise ValueError('unsupported value in option: %s' % option) + if wanted_type is int: + try: + return int(value, base=int_base) + except ValueError: + raise ValueError('unsupported value in option: %s' % option) from None try: return wanted_type(value) except ValueError: @@ -476,9 +482,11 @@ def pop_option(options, key, present, not_present, wanted_type): self.versions = pop_option(options, 'versions', True, False, bool) self.uid_forced = pop_option(options, 'uid', None, None, int) self.gid_forced = pop_option(options, 'gid', None, None, int) + self.umask = pop_option(options, 'umask', 0, 0, int, int_base=8) # umask is octal, e.g. 222 or 0222 dir_uid = self.uid_forced if self.uid_forced is not None else self.default_uid dir_gid = self.gid_forced if self.gid_forced is not None else self.default_gid - self.default_dir = Item(mode=0o40755, mtime=int(time.time() * 1e9), uid=dir_uid, gid=dir_gid) + dir_mode = 0o40755 & ~self.umask + self.default_dir = Item(mode=dir_mode, mtime=int(time.time() * 1e9), uid=dir_uid, gid=dir_gid) self._create_filesystem() llfuse.init(self, mountpoint, options) if not foreground: @@ -521,7 +529,7 @@ def getattr(self, inode, ctx=None): entry.generation = 0 entry.entry_timeout = 300 entry.attr_timeout = 300 - entry.st_mode = item.mode + entry.st_mode = item.mode & ~self.umask entry.st_nlink = item.get('nlink', 1) entry.st_uid = self.uid_forced if self.uid_forced is not None else item.uid if item.uid >= 0 else self.default_uid entry.st_gid = self.gid_forced if self.gid_forced is not None else item.gid if item.gid >= 0 else self.default_gid