fixing multivolume related bugs
authorEduardo Robles Elvira <edulix@wadobo.com>
Wed, 26 Jun 2013 08:51:04 +0000 (10:51 +0200)
committerEduardo Robles Elvira <edulix@wadobo.com>
Wed, 26 Jun 2013 08:55:52 +0000 (10:55 +0200)
deltatar/tarfile.py

index e95755d..5d356e3 100644 (file)
@@ -1049,6 +1049,8 @@ class TarInfo(object):
         """
         info["magic"] = POSIX_MAGIC
         pax_headers = self.pax_headers.copy()
+        if self.ismultivol():
+            info['size'] = info['size'] - self.volume_offset
 
         # Test string fields for values that exceed the field length or cannot
         # be represented in ASCII encoding.
@@ -1423,7 +1425,6 @@ class TarInfo(object):
         except HeaderError:
             raise SubsequentHeaderError("missing or bad subsequent header")
 
-
         if self.type in (XHDTYPE, SOLARIS_XHDTYPE):
             # Patch the TarInfo object with the extended header info.
             next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors)
@@ -1438,12 +1439,17 @@ class TarInfo(object):
                     offset += next._block(next.size)
                 tarfile.offset = offset
 
-            if "GNU.volume.filename" in pax_headers and\
-                pax_headers["GNU.volume.filename"] == next.name:
-                if "GNU.volume.size" in pax_headers:
-                    next.size = int(pax_headers["GNU.volume.size"])
-                if "GNU.volume.offset" in pax_headers:
-                    next.volume_offset = int(pax_headers["GNU.volume.offset"])
+        if next is not None:
+            if "GNU.volume.filename" in pax_headers:
+                if pax_headers["GNU.volume.filename"] == next.name:
+                    if "GNU.volume.size" in pax_headers:
+                        next.size = int(pax_headers["GNU.volume.size"])
+                    if "GNU.volume.offset" in pax_headers:
+                        next.volume_offset = int(pax_headers["GNU.volume.offset"])
+
+                for key in pax_headers.keys():
+                    if key.startswith("GNU.volume"):
+                        del tarfile.pax_headers[key]
 
         return next
 
@@ -1500,7 +1506,8 @@ class TarInfo(object):
     def isdev(self):
         return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE)
     def ismultivol(self):
-        return self.type == GNUTYPE_MULTIVOL or self.volume_offset > 0
+        return self.type == GNUTYPE_MULTIVOL or self.volume_offset > 0 or\
+            "GNU.volume.offset" in self.pax_headers
 # class TarInfo
 
 class TarFile(object):
@@ -2083,6 +2090,10 @@ class TarFile(object):
         # handle multivolume support
         if self.max_volume_size:
             size_left = self._size_left()
+            # we only split volumes in the middle of a file, that means we have
+            # to write at least one block
+            if size_left < BLOCKSIZE:
+                size_left = BLOCKSIZE
             max_size_to_write = min(size_left, tarinfo.size - tarinfo.volume_offset)
         else:
             size_left = max_size_to_write = tarinfo.size
@@ -2106,18 +2117,21 @@ class TarFile(object):
             if self.max_volume_size and max_size_to_write == size_left:
                 assert remainder == 0
 
+
             self.offset += blocks * BLOCKSIZE
             size_left -= blocks * BLOCKSIZE
             tarinfo.volume_offset += blocks * BLOCKSIZE
 
             # check if creating a new volume is needed
-            if self.max_volume_size and size_left < BLOCKSIZE:
+            if tarinfo.volume_offset < tarinfo.size and\
+                self.max_volume_size and size_left < 3*BLOCKSIZE:
+
                 tarinfo.type = GNUTYPE_MULTIVOL
 
                 if not self.new_volume_handler or\
                     not callable(self.new_volume_handler):
-                    raise Exception("We need to create a new volume and you"
-                        " didn't supply a new_volume_handler")
+                    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
@@ -2193,13 +2207,18 @@ class TarFile(object):
             if self.mode in "aw":
                 self._loaded = True
 
-                self.pax_headers["GNU.volume.filename"] = unicode(self.volume_tarinfo.name)
-                self.pax_headers["GNU.volume.size"] = unicode(self.volume_tarinfo.size - self.volume_tarinfo.volume_offset)
-                self.pax_headers["GNU.volume.offset"] = unicode(self.volume_tarinfo.volume_offset)
+                if  self.format == PAX_FORMAT:
+                    volume_info = {
+                        "GNU.volume.filename": unicode(self.volume_tarinfo.name),
+                        "GNU.volume.size": unicode(self.volume_tarinfo.size - self.volume_tarinfo.volume_offset),
+                        "GNU.volume.offset": unicode(self.volume_tarinfo.volume_offset),
+                    }
 
-                buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy())
-                self.fileobj.write(buf)
-                self.offset += len(buf)
+                    self.pax_headers.update(volume_info)
+
+                    buf = self.tarinfo.create_pax_global_header(volume_info.copy())
+                    self.fileobj.write(buf)
+                    self.offset += len(buf)
         except:
             if not self._extfileobj:
                 self.fileobj.close()