1
0
Fork 0
mirror of https://github.com/borgbase/vorta synced 2024-12-22 07:43:09 +00:00

Specify archive using -a for borg v2 mount.

* src/vorta/views/archive_tab.py : Move all command building logic into `mount.py`.

* src/vorta/borg/mount.py (BorgMountJob.prepare): Add command building logic previously in other places.

* src/vorta/borg/mount.py (BorgMountJob.prepare): Use `-a` command line option to select a single archive.

* src/vorta/utils.py (SHELL_PATTERN_ELEMENT): A pattern that can be used to detect shell pattern syntax.

* src/vorta/utils.py (get_mount_points): Implement parsing a borg v2 cmd.
This commit is contained in:
real-yfprojects 2022-08-31 09:21:35 +02:00 committed by Manu
parent b4a7c5494e
commit 5e046386d7
3 changed files with 58 additions and 28 deletions

View file

@ -1,15 +1,18 @@
import logging
import os import os
from vorta.store.models import SettingsModel from vorta.store.models import SettingsModel
from vorta.utils import borg_compat from vorta.utils import SHELL_PATTERN_ELEMENT, borg_compat
from .borg_job import BorgJob from .borg_job import BorgJob
logger = logging.getLogger(__name__)
class BorgMountJob(BorgJob): class BorgMountJob(BorgJob):
def started_event(self): def started_event(self):
self.updated.emit(self.tr('Mounting archive into folder…')) self.updated.emit(self.tr('Mounting archive into folder…'))
@classmethod @classmethod
def prepare(cls, profile): def prepare(cls, profile, archive: str = None):
ret = super().prepare(profile) ret = super().prepare(profile)
if not ret['ok']: if not ret['ok']:
return ret return ret
@ -26,8 +29,21 @@ def prepare(cls, profile):
if borg_compat.check('V2'): if borg_compat.check('V2'):
cmd.extend(["-r", profile.repo.url]) cmd.extend(["-r", profile.repo.url])
if archive:
# in shell patterns ?, * and [...] have a special meaning
pattern = SHELL_PATTERN_ELEMENT.sub(r'\\1', archive) # escape them
cmd.extend(['-a', pattern])
else: else:
cmd.append(f'{profile.repo.url}') source = f'{profile.repo.url}'
if archive:
source += f'::{archive}'
cmd.append(source)
if archive:
ret['mounted_archive'] = archive
ret['ok'] = True ret['ok'] = True
ret['cmd'] = cmd ret['cmd'] = cmd

View file

@ -358,6 +358,9 @@ def format_archive_name(profile, archive_name_tpl):
return archive_name_tpl.format(**available_vars) return archive_name_tpl.format(**available_vars)
SHELL_PATTERN_ELEMENT = re.compile(r'([?\[\]*])')
def get_mount_points(repo_url): def get_mount_points(repo_url):
mount_points = {} mount_points = {}
repo_mounts = [] repo_mounts = []
@ -368,23 +371,41 @@ def get_mount_points(repo_url):
if 'mount' not in proc.cmdline(): if 'mount' not in proc.cmdline():
continue continue
for idx, parameter in enumerate(proc.cmdline()): if borg_compat.check('V2'):
if parameter.startswith(repo_url): # command line syntax:
# mount from this repo # `borg mount -r <repo> <mountpoint> <path> (-a <archive_pattern>)`
cmd = proc.cmdline()
if repo_url in cmd:
i = cmd.index(repo_url)
if len(cmd) > i + 1:
mount_point = cmd[i + 1]
# The borg mount command specifies that the mount_point # Archive mount?
# parameter comes after the archive name ao = '-a' in cmd
if len(proc.cmdline()) > idx + 1: if ao or '--glob-archives' in cmd:
mount_point = proc.cmdline()[idx + 1] i = cmd.index('-a' if ao else '--glob-archives')
if len(cmd) >= i + 1 and not SHELL_PATTERN_ELEMENT.search(cmd[i + 1]):
# archive or full mount? mount_points[mount_point] = cmd[i + 1]
if parameter[len(repo_url) :].startswith('::'):
archive_name = parameter[len(repo_url) + 2 :]
mount_points[archive_name] = mount_point
break
else: else:
# repo mount point
repo_mounts.append(mount_point) repo_mounts.append(mount_point)
else:
for idx, parameter in enumerate(proc.cmdline()):
if parameter.startswith(repo_url):
# mount from this repo
# The borg mount command specifies that the mount_point
# parameter comes after the archive name
if len(proc.cmdline()) > idx + 1:
mount_point = proc.cmdline()[idx + 1]
# archive or full mount?
if parameter[len(repo_url) :].startswith('::'):
archive_name = parameter[len(repo_url) + 2 :]
mount_points[archive_name] = mount_point
break
else:
# repo mount point
repo_mounts.append(mount_point)
except (psutil.ZombieProcess, psutil.AccessDenied, psutil.NoSuchProcess): except (psutil.ZombieProcess, psutil.AccessDenied, psutil.NoSuchProcess):
# Getting process details may fail (e.g. zombie process on macOS) # Getting process details may fail (e.g. zombie process on macOS)

View file

@ -52,7 +52,7 @@ def __init__(self, parent=None, app=None):
"""Init.""" """Init."""
super().__init__(parent) super().__init__(parent)
self.setupUi(parent) self.setupUi(parent)
self.mount_points = {} # mount points of archives self.mount_points = {} # mapping of archive name to mount point
self.repo_mount_point: Optional[str] = None # mount point of whole repo self.repo_mount_point: Optional[str] = None # mount point of whole repo
self.menu = None self.menu = None
self.app = app self.app = app
@ -565,17 +565,11 @@ def mount_action(self, archive_name=None):
The archive to mount or None, by default None The archive to mount or None, by default None
""" """
profile = self.profile() profile = self.profile()
params = BorgMountJob.prepare(profile) params = BorgMountJob.prepare(profile, archive=archive_name)
if not params['ok']: if not params['ok']:
self._set_status(params['message']) self._set_status(params['message'])
return return
if archive_name:
# mount archive
params['cmd'][-1] += f'::{archive_name}'
params['current_archive'] = archive_name
# else mount complete repo
def receive(): def receive():
mount_point = dialog.selectedFiles() mount_point = dialog.selectedFiles()
if mount_point: if mount_point:
@ -598,13 +592,12 @@ def mount_result(self, result):
mount_point = result['params']['mount_point'] mount_point = result['params']['mount_point']
if result['params'].get('current_archive'): if result['params'].get('mounted_archive'):
# archive was mounted # archive was mounted
archive_name = result['params']['current_archive'] archive_name = result['params']['mounted_archive']
self.mount_points[archive_name] = mount_point self.mount_points[archive_name] = mount_point
# update column in table # update column in table
archive_name = result['params']['current_archive']
row = self.row_of_archive(archive_name) row = self.row_of_archive(archive_name)
item = QTableWidgetItem(result['cmd'][-1]) item = QTableWidgetItem(result['cmd'][-1])
self.archiveTable.setItem(row, 3, item) self.archiveTable.setItem(row, 3, item)