]> Dogcows Code - chaz/tar/blobdiff - src/sparse.c
Use TAR_VOLUME instead of reading volno file.
[chaz/tar] / src / sparse.c
index b43f3d78ad4a7b0e30752617d829135dc420c62f..30bf03157c962c09f51fc8b8be07eb6f287e1000 100644 (file)
 
    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
 
    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
-   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 #include <system.h>
 
 #include <system.h>
+#include <inttostr.h>
 #include <quotearg.h>
 #include "common.h"
 
 struct tar_sparse_file;
 #include <quotearg.h>
 #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
   {
@@ -47,47 +49,47 @@ struct tar_sparse_file
 {
   int fd;                           /* File descriptor */
   bool seekable;                    /* Is fd seekable? */
 {
   int fd;                           /* File descriptor */
   bool seekable;                    /* Is fd seekable? */
-  size_t offset;                    /* Current offset in fd if seekable==false.
+  off_t offset;                     /* Current offset in fd if seekable==false.
                                       Otherwise unused */
                                       Otherwise unused */
-  size_t dumped_size;               /* Number of bytes actually written
+  off_t dumped_size;                /* Number of bytes actually written
                                       to the archive */
   struct tar_stat_info *stat_info;  /* Information about the file */
                                       to the archive */
   struct tar_stat_info *stat_info;  /* Information about the file */
-  struct tar_sparse_optab *optab;
+  struct tar_sparse_optab const *optab; /* Operation table */
   void *closure;                    /* Any additional data optab calls might
   void *closure;                    /* Any additional data optab calls might
-                                      reqiure */
+                                      require */
 };
 
 /* Dump zeros to file->fd until offset is reached. It is used instead of
    lseek if the output file is not seekable */
 };
 
 /* Dump zeros to file->fd until offset is reached. It is used instead of
    lseek if the output file is not seekable */
-static long
+static bool
 dump_zeros (struct tar_sparse_file *file, off_t offset)
 {
 dump_zeros (struct tar_sparse_file *file, off_t offset)
 {
-  char buf[BLOCKSIZE];
-  
-  if (offset - file->offset < 0)
+  static char const zero_buf[BLOCKSIZE];
+
+  if (offset < file->offset)
     {
       errno = EINVAL;
     {
       errno = EINVAL;
-      return -1;
+      return false;
     }
 
     }
 
-  memset (buf, 0, sizeof buf);
   while (file->offset < offset)
     {
   while (file->offset < offset)
     {
-      size_t size = offset - file->offset;
-      size_t wrbytes;
-      
-      if (size > sizeof buf)
-       size = sizeof buf;
-      wrbytes = write (file->fd, buf, size);
+      size_t size = (BLOCKSIZE < offset - file->offset
+                    ? BLOCKSIZE
+                    : offset - file->offset);
+      ssize_t wrbytes;
+
+      wrbytes = write (file->fd, zero_buf, size);
       if (wrbytes <= 0)
        {
          if (wrbytes == 0)
            errno = EINVAL;
       if (wrbytes <= 0)
        {
          if (wrbytes == 0)
            errno = EINVAL;
-         return -1;
+         return false;
        }
       file->offset += wrbytes;
     }
        }
       file->offset += wrbytes;
     }
-  return file->offset;
+
+  return true;
 }
 
 static bool
 }
 
 static bool
@@ -101,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;
 }
 
