fixing remaining issues with PAX compress/extract
authorEduardo Robles Elvira <edulix@wadobo.com>
Fri, 21 Jun 2013 11:52:41 +0000 (13:52 +0200)
committerEduardo Robles Elvira <edulix@wadobo.com>
Fri, 21 Jun 2013 11:52:41 +0000 (13:52 +0200)
deltatar/tarfile.py

index 657404e..4a93b80 100644 (file)
@@ -939,6 +939,8 @@ class TarInfo(object):
 
         self.offset = 0         # the tar header starts here
         self.offset_data = 0    # the file's data starts here
+        self.volume_offset = 0  # the file's data corresponds with the data
+                                # starting at this position
 
         self.pax_headers = {}   # pax header information
 
@@ -976,7 +978,8 @@ class TarInfo(object):
             "gname":    self.gname,
             "devmajor": self.devmajor,
             "devminor": self.devminor,
-            "offset_data": self.offset_data
+            "offset_data": self.offset_data,
+            "volume_offset": self.volume_offset
         }
 
         if info["type"] == DIRTYPE and not info["name"].endswith("/"):
@@ -1024,11 +1027,11 @@ class TarInfo(object):
             prefix = [
                 itn(info.get("atime", 0), 12, GNU_FORMAT),
                 itn(info.get("ctime", 0), 12, GNU_FORMAT),
-                itn(self.offset_data, 12, GNU_FORMAT),
+                itn(self.volume_offset, 12, GNU_FORMAT),
                 itn(0, 119, GNU_FORMAT), # stuff unused in this tar implementation, set to zero
             ]
             info['prefix'] = "".join(prefix)
-            info['size'] = info['size'] - self.offset_data
+            info['size'] = info['size'] - self.volume_offset
 
         buf = ""
         if len(info["linkname"]) > LENGTH_LINK:
@@ -1071,11 +1074,6 @@ class TarInfo(object):
             if len(info[name]) > length:
                 pax_headers[hname] = val
 
-        if self.ismultivol():
-            pax_headers["GNU.volume.filename"] = unicode(self.name)
-            pax_headers["GNU.volume.size"] = unicode(info['size'] - self.offset_data)
-            pax_headers["GNU.volume.offset"] = unicode(self.offset_data)
-
         # Test number fields for values that exceed the field limit or values
         # that like to be stored as float.
         for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)):
@@ -1425,13 +1423,6 @@ class TarInfo(object):
         except HeaderError:
             raise SubsequentHeaderError("missing or bad subsequent header")
 
-        if next and next.type == GNUTYPE_MULTIVOL:
-            if "GNU.volume.filename" in pax_headers:
-                next.name = pax_headers["GNU.volume.filename"]
-            if "GNU.volume.size" in pax_headers:
-                next.size = int(pax_headers["GNU.volume.size"])
-            #if "GNU.volume.offset" in pax_headers:
-                #next.offset_data = int(pax_headers["GNU.volume.offset"])
 
         if self.type in (XHDTYPE, SOLARIS_XHDTYPE):
             # Patch the TarInfo object with the extended header info.
@@ -1447,6 +1438,13 @@ 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"])
+
         return next
 
     def _apply_pax_info(self, pax_headers, encoding, errors):
@@ -1502,7 +1500,7 @@ class TarInfo(object):
     def isdev(self):
         return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE)
     def ismultivol(self):
-        return self.type == GNUTYPE_MULTIVOL or 'GNU.volume.offset' in self.pax_headers
+        return self.type == GNUTYPE_MULTIVOL or self.volume_offset > 0
 # class TarInfo
 
 class TarFile(object):
@@ -2085,14 +2083,14 @@ class TarFile(object):
         # handle multivolume support
         if self.max_volume_size:
             size_left = self._size_left()
-            max_size_to_write = min(size_left, tarinfo.size - tarinfo.offset_data)
+            max_size_to_write = min(size_left, tarinfo.size - tarinfo.volume_offset)
         else:
             size_left = max_size_to_write = tarinfo.size
 
         data_written = 0
 
         # iterate, one iteration per volume (usually only one volume)
-        while tarinfo.offset_data < tarinfo.size:
+        while tarinfo.volume_offset < tarinfo.size:
             copyfileobj(fileobj, self.fileobj, max_size_to_write)
             blocks, remainder = divmod(max_size_to_write, BLOCKSIZE)
 
@@ -2112,7 +2110,7 @@ class TarFile(object):
 
             self.offset += blocks * BLOCKSIZE
             size_left -= blocks * BLOCKSIZE
-            tarinfo.offset_data += blocks * BLOCKSIZE
+            tarinfo.volume_offset += blocks * BLOCKSIZE
 
             # check if creating a new volume is needed
             if self.max_volume_size and size_left < BLOCKSIZE:
@@ -2127,20 +2125,27 @@ class TarFile(object):
                 # start working in a new volume. usually, the handler calls
                 # to self.open_volume
                 self.volume_number += 1
+
+                # set to be used by open_volume, becuase in the case of a PAX
+                # tar it needs to write information about the volume and offset
+                # in the global header
+                self.volume_tarinfo = tarinfo
                 self.new_volume_handler(self, self.base_name, self.volume_number)
 
+                self.volume_tarinfo = None
+
                 # write new volume header
                 buf = tarinfo.tobuf(self.format, self.encoding, self.errors)
                 self.offset += len(buf)
                 self.fileobj.write(buf)
                 size_left = self._size_left()
-                max_size_to_write = min(size_left, tarinfo.size - tarinfo.offset_data)
+                max_size_to_write = min(size_left, tarinfo.size - tarinfo.volume_offset)
 
         self.members.append(tarinfo)
 
     def open_volume(self, name="", fileobj=None):
         '''
-        Called by the user to change this tar file to point to a new volume
+        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:
@@ -2190,10 +2195,13 @@ class TarFile(object):
             if self.mode in "aw":
                 self._loaded = True
 
-                if self.pax_headers:
-                    buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy())
-                    self.fileobj.write(buf)
-                    self.offset += len(buf)
+                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)
+
+                buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy())
+                self.fileobj.write(buf)
+                self.offset += len(buf)
         except:
             if not self._extfileobj:
                 self.fileobj.close()