From 5760e8fe53361fcb18581f4208a646cd4105c313 Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 25 Oct 2022 01:22:53 +0200 Subject: [PATCH] [fix] Backup legacy tar --- share/actionsmap.yml | 3 ++- src/repositories/borg.py | 6 ++--- src/repositories/tar.py | 47 +++++++++++++++++++++++++--------------- src/repository.py | 5 +++++ 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 9fbfab049..c1d273b9e 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -1050,6 +1050,7 @@ backup: -p: full: --prefix help: Prefix of the backup archive + default: "" extra: pattern: &pattern_backup_archive_prefix - !!str ^[\w\-\._]{1,35}$ @@ -1307,7 +1308,7 @@ backup: help: Keep all archives within this time interval extra: pattern: &pattern_interval - - !!str ^\d+[Hdwmy]$ + - !!str ^\d+[Hdw]$ - "pattern_interval" diff --git a/src/repositories/borg.py b/src/repositories/borg.py index e18fdb5a4..7c0fc4d9c 100644 --- a/src/repositories/borg.py +++ b/src/repositories/borg.py @@ -156,11 +156,11 @@ class BorgBackupRepository(LocalBackupRepository): # password: "XXXXXXXX", } ) - else: + elif self.is_remote: cmd = ["borg", "delete", self.location] self._call('purge', cmd) - if not self.is_remote: - super().purge() + else: + super().purge() def list_archives_names(self, prefix=None): cmd = ["borg", "list", "--json", self.location] diff --git a/src/repositories/tar.py b/src/repositories/tar.py index f6dd7c986..2c0e17c13 100644 --- a/src/repositories/tar.py +++ b/src/repositories/tar.py @@ -18,19 +18,21 @@ along with this program; if not, see http://www.gnu.org/licenses """ -import glob import os import tarfile import shutil +from glob import glob +from datetime import datetime + from moulinette.utils.log import getActionLogger from moulinette import m18n from yunohost.utils.error import YunohostError, YunohostValidationError -from yunohost.utils.filesystem import free_space_in_directory -from yunohost.repository import LocalBackupRepository +from yunohost.repository import LocalBackupRepository, BackupArchive from yunohost.backup import BackupManager -from yunohost.utils.filesystem import space_used_in_directory +from yunohost.utils.system import space_used_by_directory, free_space_in_directory + from yunohost.settings import settings_get logger = getActionLogger("yunohost.repository") @@ -66,16 +68,13 @@ class TarBackupRepository(LocalBackupRepository): return [remove_extension(f) for f in archives] def compute_space_used(self): - return space_used_in_directory(self.location) + return space_used_by_directory(self.location) def compute_free_space(self): return free_space_in_directory(self.location) - def prune(self): - raise NotImplementedError() - -class TarBackupArchive: +class TarBackupArchive(BackupArchive): @property def archive_path(self): @@ -166,7 +165,7 @@ class TarBackupArchive: logger.debug("unable to delete '%s'", backup_file, exc_info=1) logger.warning(m18n.n("backup_delete_error", path=backup_file)) - def list(self): + def list(self, with_info=False): try: tar = tarfile.open( self.archive_path, @@ -179,14 +178,28 @@ class TarBackupArchive: raise YunohostError("backup_archive_open_failed") try: - return tar.getnames() + if not with_info: + return "\n".join(tar.getnames()) + else: + return {f.name: { + "mode": f.mode, # FIXME Numeric or letter mode + "type": int(f.type), + "uid": f.uid, + "gid": f.gid, + "user": f.uname, + "group": f.gname, + "size": f.size, + "mtime": datetime.fromtimestamp(f.mtime).isoformat(), + "linktarget": f.linkname, + } + for f in tar.getmembers()} except (IOError, EOFError, tarfile.ReadError) as e: tar.close() raise YunohostError( "backup_archive_corrupted", archive=self.archive_path, error=str(e) ) - def download(self): + def download(self, exclude_paths=[]): super().download() # If symlink, retrieve the real path archive_file = self.archive_path @@ -206,9 +219,9 @@ class TarBackupArchive: archive_folder, archive_file_name = archive_file.rsplit("/", 1) return static_file(archive_file_name, archive_folder, download=archive_file_name) - def extract(self, paths=None, exclude_paths=[]): - paths, exclude_paths = super().extract(paths, exclude_paths) - # Mount the tarball + def extract(self, paths=[], destination=None, exclude_paths=[]): + paths, destination, exclude_paths = super().extract(paths, destination, exclude_paths) + # Open the tarball try: tar = tarfile.open( self.archive_path, @@ -228,10 +241,10 @@ class TarBackupArchive: and all([not tarinfo.name.startswith(path) for path in exclude_paths]) ) ] - tar.extractall(members=subdir_and_files, path=self.work_dir) + tar.extractall(members=subdir_and_files, path=destination) tar.close() - def mount(self): + def mount(self, path): raise NotImplementedError() def _archive_exists(self): diff --git a/src/repository.py b/src/repository.py index 5164ff779..d6e689f8d 100644 --- a/src/repository.py +++ b/src/repository.py @@ -162,6 +162,8 @@ class BackupRepository(ConfigPanel): return {} def post_ask__method(self, question): + if question.value: + self.method = question.value self._cast_by_backup_method() return {} @@ -351,6 +353,8 @@ class BackupRepository(ConfigPanel): keep_last -= 1 continue + # TODO Improve performances by creating a BackupRepository.delete_archives() + # method to delete several archives in one command archives[created_at].delete() # ================================================= @@ -384,6 +388,7 @@ class LocalBackupRepository(BackupRepository): self.install() def purge(self): + # FIXME Manage the case where a repository is inside this repository... rm(self.location, recursive=True, force=True)