implement volume handling for rescue mode
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Tue, 29 Aug 2017 10:00:54 +0000 (12:00 +0200)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Mon, 2 Apr 2018 11:34:09 +0000 (13:34 +0200)
When reconstructing the index, traverse backup volumes and set
the “volume” member on the objects appropriately.

deltatar/deltatar.py
deltatar/tarfile.py
testing/test_recover.py

index 221b7b3..f7e88c9 100644 (file)
@@ -1541,10 +1541,16 @@ class DeltaTar(object):
         files may be corrupt; skim files for header-like information and
         attempt to retrieve the data.
         """
-        backup_index = tarfile.gen_rescue_index(backup_tar_path,
-                                                self.mode,
-                                                password=self.password,
-                                                key=self.crypto_key)
+        def gen_volume_name (nvol):
+            return os.path.join (os.path.dirname (backup_tar_path),
+                                 self.volume_name_func (backup_tar_path,
+                                                        True,
+                                                        nvol))
+
+        backup_index = tarfile.gen_rescue_index (gen_volume_name,
+                                                 self.mode,
+                                                 password=self.password,
+                                                 key=self.crypto_key)
 
         return self.restore_backup(target_path,
                                    backup_index=backup_index,
index 31e46b9..b2de2d5 100644 (file)
@@ -2750,6 +2750,7 @@ class TarFile(object):
         '''
         Called by the user to change this tar file to point to a new volume.
         '''
+
         # open the file using either fileobj or name
         if not fileobj:
             if self.mode == "a" and not os.path.exists(name):
@@ -2766,7 +2767,8 @@ class TarFile(object):
                             fileobj=None,
                             bufsize=self.fileobj.bufsize,
                             encryption=encryption or self.fileobj.encryption,
-                            concat=self.fileobj.arcmode & ARCMODE_CONCAT)
+                            concat=self.fileobj.arcmode & ARCMODE_CONCAT,
+                            tolerance=self.fileobj.tolerance)
             else:
                 # here, we lose information about compression/encryption!
                 self._dbg(3, 'open_volume: builtin open')
@@ -3748,28 +3750,45 @@ def idxent_of_tarinfo (tarinfo):
         }
 
 
-def gen_rescue_index (backup_tar_path, mode, password=None, key=None):
+def gen_rescue_index (gen_volume_name, mode, maxvol=None, password=None, key=None):
+    infos   = []
     psidx   = [] # pseudo index, return value
     offsets = None
     secret  = crypto.make_secret (password=password, key=key)
 
-    if secret is not None:
-        offsets = crypto.reconstruct_offsets (backup_tar_path, secret)
-    elif mode == "#gz":
-        offsets = reconstruct_offsets_gz (backup_tar_path)
-    elif mode == "#":
-        offsets = reconstruct_offsets_tar (backup_tar_path)
-    else:
-        raise TarError ("no rescue handling for mode “%s”" % mode)
+    nvol = 0
 
-    fileobj = bltn_open (backup_tar_path, "rb")
-    infos   = [ (off, read_tarobj_at_offset (fileobj, off, mode, secret=secret))
-                for off in offsets ]
-    def aux (o, ti):
+    def aux (o, nvol, ti):
         ie = idxent_of_tarinfo (ti)
         ie ["offset"] = o
+        ie ["volume"] = nvol
         return ie
-    psidx   = [ aux (o, ti) for o, ti in infos ]
+
+    while True:
+        vpath = gen_volume_name (nvol)
+        try:
+            if secret is not None:
+                offsets = crypto.reconstruct_offsets (vpath, secret)
+            elif mode == "#gz":
+                offsets = reconstruct_offsets_gz (vpath)
+            elif mode == "#":
+                offsets = reconstruct_offsets_tar (vpath)
+            else:
+                raise TarError ("no rescue handling for mode “%s”" % mode)
+        except FileNotFoundError as exn:
+            # volume does not exist
+            if maxvol is not None and i < maxvol:
+                continue # explicit volume number specified, ignore missing ones
+            else:
+                break
+
+        fileobj = bltn_open (vpath, "rb")
+        infos  += [ (off, nvol, read_tarobj_at_offset (fileobj, off, mode,
+                                                       secret=secret))
+                    for off in offsets ]
+        nvol += 1
+
+    psidx   = [ aux (o, nvol, ti) for o, nvol, ti in infos ]
 
     return psidx
 
index a769d91..f2147dd 100644 (file)
@@ -545,7 +545,12 @@ class GenIndexTest (DefectiveTest):
             (source_path=self.src_path, backup_path=bak_path,
              max_volume_size=1)
 
-        psidx = tarfile.gen_rescue_index (backup_full, mode, password=self.PASSWORD)
+        def gen_volume_name (nvol):
+            return os.path.join (bak_path, vname (backup_full, True, nvol))
+
+        psidx = tarfile.gen_rescue_index (gen_volume_name,
+                                          mode,
+                                          password=self.PASSWORD)
 
         assert len (psidx) == len (self.hash)