@@ -168,14 +175,9 @@ tar_sparse_fixup_header (struct tar_sparse_file *file)
 static bool
 lseek_or_error (struct tar_sparse_file *file, off_t offset)
 {
 static bool
 lseek_or_error (struct tar_sparse_file *file, off_t offset)
 {
-  off_t off;
-
-  if (file->seekable)
-    off = lseek (file->fd, offset, SEEK_SET);
-  else
-    off = dump_zeros (file, offset);
-  
-  if (off < 0)
+  if (file->seekable
+      ? lseek (file->fd, offset, SEEK_SET) < 0
+      : ! dump_zeros (file, offset))
     {
       seek_diag_details (file->stat_info->orig_file_name, offset);
       return false;
     {
       seek_diag_details (file->stat_info->orig_file_name, offset);
       return false;
@@ -187,7 +189,7 @@ lseek_or_error (struct tar_sparse_file *file, off_t offset)
    it's made *entirely* of zeros, returning a 0 the instant it finds
    something that is a nonzero, i.e., useful data.  */
 static bool
    it's made *entirely* of zeros, returning a 0 the instant it finds
    something that is a nonzero, i.e., useful data.  */
 static bool
-zero_block_p (char *buffer, size_t size)
+zero_block_p (char const *buffer, size_t size)
 {
   while (size--)
     if (*buffer++)
 {
   while (size--)
     if (*buffer++)
@@ -195,58 +197,46 @@ zero_block_p (char *buffer, size_t size)
   return true;
 }
 
   return true;
 }
 
-#define clear_block(p) memset (p, 0, BLOCKSIZE);
-
-#define SPARSES_INIT_COUNT SPARSES_IN_SPARSE_HEADER
-
 static void
 static void
-sparse_add_map (struct tar_sparse_file *file, struct sp_array *sp)
+sparse_add_map (struct tar_stat_info *st, struct sp_array const *sp)
 {
 {
-  if (file->stat_info->sparse_map == NULL)
-    {
-      file->stat_info->sparse_map =
-       xmalloc (SPARSES_INIT_COUNT * sizeof file->stat_info->sparse_map[0]);
-      file->stat_info->sparse_map_size = SPARSES_INIT_COUNT;
-    }
-  else if (file->stat_info->sparse_map_avail == file->stat_info->sparse_map_size)
-    {
-      file->stat_info->sparse_map_size *= 2;
-      file->stat_info->sparse_map =
-       xrealloc (file->stat_info->sparse_map,
-                 file->stat_info->sparse_map_size
-                 * sizeof file->stat_info->sparse_map[0]);
-    }
-  file->stat_info->sparse_map[file->stat_info->sparse_map_avail++] = *sp;
+  struct sp_array *sparse_map = st->sparse_map;
+  size_t avail = st->sparse_map_avail;
+  if (avail == st->sparse_map_size)
+    st->sparse_map = sparse_map =
+      x2nrealloc (sparse_map, &st->sparse_map_size, sizeof *sparse_map);
+  sparse_map[avail] = *sp;
+  st->sparse_map_avail = avail + 1;
 }
 
 /* Scan the sparse file and create its map */
 static bool
 sparse_scan_file (struct tar_sparse_file *file)
 {
 }
 
 /* Scan the sparse file and create its map */
 static bool
 sparse_scan_file (struct tar_sparse_file *file)
 {
-  static char buffer[BLOCKSIZE];
+  struct tar_stat_info *st = file->stat_info;
+  int fd = file->fd;
+  char buffer[BLOCKSIZE];
   size_t count;
   size_t count;
-  size_t offset = 0;
+  off_t offset = 0;
   struct sp_array sp = {0, 0};
 
   if (!lseek_or_error (file, 0))
     return false;
   struct sp_array sp = {0, 0};
 
   if (!lseek_or_error (file, 0))
     return false;
-  clear_block (buffer);
-
-  file->stat_info->sparse_map_avail = 0;
-  file->stat_info->archive_file_size = 0;
 
 
+  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;
 
-  while ((count = safe_read (file->fd, buffer, sizeof buffer)) != 0
+  while ((count = safe_read (fd, buffer, sizeof buffer)) != 0
         && count != SAFE_READ_ERROR)
     {
         && count != SAFE_READ_ERROR)
     {
-      /* Analize the block */
+      /* Analyze the block.  */
       if (zero_block_p (buffer, count))
        {
          if (sp.numbytes)
            {
       if (zero_block_p (buffer, count))
        {
          if (sp.numbytes)
            {
-             sparse_add_map (file, &sp);
+             sparse_add_map (st, &sp);
              sp.numbytes = 0;
              if (!tar_sparse_scan (file, scan_block, NULL))
                return false;
              sp.numbytes = 0;
              if (!tar_sparse_scan (file, scan_block, NULL))
                return false;
@@ -257,26 +247,25 @@ sparse_scan_file (struct tar_sparse_file *file)
          if (sp.numbytes == 0)
            sp.offset = offset;
          sp.numbytes += count;
          if (sp.numbytes == 0)
            sp.offset = offset;
          sp.numbytes += count;
-         file->stat_info->archive_file_size += count;
+         st->archive_file_size += count;
          if (!tar_sparse_scan (file, scan_block, buffer))
            return false;
        }
 
       offset += count;
          if (!tar_sparse_scan (file, scan_block, buffer))
            return false;
        }
 
       offset += count;
-      clear_block (buffer);
     }
 
   if (sp.numbytes == 0)
     sp.offset = offset;
 
     }
 
   if (sp.numbytes == 0)
     sp.offset = offset;
 
-  sparse_add_map (file, &sp);
-  file->stat_info->archive_file_size += count;
+  sparse_add_map (st, &sp);
+  st->archive_file_size += count;
   return tar_sparse_scan (file, scan_end, NULL);
 }
 
   return tar_sparse_scan (file, scan_end, NULL);
 }
 
-static struct tar_sparse_optab oldgnu_optab;
-static struct tar_sparse_optab star_optab;
-static struct tar_sparse_optab pax_optab;
+static struct tar_sparse_optab const oldgnu_optab;
+static struct tar_sparse_optab const star_optab;
+static struct tar_sparse_optab const pax_optab;
 
 static bool
 sparse_select_optab (struct tar_sparse_file *file)
 
 static bool
 sparse_select_optab (struct tar_sparse_file *file)
@@ -321,20 +310,21 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i)
       size_t bytes_read;
 
       blk = find_next_block ();
       size_t bytes_read;
 
       blk = find_next_block ();
-      memset (blk->buffer, 0, BLOCKSIZE);
       bytes_read = safe_read (file->fd, blk->buffer, bufsize);
       if (bytes_read == SAFE_READ_ERROR)
        {
           read_diag_details (file->stat_info->orig_file_name,
       bytes_read = safe_read (file->fd, blk->buffer, bufsize);
       if (bytes_read == SAFE_READ_ERROR)
        {
           read_diag_details (file->stat_info->orig_file_name,
-                            file->stat_info->sparse_map[i].offset
-                                + file->stat_info->sparse_map[i].numbytes
-                                - bytes_left,
-                    bufsize);
+                            (file->stat_info->sparse_map[i].offset
+                             + file->stat_info->sparse_map[i].numbytes
+                             - bytes_left),
+                            bufsize);
          return false;
        }
 
          return false;
        }
 
+      memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read);
       bytes_left -= bytes_read;
       file->dumped_size += bytes_read;
       bytes_left -= bytes_read;
       file->dumped_size += bytes_read;
+      mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
       set_next_block_after (blk);
     }
 
       set_next_block_after (blk);
     }
 
@@ -371,6 +361,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)
        {
@@ -391,14 +382,12 @@ sparse_dump_file (int fd, struct tar_stat_info *st)
   bool rc;
   struct tar_sparse_file file;
 
   bool rc;
   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 */
-  file.offset = 0;
-  
-  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)
@@ -409,12 +398,14 @@ sparse_dump_file (int fd, struct tar_stat_info *st)
        {
          size_t i;
 
        {
          size_t i;
 
+         mv_begin (file.stat_info);
          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);
+         mv_end ();
        }
     }
 
        }
     }
 
