]> Dogcows Code - chaz/tar/blobdiff - src/sparse.c
tar: use ctime, not mtime, when checking placeholders
[chaz/tar] / src / sparse.c
index 0955248c492a9e75cb767edd81d17693cfde9bb0..67e5a857166df9f31c98644dd024a4423cf01520 100644 (file)
@@ -1,10 +1,10 @@
 /* Functions for dealing with sparse files
 
 /* Functions for dealing with sparse files
 
-   Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any later
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
    version.
 
    This program is distributed in the hope that it will be useful, but
@@ -22,6 +22,7 @@
 #include "common.h"
 
 struct tar_sparse_file;
 #include "common.h"
 
 struct tar_sparse_file;
+static bool sparse_select_optab (struct tar_sparse_file *file);
 
 enum sparse_scan_state
   {
 
 enum sparse_scan_state
   {
@@ -53,7 +54,7 @@ struct tar_sparse_file
   off_t dumped_size;                /* Number of bytes actually written
                                       to the archive */
   struct tar_stat_info *stat_info;  /* Information about the file */
   off_t dumped_size;                /* Number of bytes actually written
                                       to the archive */
   struct tar_stat_info *stat_info;  /* Information about the file */
-  struct tar_sparse_optab const *optab;
+  struct tar_sparse_optab const *optab; /* Operation table */
   void *closure;                    /* Any additional data optab calls might
                                       require */
 };
   void *closure;                    /* Any additional data optab calls might
                                       require */
 };
@@ -102,9 +103,14 @@ tar_sparse_member_p (struct tar_sparse_file *file)
 static bool
 tar_sparse_init (struct tar_sparse_file *file)
 {
 static bool
 tar_sparse_init (struct tar_sparse_file *file)
 {
-  file->dumped_size = 0;
+  memset (file, 0, sizeof *file);
+
+  if (!sparse_select_optab (file))
+    return false;
+
   if (file->optab->init)
     return file->optab->init (file);
   if (file->optab->init)
     return file->optab->init (file);
+
   return true;
 }
 
   return true;
 }
 
@@ -218,7 +224,7 @@ sparse_scan_file (struct tar_sparse_file *file)
     return false;
 
   st->archive_file_size = 0;
     return false;
 
   st->archive_file_size = 0;
-  
+
   if (!tar_sparse_scan (file, scan_begin, NULL))
     return false;
 
   if (!tar_sparse_scan (file, scan_begin, NULL))
     return false;
 
@@ -354,6 +360,7 @@ sparse_extract_region (struct tar_sparse_file *file, size_t i)
       count = full_write (file->fd, blk->buffer, wrbytes);
       write_size -= count;
       file->dumped_size += count;
       count = full_write (file->fd, blk->buffer, wrbytes);
       write_size -= count;
       file->dumped_size += count;
