Ensure files are closed when starting new volume.
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Tue, 19 Jul 2016 10:54:15 +0000 (12:54 +0200)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Thu, 12 Nov 2020 14:03:08 +0000 (15:03 +0100)
Move closing of old volume from handler to own code (handlers might
try to close again, should not be a problem).

Also ensure files are closed if volume handler fails.

deltatar/tarfile.py

index 1115373..fcb6dcc 100644 (file)
@@ -2729,7 +2729,6 @@ class TarFile(object):
                     raise Exception("We need to create a new volume and you "
                                     "didn't supply a new_volume_handler")
 
-
                 # the new volume handler should do everything needed to
                 # start working in a new volume. usually, the handler calls
                 # to self.open_volume
@@ -2741,9 +2740,29 @@ class TarFile(object):
                 tarinfo.volume_offset = tarinfo.size - source_size_left
                 self.volume_tarinfo = tarinfo
 
-                # the “new_volume_handler” is supposed to call .close() on the
-                # “fileobj” _Stream
-                self.new_volume_handler(self, self.base_name, self.volume_number)
+                # close current volume file (even if fileobj is external)
+                self.fileobj.close()
+
+                try:
+                    if not self.new_volume_handler or\
+                        not callable(self.new_volume_handler):
+
+                        # complain
+                        raise Exception("We need to create a new volume and "
+                                        "you didn't supply a "
+                                        "new_volume_handler")
+
+                    self.new_volume_handler(self, self.base_name,
+                                            self.volume_number)
+                except Exception:
+                    # close files
+                    if fileobj:
+                        fileobj.close()
+                    if self.fileobj:
+                        if not self._extfileobj:
+                            self.fileobj.close()
+                        self.closed = True
+                    raise
 
                 self.volume_tarinfo = None
 
@@ -3049,40 +3068,41 @@ class TarFile(object):
         source.seek(tarinfo.offset_data)
         decrypt = False
         iterate = True
-        target = bltn_open(targetpath, "wb")
+        target = None
+        try:
+            target = bltn_open(targetpath, "wb")
 
-        if tarinfo.sparse is not None:
-            try:
+            if tarinfo.sparse is not None:
                 for offset, size in tarinfo.sparse:
                     target.seek(offset)
                     copyfileobj(source, target, size)
                 target.seek(tarinfo.size)
                 target.truncate()
-            finally:
-                target.close()
                 return
 
-        while iterate:
-            iterate = False
-            try:
-                copyfileobj(source, target, tarinfo.size)
-            except OSError:
-                source.close()
-                # only if we are extracting a multivolume this can be treated
-                if not self.new_volume_handler:
-                    raise Exception("We need to read a new volume and you"
-                        " didn't supply a new_volume_handler")
-
-                # the new volume handler should do everything needed to
-                # start working in a new volume. usually, the handler calls
-                # to self.open_volume
-                self.volume_number += 1
-                self.new_volume_handler(self, self.base_name, self.volume_number)
-                tarinfo = self.firstmember
-                source = self.fileobj
-                iterate = True
-            finally:
-                if iterate is False: target.close()
+            while iterate:
+                iterate = False
+                try:
+                    copyfileobj(source, target, tarinfo.size)
+                except OSError:
+                    source.close()
+                    # only if we are extracting a multivolume this can be treated
+                    if not self.new_volume_handler:
+                        raise Exception("We need to read a new volume and you"
+                            " didn't supply a new_volume_handler")
+
+                    # the new volume handler should do everything needed to
+                    # start working in a new volume. usually, the handler calls
+                    # to self.open_volume
+                    self.volume_number += 1
+                    self.new_volume_handler(self, self.base_name, self.volume_number)
+                    tarinfo = self.firstmember
+                    source = self.fileobj
+                    iterate = True
+
+        finally:
+            if target:
+                target.close()
 
 
     def makeunknown(self, tarinfo, targetpath):