-  pad_archive(file.stat_info->archive_file_size - file.dumped_size);
+  pad_archive (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;
 }
 
@@ -432,7 +423,7 @@ sparse_member_p (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_member_p (&file);
     return false;
   file.stat_info = st;
   return tar_sparse_member_p (&file);
@@ -443,7 +434,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);
@@ -456,14 +447,13 @@ 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_decode_header (&file);
   for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
@@ -478,21 +468,18 @@ 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);
   skip_file (file.stat_info->archive_file_size);
   return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
 }
 
 \f
   rc = tar_sparse_decode_header (&file);
   skip_file (file.stat_info->archive_file_size);
   return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
 }
 
 \f
-static char diff_buffer[BLOCKSIZE];
-
 static bool
 check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
 {
 static bool
 check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
 {
@@ -502,11 +489,9 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
   while (beg < end)
     {
       size_t bytes_read;
   while (beg < end)
     {
       size_t bytes_read;
-      size_t rdsize = end - beg;
+      size_t rdsize = BLOCKSIZE < end - beg ? BLOCKSIZE : end - beg;
+      char diff_buffer[BLOCKSIZE];
 
 
-      if (rdsize > BLOCKSIZE)
-       rdsize = BLOCKSIZE;
-      clear_block (diff_buffer);
       bytes_read = safe_read (file->fd, diff_buffer, rdsize);
       if (bytes_read == SAFE_READ_ERROR)
        {
       bytes_read = safe_read (file->fd, diff_buffer, rdsize);
       if (bytes_read == SAFE_READ_ERROR)
        {
@@ -517,8 +502,10 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
        }
       if (!zero_block_p (diff_buffer, bytes_read))
        {
        }
       if (!zero_block_p (diff_buffer, bytes_read))
        {
+         char begbuf[INT_BUFSIZE_BOUND (off_t)];
          report_difference (file->stat_info,
          report_difference (file->stat_info,
-                            _("File fragment at %lu is not a hole"), beg);
+                            _("File fragment at %s is not a hole"),
+                            offtostr (beg, begbuf));
          return false;
        }
 
          return false;
        }
 
@@ -535,10 +522,13 @@ 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;
       size_t rdsize = (size_left > BLOCKSIZE) ? BLOCKSIZE : size_left;
   while (size_left > 0)
     {
       size_t bytes_read;
       size_t rdsize = (size_left > BLOCKSIZE) ? BLOCKSIZE : size_left;
+      char diff_buffer[BLOCKSIZE];
 
       union block *blk = find_next_block ();
       if (!blk)
 
       union block *blk = find_next_block ();
       if (!blk)
@@ -551,14 +541,15 @@ check_data_region (struct tar_sparse_file *file, size_t i)
       if (bytes_read == SAFE_READ_ERROR)
        {
           read_diag_details (file->stat_info->orig_file_name,
       if (bytes_read == SAFE_READ_ERROR)
        {
           read_diag_details (file->stat_info->orig_file_name,
-                            file->stat_info->sparse_map[i].offset
-                                + file->stat_info->sparse_map[i].numbytes
-                                - size_left,
+                            (file->stat_info->sparse_map[i].offset
+                             + file->stat_info->sparse_map[i].numbytes
+                             - size_left),
                             rdsize);
          return false;
        }
       file->dumped_size += bytes_read;
       size_left -= bytes_read;
                             rdsize);
          return false;
        }
       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"));
@@ -576,26 +567,28 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
   size_t i;
   off_t offset = 0;
 
   size_t i;
   off_t offset = 0;
 
-  file.stat_info = st;
-  file.fd = fd;
-
-  if (!sparse_select_optab (&file)
-      || !tar_sparse_init (&file))
+  if (!tar_sparse_init (&file))
     return dump_status_not_implemented;
 
     return dump_status_not_implemented;
 
+  file.stat_info = st;
+  file.fd = fd;
+  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 (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;
 }
@@ -647,7 +640,7 @@ oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
       || file->stat_info->archive_file_size < 0)
     return add_fail;
 
       || file->stat_info->archive_file_size < 0)
     return add_fail;
 
-  sparse_add_map (file, &sp);
+  sparse_add_map (file->stat_info, &sp);
   return add_ok;
 }
 
   return add_ok;
 }
 
@@ -658,7 +651,7 @@ oldgnu_fixup_header (struct tar_sparse_file *file)
      which actually contains archived size. The following fixes it */
   file->stat_info->archive_file_size = file->stat_info->stat.st_size;
   file->stat_info->stat.st_size =
      which actually contains archived size. The following fixes it */
   file->stat_info->archive_file_size = file->stat_info->stat.st_size;
   file->stat_info->stat.st_size =
-                OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
+    OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
   return true;
 }
 
   return true;
 }
 
