Deal with partially extracted tarballs. trunk

Thu, 05 Jun 2008 21:24:44 -0400

author
Brett Smith <brett@brettcsmith.org>
date
Thu, 05 Jun 2008 21:24:44 -0400
branch
trunk
changeset 62
17d845dacff5
parent 61
23eb5e0a6665
child 63
0b0bb1608b37

Deal with partially extracted tarballs.

If you can only extract part of a tarball -- for example, because it
includes nodes and you're not root -- tar will extract what it can but give
an error code. So now dtrx will only fail if some subprogram threw an
error code *and* nothing actually got extracted. Otherwise, we'll show
whatever errors came up, but we'll still assume the extraction was as
successful as it's going to be.

scripts/dtrx file | annotate | diff | comparison | revisions
tests/test-tar-with-node.tar.gz file | annotate | diff | comparison | revisions
tests/tests.yml file | annotate | diff | comparison | revisions
--- a/scripts/dtrx	Thu Jun 05 10:00:38 2008 -0400
+++ b/scripts/dtrx	Thu Jun 05 21:24:44 2008 -0400
@@ -138,6 +138,8 @@
         self.content_type = None
         self.content_name = None
         self.pipes = []
+        self.stderr = tempfile.TemporaryFile()
+        self.exit_codes = []
         try:
             self.archive = open(filename, 'r')
         except (IOError, OSError), error:
@@ -150,6 +152,12 @@
     def pipe(self, command, description="extraction"):
         self.pipes.append((command, description))
 
+    def first_bad_exit_code(self):
+        for index, code in enumerate(self.exit_codes):
+            if code != 0:
+                return index
+        return None
+
     def run_pipes(self, final_stdout=None):
         if not self.pipes:
             return
@@ -171,23 +179,17 @@
             try:
                 processes.append(subprocess.Popen(command, stdin=stdin,
                                                   stdout=stdout,
-                                                  stderr=subprocess.PIPE))
+                                                  stderr=self.stderr))
             except OSError, error:
                 if error.errno == errno.ENOENT:
                     raise ExtractorUnusable("could not run %s" % (command[0],))
                 raise
-        exit_codes = [pipe.wait() for pipe in processes]
+        self.exit_codes = [pipe.wait() for pipe in processes]
         self.archive.close()
         for index in range(last_pipe):
             processes[index].stdout.close()
-            processes[index].stderr.close()
-        for index, status in enumerate(exit_codes):
-            if status != 0:
-                raise ExtractorError("%s error: '%s' returned status code %s" %
-                                     (self.pipes[index][1],
-                                      ' '.join(self.pipes[index][0]), status))
         self.archive = final_stdout
-    
+
     def prepare(self):
         pass
 
@@ -232,6 +234,19 @@
             pieces.pop()
         return '.'.join(pieces)
 
+    def check_success(self, got_output):
+        self.stderr.seek(0, 0)
+        if self.stderr.read(1):
+            self.stderr.seek(0, 0)
+            logger.warning(self.stderr.read(-1))
+        self.stderr.close()
+        error_index = self.first_bad_exit_code()
+        if (not got_output) and (error_index is not None):
+            command = ' '.join(self.pipes[error_index][0])
+            raise ExtractorError("%s error: '%s' returned status code %s" %
+                                 (self.pipes[error_index][1], command,
+                                  self.exit_codes[error_index]))
+        
     def extract(self):
         try:
             self.target = tempfile.mkdtemp(prefix='.dtrx-', dir='.')
@@ -243,6 +258,7 @@
             self.archive.seek(0, 0)
             self.extract_archive()
             self.check_contents()
+            self.check_success(self.content_type != EMPTY)
         except EXTRACTION_ERRORS:
             self.archive.close()
             os.chdir(old_path)
@@ -285,15 +301,14 @@
             output_fd, self.target = tempfile.mkstemp(prefix='.dtrx-', dir='.')
         except (OSError, IOError), error:
             raise ExtractorError("cannot extract here: %s" % (error.strerror,))
+        self.run_pipes(output_fd)
+        os.close(output_fd)
         try:
-            self.run_pipes(output_fd)
+            self.check_success(os.stat(self.target)[stat.ST_SIZE] > 0)
         except EXTRACTION_ERRORS:
-            os.close(output_fd)
             os.unlink(self.target)
             raise
-        os.close(output_fd)
-        
-
+            
 class TarExtractor(BaseExtractor):
     file_type = 'tar file'
 
Binary file tests/test-tar-with-node.tar.gz has changed
--- a/tests/tests.yml	Thu Jun 05 10:00:38 2008 -0400
+++ b/tests/tests.yml	Thu Jun 05 21:24:44 2008 -0400
@@ -573,3 +573,12 @@
     l
     n
   grep: "^test-deep-recursion/subsubdir/test-text\.gz$"
+
+- name: partly failed extraction
+  options: -n
+  filenames: test-tar-with-node.tar.gz
+  baseline: |
+    mkdir test-tar-with-node
+    cd test-tar-with-node
+    tar -zxf ../$1
+  grep: Cannot mknod

mercurial