From 6690f5e012e65eb8064b18eef16df611961e2bb5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 23 Aug 2017 10:49:36 +0200 Subject: [PATCH] draft rescue mode through all layers MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The strategy is for rescue mode to reconstruct the relevant [*] information from the index by inspecting the passed tar object, then continue from there. On the crypto side, this boils down to a streamlined (and silent) version of the “scan” mode. The tarfile side is still WIP. [*] Omitting the useless parts like inode number. --- deltatar/crypto.py | 35 ++++++++++++++++++++++++++++++++--- deltatar/deltatar.py | 8 ++++++-- deltatar/tarfile.py | 18 ++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/deltatar/crypto.py b/deltatar/crypto.py index 538e4db..a5d08c4 100755 --- a/deltatar/crypto.py +++ b/deltatar/crypto.py @@ -637,6 +637,36 @@ def try_decrypt (ifd, off, hdr, secret, ofd=-1): return pos - off +def readable_objects_offsets (ifd, secret, cands): + """ + From a list of candidates, locate the ones that mark the start of actual + readable PDTCRYPT objects. + """ + good = [] + nobj = 0 + for cand in cands: + nobj += 1 + vdt, hdr = inspect_hdr (ifd, cand) + if vdt == HDR_CAND_JUNK: + pass # ignore unreadable ones + elif vdt in [HDR_CAND_GOOD, HDR_CAND_FISHY]: + off0 = cand + PDTCRYPT_HDR_SIZE + ok = try_decrypt (ifd, off0, hdr, secret) == hdr ["ctsize"] + if ok is True: + good.append (cand) + return good + + +def reconstruct_offsets (fname, secret): + ifd = os.open (fname, os.O_RDONLY) + + try: + cands = locate_hdr_candidates (ifd) + return readable_objects_offsets (ifd, secret, cands) + finally: + os.close (ifd) + + ############################################################################### ## passthrough / null encryption ############################################################################### @@ -1364,7 +1394,7 @@ def open2_dump_file (fname, dir_fd, force=False): outfd = -1 oflags = os.O_CREAT | os.O_WRONLY - if PDTCRYPT_OVERWRITE is True: + if force is True: oflags |= os.O_TRUNC else: oflags |= os.O_EXCL @@ -1874,6 +1904,7 @@ def bail (msg): def parse_argv (argv): + global PDTCRYPT_OVERWRITE global SELF mode = PDTCRYPT_DECRYPT secret = None @@ -1931,7 +1962,6 @@ def parse_argv (argv): outsspec = checked_arg () if PDTCRYPT_VERBOSE is True: noise ("PDT: decrypt to %s" % outsspec) elif arg in [ "-f", "--force" ]: - global PDTCRYPT_OVERWRITE PDTCRYPT_OVERWRITE = True if PDTCRYPT_VERBOSE is True: noise ("PDT: overwrite existing files") elif arg in [ "-S", "--split" ]: @@ -1965,7 +1995,6 @@ def parse_argv (argv): outsspec = checked_arg () if PDTCRYPT_VERBOSE is True: noise ("PDT: decrypt to %s" % outsspec) elif arg in [ "-f", "--force" ]: - global PDTCRYPT_OVERWRITE PDTCRYPT_OVERWRITE = True if PDTCRYPT_VERBOSE is True: noise ("PDT: overwrite existing files") else: diff --git a/deltatar/deltatar.py b/deltatar/deltatar.py index b1b8ccc..02670da 100644 --- a/deltatar/deltatar.py +++ b/deltatar/deltatar.py @@ -1490,15 +1490,19 @@ class DeltaTar(object): disaster=tarfile.TOLERANCE_RECOVER) - def rescue_backup(self, target_path, backup_indexes_paths=[], + def rescue_backup(self, target_path, backup_tar_path, restore_callback=None): """ More aggressive “unfsck” mode: do not rely on the index data as the files may be corrupt; skim files for header-like information and attempt to retrieve the data. """ + faux_index = tarfile.gen_rescue_index(backup_tar_path, + password=self.password, + key=self.crypto_key) + return self.restore_backup(target_path, - backup_indexes_paths=backup_indexes_paths, + backup_index=faux_index, disaster=tarfile.TOLERANCE_RESCUE) diff --git a/deltatar/tarfile.py b/deltatar/tarfile.py index cc74e92..ef4fca0 100644 --- a/deltatar/tarfile.py +++ b/deltatar/tarfile.py @@ -3291,6 +3291,24 @@ class TarIter: return tarinfo +#--------------------------------------------------------- +# support functionality for rescue mode +#--------------------------------------------------------- + +def gen_rescue_index (backup_tar_path, password=None, key=None): + psidx = [] # pseudo index, return value + offsets = None + secret = None + + if password is not None: + secret = (crypto.PDTCRYPT_SECRET_PW, password) + elif key is not None: + secret = (crypto.PDTCRYPT_SECRET_KEY, key) + + if secret is not None: + offsets = crypto.reconstruct_offsets (backup_tar_path, secret) + + return psidx #-------------------- # exported functions -- 1.7.1