@@ -669,7 +662,7 @@ oldgnu_get_sparse_info (struct tar_sparse_file *file)
   size_t i;
   union block *h = current_header;
   int ext_p;
   size_t i;
   union block *h = current_header;
   int ext_p;
-  static enum oldgnu_add_status rc;
+  enum oldgnu_add_status rc;
 
   file->stat_info->sparse_map_avail = 0;
   for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
 
   file->stat_info->sparse_map_avail = 0;
   for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
@@ -756,7 +749,7 @@ oldgnu_dump_header (struct tar_sparse_file *file)
   return true;
 }
 
   return true;
 }
 
-static struct tar_sparse_optab oldgnu_optab = {
+static struct tar_sparse_optab const oldgnu_optab = {
   NULL,  /* No init function */
   NULL,  /* No done function */
   oldgnu_sparse_member_p,
   NULL,  /* No init function */
   NULL,  /* No done function */
   oldgnu_sparse_member_p,
@@ -795,7 +788,7 @@ star_get_sparse_info (struct tar_sparse_file *file)
   size_t i;
   union block *h = current_header;
   int ext_p;
   size_t i;
   union block *h = current_header;
   int ext_p;
-  static enum oldgnu_add_status rc;
+  enum oldgnu_add_status rc = add_ok;
 
   file->stat_info->sparse_map_avail = 0;
 
 
   file->stat_info->sparse_map_avail = 0;
 
@@ -837,7 +830,7 @@ star_get_sparse_info (struct tar_sparse_file *file)
 }
 
 
 }
 
 