+      mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
       file->offset += count;
       if (count != wrbytes)
        {
       file->offset += count;
       if (count != wrbytes)
        {
@@ -372,16 +379,15 @@ enum dump_status
 sparse_dump_file (int fd, struct tar_stat_info *st)
 {
   bool rc;
 sparse_dump_file (int fd, struct tar_stat_info *st)
 {
   bool rc;
-  struct tar_sparse_file file = { 0, };
+  struct tar_sparse_file file;
+
+  if (!tar_sparse_init (&file))
+    return dump_status_not_implemented;
 
   file.stat_info = st;
   file.fd = fd;
   file.seekable = true; /* File *must* be seekable for dump to work */
 
 
   file.stat_info = st;
   file.fd = fd;
   file.seekable = true; /* File *must* be seekable for dump to work */
 
-  if (!sparse_select_optab (&file)
-      || !tar_sparse_init (&file))
-    return dump_status_not_implemented;
-
   rc = sparse_scan_file (&file);
   if (rc && file.optab->dump_region)
     {
   rc = sparse_scan_file (&file);
   if (rc && file.optab->dump_region)
     {
@@ -391,6 +397,9 @@ sparse_dump_file (int fd, struct tar_stat_info *st)
        {
          size_t i;
 
        {
          size_t i;
 
+         mv_begin_write (file.stat_info->file_name,
+                         file.stat_info->stat.st_size,
+                         file.stat_info->archive_file_size - file.dumped_size);
          for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
            rc = tar_sparse_dump_region (&file, i);
        }
          for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
            rc = tar_sparse_dump_region (&file, i);
        }
@@ -400,21 +409,12 @@ sparse_dump_file (int fd, struct tar_stat_info *st)
   return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
 }
 
   return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
 }
 
-/* Returns true if the file represented by stat is a sparse one */
-bool
-sparse_file_p (struct tar_stat_info *st)
-{
-  return (ST_NBLOCKS (st->stat)
-         < (st->stat.st_size / ST_NBLOCKSIZE
-            + (st->stat.st_size % ST_NBLOCKSIZE != 0)));
-}
-
 bool
 sparse_member_p (struct tar_stat_info *st)
 {
   struct tar_sparse_file file;
 
 bool
 sparse_member_p (struct tar_stat_info *st)
 {
   struct tar_sparse_file file;
 
-  if (!sparse_select_optab (&file))
+  if (!tar_sparse_init (&file))
     return false;
   file.stat_info = st;
   return tar_sparse_member_p (&file);
     return false;
   file.stat_info = st;
   return tar_sparse_member_p (&file);
@@ -425,7 +425,7 @@ sparse_fixup_header (struct tar_stat_info *st)
 {
   struct tar_sparse_file file;
 
 {
   struct tar_sparse_file file;
 
-  if (!sparse_select_optab (&file))
+  if (!tar_sparse_init (&file))
     return false;
   file.stat_info = st;
   return tar_sparse_fixup_header (&file);
     return false;
   file.stat_info = st;
   return tar_sparse_fixup_header (&file);
@@ -438,15 +438,14 @@ sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size)
   struct tar_sparse_file file;
   size_t i;
 
   struct tar_sparse_file file;
   size_t i;
 
+  if (!tar_sparse_init (&file))
+    return dump_status_not_implemented;
+
   file.stat_info = st;
   file.fd = fd;
   file.seekable = lseek (fd, 0, SEEK_SET) == 0;
   file.offset = 0;
 
   file.stat_info = st;
   file.fd = fd;
   file.seekable = lseek (fd, 0, SEEK_SET) == 0;
   file.offset = 0;
 
-  if (!sparse_select_optab (&file)
-      || !tar_sparse_init (&file))
-    return dump_status_not_implemented;
-
   rc = tar_sparse_decode_header (&file);
   for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
     rc = tar_sparse_extract_region (&file, i);
   rc = tar_sparse_decode_header (&file);
   for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
     rc = tar_sparse_extract_region (&file, i);
@@ -460,15 +459,14 @@ sparse_skip_file (struct tar_stat_info *st)
   bool rc = true;
   struct tar_sparse_file file;
 
   bool rc = true;
   struct tar_sparse_file file;
 
+  if (!tar_sparse_init (&file))
+    return dump_status_not_implemented;
+
   file.stat_info = st;
   file.fd = -1;
 
   file.stat_info = st;
   file.fd = -1;
 
-  if (!sparse_select_optab (&file)
-      || !tar_sparse_init (&file))
-    return dump_status_not_implemented;
-
   rc = tar_sparse_decode_header (&file);
   rc = tar_sparse_decode_header (&file);
-  skip_file (file.stat_info->archive_file_size);
+  skip_file (file.stat_info->archive_file_size - file.dumped_size);
   return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
 }
 
   return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
 }
 
@@ -515,6 +513,8 @@ check_data_region (struct tar_sparse_file *file, size_t i)
   if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
   size_left = file->stat_info->sparse_map[i].numbytes;
   if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
   size_left = file->stat_info->sparse_map[i].numbytes;
+  mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
+
   while (size_left > 0)
     {
       size_t bytes_read;
   while (size_left > 0)
     {
       size_t bytes_read;
@@ -540,6 +540,7 @@ check_data_region (struct tar_sparse_file *file, size_t i)
        }
       file->dumped_size += bytes_read;
       size_left -= bytes_read;
        }
       file->dumped_size += bytes_read;
       size_left -= bytes_read;
+      mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
       if (memcmp (blk->buffer, diff_buffer, rdsize))
        {
          report_difference (file->stat_info, _("Contents differ"));
       if (memcmp (blk->buffer, diff_buffer, rdsize))
        {
          report_difference (file->stat_info, _("Contents differ"));
@@ -557,25 +558,27 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
   size_t i;
   off_t offset = 0;
 
   size_t i;
   off_t offset = 0;
 
+  if (!tar_sparse_init (&file))
+    return dump_status_not_implemented;
+
   file.stat_info = st;
   file.fd = fd;
   file.stat_info = st;
   file.fd = fd;
-
-  if (!sparse_select_optab (&file)
-      || !tar_sparse_init (&file))
-    return dump_status_not_implemented;
+  file.seekable = true; /* File *must* be seekable for compare to work */
 
   rc = tar_sparse_decode_header (&file);
 
   rc = tar_sparse_decode_header (&file);
+  mv_begin_read (st);
   for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
     {
       rc = check_sparse_region (&file,
                                offset, file.stat_info->sparse_map[i].offset)
   for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
     {
       rc = check_sparse_region (&file,
                                offset, file.stat_info->sparse_map[i].offset)
-       && check_data_region (&file, i);
+           && check_data_region (&file, i);
       offset = file.stat_info->sparse_map[i].offset
                + file.stat_info->sparse_map[i].numbytes;
     }
 
   if (!rc)
     skip_file (file.stat_info->archive_file_size - file.dumped_size);
       offset = file.stat_info->sparse_map[i].offset
                + file.stat_info->sparse_map[i].numbytes;
     }
 
   if (!rc)
     skip_file (file.stat_info->archive_file_size - file.dumped_size);
+  mv_end ();
 
   tar_sparse_done (&file);
   return rc;
 
   tar_sparse_done (&file);
   return rc;
@@ -728,11 +731,9 @@ oldgnu_dump_header (struct tar_sparse_file *file)
       oldgnu_store_sparse_info (file, &i,
                                blk->sparse_header.sp,
                                SPARSES_IN_SPARSE_HEADER);
       oldgnu_store_sparse_info (file, &i,
                                blk->sparse_header.sp,
                                SPARSES_IN_SPARSE_HEADER);
-      set_next_block_after (blk);
       if (i < file->stat_info->sparse_map_avail)
        blk->sparse_header.isextended = 1;
       if (i < file->stat_info->sparse_map_avail)
        blk->sparse_header.isextended = 1;
-      else
-       break;
+      set_next_block_after (blk);
     }
   return true;
 }
     }
   return true;
 }
@@ -831,8 +832,12 @@ static struct tar_sparse_optab const star_optab = {
 };
 
 \f
 };
 
 \f
-/* GNU PAX sparse file format. The sparse file map is stored in
-   x header:
+/* GNU PAX sparse file format. There are several versions:
+
+   * 0.0
+
+   The initial version of sparse format used by tar 1.14-1.15.1.
+   The sparse file map is stored in x header:
 
    GNU.sparse.size      Real size of the stored file
    GNU.sparse.numblocks Number of blocks in the sparse map
 
    GNU.sparse.size      Real size of the stored file
    GNU.sparse.numblocks Number of blocks in the sparse map
@@ -840,34 +845,320 @@ static struct tar_sparse_optab const star_optab = {
      GNU.sparse.offset    Offset of the next data block
      GNU.sparse.numbytes  Size of the next data block
    end repeat
      GNU.sparse.offset    Offset of the next data block
      GNU.sparse.numbytes  Size of the next data block
    end repeat
+
+   This has been reported as conflicting with the POSIX specs. The reason is
+   that offsets and sizes of non-zero data blocks were stored in multiple
+   instances of GNU.sparse.offset/GNU.sparse.numbytes variables, whereas
+   POSIX requires the latest occurrence of the variable to override all
+   previous occurrences.
+
+   To avoid this incompatibility two following versions were introduced.
+
+   * 0.1
+
+   Used by tar 1.15.2 -- 1.15.91 (alpha releases).
+
+   The sparse file map is stored in
+   x header:
+
+   GNU.sparse.size      Real size of the stored file
+   GNU.sparse.numblocks Number of blocks in the sparse map
+   GNU.sparse.map       Map of non-null data chunks. A string consisting
+                       of comma-separated values "offset,size[,offset,size]..."
+
+   The resulting GNU.sparse.map string can be *very* long. While POSIX does not
+   impose any limit on the length of a x header variable, this can confuse some
+   tars.
+
+   * 1.0
+
+   Starting from this version, the exact sparse format version is specified
+   explicitely in the header using the following variables:
+
+   GNU.sparse.major     Major version
+   GNU.sparse.minor     Minor version
+
+   X header keeps the following variables:
+
+   GNU.sparse.name      Real file name of the sparse file
+   GNU.sparse.realsize  Real size of the stored file (corresponds to the old
+                        GNU.sparse.size variable)
+
+   The name field of the ustar header is constructed using the pattern
+   "%d/GNUSparseFile.%p/%f".
+
+   The sparse map itself is stored in the file data block, preceding the actual
+   file data. It consists of a series of octal numbers of arbitrary length,
+   delimited by newlines. The map is padded with nulls to the nearest block
+   boundary.
+
+   The first number gives the number of entries in the map. Following are map
+   entries, each one consisting of two numbers giving the offset and size of
+   the data block it describes.
+
+   The format is designed in such a way that non-posix aware tars and tars not
+   supporting GNU.sparse.* keywords will extract each sparse file in its
+   condensed form with the file map attached and will place it into a separate
+   directory. Then, using a simple program it would be possible to expand the
+   file to its original form even without GNU tar.
+
+   Bu default, v.1.0 archives are created. To use other formats,
+   --sparse-version option is provided. Additionally, v.0.0 can be obtained
+   by deleting GNU.sparse.map from 0.1 format: --sparse-version 0.1
+   --pax-option delete=GNU.sparse.map
 */
 
 static bool
 pax_sparse_member_p (struct tar_sparse_file *file)
 {
 */
 
 static bool
 pax_sparse_member_p (struct tar_sparse_file *file)
 {
-  return file->stat_info->sparse_map_avail > 0;
+  return file->stat_info->sparse_map_avail > 0
+          || file->stat_info->sparse_major > 0;
 }
 
 static bool
 }
 
 static bool
-pax_dump_header (struct tar_sparse_file *file)
+pax_dump_header_0 (struct tar_sparse_file *file)
 {
   off_t block_ordinal = current_block_ordinal ();
   union block *blk;
   size_t i;
 {
   off_t block_ordinal = current_block_ordinal ();
   union block *blk;
   size_t i;
+  char nbuf[UINTMAX_STRSIZE_BOUND];
+  struct sp_array *map = file->stat_info->sparse_map;
+  char *save_file_name = NULL;
 
   /* Store the real file size */
   xheader_store ("GNU.sparse.size", file->stat_info, NULL);
   xheader_store ("GNU.sparse.numblocks", file->stat_info, NULL);
 
   /* Store the real file size */
   xheader_store ("GNU.sparse.size", file->stat_info, NULL);
   xheader_store ("GNU.sparse.numblocks", file->stat_info, NULL);
+
+  if (xheader_keyword_deleted_p ("GNU.sparse.map")
+      || tar_sparse_minor == 0)
+    {
+      for (i = 0; i < file->stat_info->sparse_map_avail; i++)
+       {
+         xheader_store ("GNU.sparse.offset", file->stat_info, &i);
+         xheader_store ("GNU.sparse.numbytes", file->stat_info, &i);
+       }
+    }
+  else
+    {
+      xheader_store ("GNU.sparse.name", file->stat_info, NULL);
+      save_file_name = file->stat_info->file_name;
+      file->stat_info->file_name = xheader_format_name (file->stat_info,
+                                              "%d/GNUSparseFile.%p/%f", 0);
+
+      xheader_string_begin (&file->stat_info->xhdr);
+      for (i = 0; i < file->stat_info->sparse_map_avail; i++)
+       {
+         if (i)
+           xheader_string_add (&file->stat_info->xhdr, ",");
+         xheader_string_add (&file->stat_info->xhdr,
+                             umaxtostr (map[i].offset, nbuf));
+         xheader_string_add (&file->stat_info->xhdr, ",");
+         xheader_string_add (&file->stat_info->xhdr,
+                             umaxtostr (map[i].numbytes, nbuf));
+       }
+      if (!xheader_string_end (&file->stat_info->xhdr,
+                              "GNU.sparse.map"))
+       {
+         free (file->stat_info->file_name);
+         file->stat_info->file_name = save_file_name;
+         return false;
+       }
+    }
+  blk = start_header (file->stat_info);
+  /* Store the effective (shrunken) file size */
+  OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
+  finish_header (file->stat_info, blk, block_ordinal);
+  if (save_file_name)
+    {
+      free (file->stat_info->file_name);
+      file->stat_info->file_name = save_file_name;
+    }
+  return true;
+}
+
+static bool
+pax_dump_header_1 (struct tar_sparse_file *file)
+{
+  off_t block_ordinal = current_block_ordinal ();
+  union block *blk;
+  char *p, *q;
+  size_t i;
+  char nbuf[UINTMAX_STRSIZE_BOUND];
+  off_t size = 0;
+  struct sp_array *map = file->stat_info->sparse_map;
+  char *save_file_name = file->stat_info->file_name;
+
+#define COPY_STRING(b,dst,src) do                \
+ {                                               \
+   char *endp = b->buffer + BLOCKSIZE;           \
+   char const *srcp = src;                       \
+   while (*srcp)                                 \
+     {                                           \
+       if (dst == endp)                          \
+        {                                       \
+          set_next_block_after (b);             \
+          b = find_next_block ();               \
+           dst = b->buffer;                      \
+          endp = b->buffer + BLOCKSIZE;         \
+        }                                       \
+       *dst++ = *srcp++;                         \
+     }                                           \
+   } while (0)
+
+  /* Compute stored file size */
+  p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
+  size += strlen (p) + 1;
   for (i = 0; i < file->stat_info->sparse_map_avail; i++)
     {
   for (i = 0; i < file->stat_info->sparse_map_avail; i++)
     {
-      xheader_store ("GNU.sparse.offset", file->stat_info, &i);
-      xheader_store ("GNU.sparse.numbytes", file->stat_info, &i);
+      p = umaxtostr (map[i].offset, nbuf);
+      size += strlen (p) + 1;
+      p = umaxtostr (map[i].numbytes, nbuf);
+      size += strlen (p) + 1;
     }
     }
+  size = (size + BLOCKSIZE - 1) / BLOCKSIZE;
+  file->stat_info->archive_file_size += size * BLOCKSIZE;
+  file->dumped_size += size * BLOCKSIZE;
+
+  /* Store sparse file identification */
+  xheader_store ("GNU.sparse.major", file->stat_info, NULL);
+  xheader_store ("GNU.sparse.minor", file->stat_info, NULL);
+  xheader_store ("GNU.sparse.name", file->stat_info, NULL);
+  xheader_store ("GNU.sparse.realsize", file->stat_info, NULL);
+
+  file->stat_info->file_name = xheader_format_name (file->stat_info,
+                                           "%d/GNUSparseFile.%p/%f", 0);
 
   blk = start_header (file->stat_info);
   /* Store the effective (shrunken) file size */
   OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
   finish_header (file->stat_info, blk, block_ordinal);
 
   blk = start_header (file->stat_info);
   /* Store the effective (shrunken) file size */
   OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
   finish_header (file->stat_info, blk, block_ordinal);
+  free (file->stat_info->file_name);
+  file->stat_info->file_name = save_file_name;
+
+  blk = find_next_block ();
+  q = blk->buffer;
+  p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
+  COPY_STRING (blk, q, p);
+  COPY_STRING (blk, q, "\n");
+  for (i = 0; i < file->stat_info->sparse_map_avail; i++)
+    {
+      p = umaxtostr (map[i].offset, nbuf);
+      COPY_STRING (blk, q, p);
+      COPY_STRING (blk, q, "\n");
+      p = umaxtostr (map[i].numbytes, nbuf);
+      COPY_STRING (blk, q, p);
+      COPY_STRING (blk, q, "\n");
+    }
+  memset (q, 0, BLOCKSIZE - (q - blk->buffer));
+  set_next_block_after (blk);
+  return true;
+}
+
+static bool
+pax_dump_header (struct tar_sparse_file *file)
+{
+  file->stat_info->sparse_major = tar_sparse_major;
+  file->stat_info->sparse_minor = tar_sparse_minor;
+
+  return (file->stat_info->sparse_major == 0) ?
+           pax_dump_header_0 (file) : pax_dump_header_1 (file);
+}
+
+static bool
+decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
+{
+  uintmax_t u;
+  char *arg_lim;
+
+  if (!ISDIGIT (*arg))
+    return false;
+
+  u = strtoumax (arg, &arg_lim, 10);
+
+  if (! (u <= maxval && errno != ERANGE) || *arg_lim)
+    return false;
+
+  *num = u;
+  return true;
+}
+
+static bool
+pax_decode_header (struct tar_sparse_file *file)
+{
+  if (file->stat_info->sparse_major > 0)
+    {
+      uintmax_t u;
+      char nbuf[UINTMAX_STRSIZE_BOUND];
+      union block *blk;
+      char *p;
+      size_t i;
+
+#define COPY_BUF(b,buf,src) do                                     \
+ {                                                                 \
+   char *endp = b->buffer + BLOCKSIZE;                             \
+   char *dst = buf;                                                \
+   do                                                              \
+     {                                                             \
+       if (dst == buf + UINTMAX_STRSIZE_BOUND -1)                  \
+         {                                                         \
+           ERROR ((0, 0, _("%s: numeric overflow in sparse archive member"), \
+                 file->stat_info->orig_file_name));               \
+           return false;                                           \
+         }                                                         \
+       if (src == endp)                                            \
+        {                                                         \
+          set_next_block_after (b);                               \
+           file->dumped_size += BLOCKSIZE;                         \
+           b = find_next_block ();                                 \
+           src = b->buffer;                                        \
+          endp = b->buffer + BLOCKSIZE;                           \
+        }                                                         \
+       *dst = *src++;                                              \
+     }                                                             \
+   while (*dst++ != '\n');                                         \
+   dst[-1] = 0;                                                    \
+ } while (0)
+
+      set_next_block_after (current_header);
+      file->dumped_size += BLOCKSIZE;
+      blk = find_next_block ();
+      p = blk->buffer;
+      COPY_BUF (blk,nbuf,p);
+      if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
+       {
+         ERROR ((0, 0, _("%s: malformed sparse archive member"),
+                 file->stat_info->orig_file_name));
+         return false;
+       }
+      file->stat_info->sparse_map_size = u;
+      file->stat_info->sparse_map = xcalloc (file->stat_info->sparse_map_size,
+                                            sizeof (*file->stat_info->sparse_map));
+      file->stat_info->sparse_map_avail = 0;
+      for (i = 0; i < file->stat_info->sparse_map_size; i++)
+       {
+         struct sp_array sp;
+
+         COPY_BUF (blk,nbuf,p);
+         if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
+           {
+             ERROR ((0, 0, _("%s: malformed sparse archive member"),
+                     file->stat_info->orig_file_name));
+             return false;
+           }
+         sp.offset = u;
+         COPY_BUF (blk,nbuf,p);
+         if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
+           {
+             ERROR ((0, 0, _("%s: malformed sparse archive member"),
+                     file->stat_info->orig_file_name));
+             return false;
+           }
+         sp.numbytes = u;
+         sparse_add_map (file->stat_info, &sp);
+       }
+      set_next_block_after (blk);
+    }
+
   return true;
 }
 
   return true;
 }
 
@@ -876,8 +1167,8 @@ static struct tar_sparse_optab const pax_optab = {
   NULL,  /* No done function */
   pax_sparse_member_p,
   pax_dump_header,
   NULL,  /* No done function */
   pax_sparse_member_p,
   pax_dump_header,
-  NULL,  /* No decode_header function */
-  NULL,  /* No fixup_header function */
+  NULL,
+  pax_decode_header,
   NULL,  /* No scan_block function */
   sparse_dump_region,
   sparse_extract_region,
   NULL,  /* No scan_block function */
   sparse_dump_region,
   sparse_extract_region,
This page took 0.035244 seconds and 4 git commands to generate.