bpo-32713: Fix tarfile.itn for large/negative float values. (GH-5434)
[python-delta-tar] / rescue_tar.py
index 906fc27..ca64725 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright (C) 2013 Intra2net AG
 #
 
 import argparse
 import os
+import sys
 import tempfile
 from functools import partial
 
 from deltatar import tarfile
+from deltatar import crypto
 import filesplit
 
-def rescue(tar_files, rescue_dir=None):
+def rescue(tar_files, rescue_dir=None, password=None):
     '''
     Rescues a multivolume tarfile. Checks file name extension to detect
     format (compression, etc). Assumes it to be multivolume tar.
@@ -50,10 +52,21 @@ def rescue(tar_files, rescue_dir=None):
 
     # autodetect file type by extension
     first_tar_file = tar_files[0]
+
+    mode   = "r"
+    decr = None
+    separator = None
     if first_tar_file.endswith(".tar.gz"):
         mode = "r#gz"
-    elif first_tar_file.endswith(".tar"):
-        mode = "r"
+        separator = tarfile.GZ_MAGIC_BYTES
+    elif first_tar_file.endswith(".tar.gz.pdtcrypt"):
+        if password is None:
+            print ("ERROR: tarball is encrypted but no password given",
+                   file=sys.stderr)
+            return -1
+        mode = "r#gz"
+        decr = crypto.Decrypt (password=password)
+        separator = crypto.PDTCRYPT_HDR_MAGIC
 
     base_name = os.path.basename(first_tar_file)
     extract_files = tar_files
@@ -74,10 +87,12 @@ def rescue(tar_files, rescue_dir=None):
             return open(path, 'wb')
         new_gz = partial(new_gz, context, extract_files)
 
-        # split in compressed chunks
+        # split in compressed or encrypted chunks, respectively
         for f in tar_files:
-            filesplit.split_file(b'\x1f\x8b',
-                os.path.join(rescue_dir, base_name), f, new_gz)
+            filesplit.split_file (separator,
+                                  os.path.join(rescue_dir, base_name),
+                                  f,
+                                  new_gz)
 
     # includes volumes already extracted with new_volume_handler
     already_extracted_vols = []
@@ -99,30 +114,47 @@ def rescue(tar_files, rescue_dir=None):
 
         volume_path = "%s.%d" % (base_name, next_num)
         already_extracted_vols.append(volume_path)
-        tarobj.open_volume(volume_path)
+        tarobj.open_volume(volume_path, encryption=decr)
 
     new_volume_handler = partial(new_volume_handler, already_extracted_vols)
 
     # extract files, as much as possible
+    errs = 0
     for f in extract_files:
         if f in already_extracted_vols:
             continue
         try:
-            tarobj = tarfile.TarFile.open(f, mode=mode,
-                new_volume_handler=new_volume_handler)
+            tarobj = tarfile.TarFile.open \
+                        (f,
+                         mode=mode,
+                         encryption=decr,
+                         new_volume_handler=new_volume_handler)
             tarobj.extractall()
             tarobj.close()
-        except:
-            pass
+        except Exception as exn:
+            print ("ERROR: error extracting file ā€œ%sā€ (%s)" % (f, exn),
+                   file=sys.stderr)
+            errs += 1
+
+    if errs > 0:
+        print ("ERROR: encountered %d errors extracting %s"
+               % (errs, first_tar_file), file=sys.stderr)
+        return -1
+
+    return 0
 
 if __name__ == "__main__":
     parser = argparse.ArgumentParser()
 
-    parser.add_argument("--rescue_dir", help="directory where rescue files "
+    parser.add_argument("--rescue-dir", help="directory where rescue files "
         "should be created. /tmp by default")
+    parser.add_argument("--password",
+                        help="password; mandatory for encrypted tarballs")
     parser.add_argument("tar_files", nargs="+", help="list of files of a "
         "multitar file to rescue. Assumes format first.extension "
         "second.extension.0 third.extension.1 ...")
 
     args = parser.parse_args()
-    rescue(tar_files=args.tar_files, rescue_dir=args.rescue_dir)
+    sys.exit (rescue (tar_files=args.tar_files,
+                      rescue_dir=args.rescue_dir,
+                      password=args.password))