-static struct tar_sparse_optab star_optab = {
+static struct tar_sparse_optab const star_optab = {
   NULL,  /* No init function */
   NULL,  /* No done function */
   star_sparse_member_p,
   NULL,  /* No init function */
   NULL,  /* No done function */
   star_sparse_member_p,
@@ -855,16 +848,34 @@ static struct tar_sparse_optab star_optab = {
 
    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
+   GNU.sparse.map       Map of non-null data chunks. A string consisting
+                       of comma-separated values "offset,size[,offset,size]..."
+
+   Tar versions 1.14-1.15.1 instead of the latter used:
+   
    repeat numblocks time
      GNU.sparse.offset    Offset of the next data block
      GNU.sparse.numbytes  Size of the next data block
    end repeat
    repeat numblocks time
      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. However,
+   POSIX requires the latest occurrence of the variable to override all
+   previous occurrences.
+     
+   To avoid this incompatibility new keyword GNU.sparse.map was introduced
+   in tar 1.15.2. Some people might still need the 1.14 way of handling
+   sparse files for the compatibility reasons: it can be achieved by
+   specifying `--pax-option delete=GNU.sparse.map' in the command line.
+
+   See FIXME-1.14-1.15.1-1.20, below.
 */
 
 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->archive_file_size != file->stat_info->stat.st_size;
+  return file->stat_info->sparse_map_avail > 0;
 }
 
 static bool
 }
 
 static bool
@@ -873,16 +884,40 @@ pax_dump_header (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;
+    
   /* 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);
-  for (i = 0; i < file->stat_info->sparse_map_avail; i++)
+
+  /* FIXME-1.14-1.15.1-1.20: See the comment above.
+     Starting with 1.17 this should display a warning about POSIX-incompatible
+     keywords being generated. In 1.20, the true branch of the if block below
+     will be removed and GNU.sparse.map will be marked in xhdr_tab as
+     protected. */
+  
+  if (xheader_keyword_deleted_p ("GNU.sparse.map"))
+    {
+      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.offset", file->stat_info, &i);
-      xheader_store ("GNU.sparse.numbytes", file->stat_info, &i);
+      xheader_string_begin ();
+      for (i = 0; i < file->stat_info->sparse_map_avail; i++)
+       {
+         if (i)
+           xheader_string_add (",");
+         xheader_string_add (umaxtostr (map[i].offset, nbuf));
+         xheader_string_add (",");
+         xheader_string_add (umaxtostr (map[i].numbytes, nbuf));
+       }
+      xheader_string_end ("GNU.sparse.map");
     }
     }
-
   blk = start_header (file->stat_info);
   /* Store the effective (shrunken) file size */
   OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
   blk = start_header (file->stat_info);
   /* Store the effective (shrunken) file size */
   OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
@@ -890,7 +925,7 @@ pax_dump_header (struct tar_sparse_file *file)
   return true;
 }
 
   return true;
 }
 
-static struct tar_sparse_optab pax_optab = {
+static struct tar_sparse_optab const pax_optab = {
   NULL,  /* No init function */
   NULL,  /* No done function */
   pax_sparse_member_p,
   NULL,  /* No init function */
   NULL,  /* No done function */
   pax_sparse_member_p,
@@ -901,4 +936,3 @@ static struct tar_sparse_optab pax_optab = {
   sparse_dump_region,
   sparse_extract_region,
 };
   sparse_dump_region,
   sparse_extract_region,
 };
-
This page took 0.038807 seconds and 4 git commands to generate.