]> Dogcows Code - chaz/tar/blobdiff - src/sparse.c
A sweep of the sparse code prompted by a bug report by Jim Meyering.
[chaz/tar] / src / sparse.c
index 39be72db56f964ad5046553859c2fe3934b5664c..ecbbe108842bbd27366b3d16a470fc4e047a474e 100644 (file)
@@ -1,6 +1,6 @@
-/* Functions for dealing with sparse files 
+/* Functions for dealing with sparse files
 
 
-   Copyright (C) 2003 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005 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
 
    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"
 
 #include <quotearg.h>
 #include "common.h"
 
@@ -33,29 +34,74 @@ struct tar_sparse_optab
 {
   bool (*init) (struct tar_sparse_file *);
   bool (*done) (struct tar_sparse_file *);
 {
   bool (*init) (struct tar_sparse_file *);
   bool (*done) (struct tar_sparse_file *);
+  bool (*sparse_member_p) (struct tar_sparse_file *);
   bool (*dump_header) (struct tar_sparse_file *);
   bool (*dump_header) (struct tar_sparse_file *);
+  bool (*fixup_header) (struct tar_sparse_file *);
   bool (*decode_header) (struct tar_sparse_file *);
   bool (*scan_block) (struct tar_sparse_file *, enum sparse_scan_state,
                      void *);
   bool (*decode_header) (struct tar_sparse_file *);
   bool (*scan_block) (struct tar_sparse_file *, enum sparse_scan_state,
                      void *);
-  bool (*dump_region) (struct tar_sparse_file *, size_t index);
-  bool (*extract_region) (struct tar_sparse_file *, size_t index);
+  bool (*dump_region) (struct tar_sparse_file *, size_t);
+  bool (*extract_region) (struct tar_sparse_file *, size_t);
 };
 
 struct tar_sparse_file
 {
   int fd;                           /* File descriptor */
 };
 
 struct tar_sparse_file
 {
   int fd;                           /* File descriptor */
-  size_t dumped_size;               /* Number of bytes actually written
+  bool seekable;                    /* Is fd seekable? */
+  off_t offset;                     /* Current offset in fd if seekable==false.
+                                      Otherwise unused */
+  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;
   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 */
+static bool
+dump_zeros (struct tar_sparse_file *file, off_t offset)
+{
+  static char const zero_buf[BLOCKSIZE];
+
+  if (offset < file->offset)
+    {
+      errno = EINVAL;
+      return false;
+    }
+
+  while (file->offset < offset)
+    {
+      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;
+         return false;
+       }
+      file->offset += wrbytes;
+    }
+
+  return true;
+}
+
+static bool
+tar_sparse_member_p (struct tar_sparse_file *file)
+{
+  if (file->optab->sparse_member_p)
+    return file->optab->sparse_member_p (file);
+  return false;
+}
+
 static bool
 tar_sparse_init (struct tar_sparse_file *file)
 {
 static bool
 tar_sparse_init (struct tar_sparse_file *file)
 {
-  file->dumped_size = 0;
   if (file->optab->init)
     return file->optab->init (file);
   return true;
   if (file->optab->init)
     return file->optab->init (file);
   return true;
@@ -79,18 +125,18 @@ tar_sparse_scan (struct tar_sparse_file *file, enum sparse_scan_state state,
 }
 
 static bool
 }
 
 static bool
-tar_sparse_dump_region (struct tar_sparse_file *file, size_t index)
+tar_sparse_dump_region (struct tar_sparse_file *file, size_t i)
 {
   if (file->optab->dump_region)
 {
   if (file->optab->dump_region)
-    return file->optab->dump_region (file, index);
+    return file->optab->dump_region (file, i);
   return false;
 }
 
 static bool
   return false;
 }
 
 static bool
-tar_sparse_extract_region (struct tar_sparse_file *file, size_t index)
+tar_sparse_extract_region (struct tar_sparse_file *file, size_t i)
 {
   if (file->optab->extract_region)
 {
   if (file->optab->extract_region)
-    return file->optab->extract_region (file, index);
+    return file->optab->extract_region (file, i);
   return false;
 }
 
   return false;
 }
 
@@ -107,14 +153,24 @@ tar_sparse_decode_header (struct tar_sparse_file *file)
 {
   if (file->optab->decode_header)
     return file->optab->decode_header (file);
 {
   if (file->optab->decode_header)
     return file->optab->decode_header (file);
-  return false;
+  return true;
+}
+
+static bool
+tar_sparse_fixup_header (struct tar_sparse_file *file)
+{
+  if (file->optab->fixup_header)
+    return file->optab->fixup_header (file);
+  return true;
 }
 
 \f
 static bool
 }
 
 \f
 static bool
-lseek_or_error (struct tar_sparse_file *file, off_t offset, int whence)
+lseek_or_error (struct tar_sparse_file *file, off_t offset)
 {
 {
-  if (lseek (file->fd, offset, whence) < 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;
@@ -126,7 +182,7 @@ lseek_or_error (struct tar_sparse_file *file, off_t offset, int whence)
    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++)
@@ -134,57 +190,44 @@ 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};
 
   struct sp_array sp = {0, 0};
 
-  if (!lseek_or_error (file, 0, SEEK_SET))
+  if (!lseek_or_error (file, 0))
     return false;
     return false;
-  clear_block (buffer);
 
 
-  file->stat_info->sparse_map_size = 0;
-  file->stat_info->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)
     {
     {
-      /* 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;
@@ -195,27 +238,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;
        }
          if (!tar_sparse_scan (file, scan_block, buffer))
            return false;
        }
-      
+
       offset += count;
       offset += count;
-      clear_block (buffer);
     }
     }
-      
+
   if (sp.numbytes == 0)
   if (sp.numbytes == 0)
-    {
-      sp.offset = offset - 1;
-      sp.numbytes = 1;
-    }
-  sparse_add_map (file, &sp);
-  file->stat_info->archive_file_size += count;
+    sp.offset = offset;
+
+  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 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)
@@ -232,13 +273,13 @@ sparse_select_optab (struct tar_sparse_file *file)
       break;
 
     case POSIX_FORMAT:
       break;
 
     case POSIX_FORMAT:
-      /* FIXME: Add method */
-      return false;
+      file->optab = &pax_optab;
+      break;
 
     case STAR_FORMAT:
       file->optab = &star_optab;
       break;
 
     case STAR_FORMAT:
       file->optab = &star_optab;
       break;
-       
+
     default:
       return false;
     }
     default:
       return false;
     }
@@ -246,51 +287,57 @@ sparse_select_optab (struct tar_sparse_file *file)
 }
 
 static bool
 }
 
 static bool
-sparse_dump_region (struct tar_sparse_file *file, size_t index)
+sparse_dump_region (struct tar_sparse_file *file, size_t i)
 {
   union block *blk;
 {
   union block *blk;
-  off_t bytes_left = file->stat_info->sparse_map[index].numbytes;
-  
-  if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset,
-                      SEEK_SET))
+  off_t bytes_left = file->stat_info->sparse_map[i].numbytes;
+
+  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
 
     return false;
 
-  do
+  while (bytes_left > 0)
     {
       size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left;
     {
       size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left;
-      off_t bytes_read;
-      
+      size_t bytes_read;
+
       blk = find_next_block ();
       blk = find_next_block ();
-      memset (blk->buffer, 0, BLOCKSIZE);
       bytes_read = safe_read (file->fd, blk->buffer, bufsize);
       bytes_read = safe_read (file->fd, blk->buffer, bufsize);
-      if (bytes_read < 0)
+      if (bytes_read == SAFE_READ_ERROR)
        {
           read_diag_details (file->stat_info->orig_file_name,
        {
           read_diag_details (file->stat_info->orig_file_name,
-                            file->stat_info->sparse_map[index].offset
-                                + file->stat_info->sparse_map[index].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;
       set_next_block_after (blk);
     }
       bytes_left -= bytes_read;
       file->dumped_size += bytes_read;
       set_next_block_after (blk);
     }
-  while (bytes_left > 0);
+
   return true;
 }
 
 static bool
   return true;
 }
 
 static bool
-sparse_extract_region (struct tar_sparse_file *file, size_t index)
+sparse_extract_region (struct tar_sparse_file *file, size_t i)
 {
   size_t write_size;
 {
   size_t write_size;
-  
-  if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset,
-                      SEEK_SET))
+
+  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
     return false;
-  write_size = file->stat_info->sparse_map[index].numbytes;
-  while (write_size > 0)
+
+  write_size = file->stat_info->sparse_map[i].numbytes;
+
+  if (write_size == 0)
+    {
+      /* Last block of the file is a hole */
+      if (file->seekable && sys_truncate (file->fd))
+       truncate_warn (file->stat_info->orig_file_name);
+    }
+  else while (write_size > 0)
     {
       size_t count;
       size_t wrbytes = (write_size > BLOCKSIZE) ? BLOCKSIZE : write_size;
     {
       size_t count;
       size_t wrbytes = (write_size > BLOCKSIZE) ? BLOCKSIZE : write_size;
@@ -304,6 +351,7 @@ sparse_extract_region (struct tar_sparse_file *file, size_t index)
       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;
+      file->offset += count;
       if (count != wrbytes)
        {
          write_error_details (file->stat_info->orig_file_name,
       if (count != wrbytes)
        {
          write_error_details (file->stat_info->orig_file_name,
@@ -318,13 +366,14 @@ sparse_extract_region (struct tar_sparse_file *file, size_t index)
 
 /* Interface functions */
 enum dump_status
 
 /* Interface functions */
 enum dump_status
-sparse_dump_file (int fd, struct tar_stat_info *stat)
+sparse_dump_file (int fd, struct tar_stat_info *st)
 {
   bool rc;
 {
   bool rc;
-  struct tar_sparse_file file;
+  struct tar_sparse_file file = { 0, };
 
 
-  file.stat_info = stat;
+  file.stat_info = st;
   file.fd = fd;
   file.fd = fd;
+  file.seekable = true; /* File *must* be seekable for dump to work */
 
   if (!sparse_select_optab (&file)
       || !tar_sparse_init (&file))
 
   if (!sparse_select_optab (&file)
       || !tar_sparse_init (&file))
@@ -344,28 +393,52 @@ sparse_dump_file (int fd, struct tar_stat_info *stat)
        }
     }
 
        }
     }
 
-  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;
 }
 
 /* Returns true if the file represented by stat is a sparse one */
 bool
   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 *stat)
+sparse_file_p (struct tar_stat_info *st)
 {
 {
-  return (ST_NBLOCKS (stat->stat)
-         < (stat->stat.st_size / ST_NBLOCKSIZE
-            + (stat->stat.st_size % ST_NBLOCKSIZE != 0)));
+  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;
+
+  if (!sparse_select_optab (&file))
+    return false;
+  file.stat_info = st;
+  return tar_sparse_member_p (&file);
+}
+
+bool
+sparse_fixup_header (struct tar_stat_info *st)
+{
+  struct tar_sparse_file file;
+
+  if (!sparse_select_optab (&file))
+    return false;
+  file.stat_info = st;
+  return tar_sparse_fixup_header (&file);
 }
 
 enum dump_status
 }
 
 enum dump_status
-sparse_extract_file (int fd, struct tar_stat_info *stat, off_t *size)
+sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size)
 {
   bool rc = true;
   struct tar_sparse_file file;
   size_t i;
 {
   bool rc = true;
   struct tar_sparse_file file;
   size_t i;
-  
-  file.stat_info = stat;
+
+  file.stat_info = st;
   file.fd = fd;
   file.fd = fd;
+  file.seekable = lseek (fd, 0, SEEK_SET) == 0;
+  file.offset = 0;
 
   if (!sparse_select_optab (&file)
       || !tar_sparse_init (&file))
 
   if (!sparse_select_optab (&file)
       || !tar_sparse_init (&file))
@@ -378,25 +451,39 @@ sparse_extract_file (int fd, struct tar_stat_info *stat, off_t *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;
 }
 
+enum dump_status
+sparse_skip_file (struct tar_stat_info *st)
+{
+  bool rc = true;
+  struct tar_sparse_file file;
+
+  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
 \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)
 {
-  if (!lseek_or_error (file, beg, SEEK_SET))
+  if (!lseek_or_error (file, beg))
     return false;
     return false;
-  
+
   while (beg < end)
     {
       size_t bytes_read;
   while (beg < end)
     {
       size_t bytes_read;
-      size_t rdsize = end - beg;
-      
-      if (rdsize > BLOCKSIZE)
-       rdsize = BLOCKSIZE;
-      clear_block (diff_buffer);
+      size_t rdsize = BLOCKSIZE < end - beg ? BLOCKSIZE : end - beg;
+      char diff_buffer[BLOCKSIZE];
+
       bytes_read = safe_read (file->fd, diff_buffer, rdsize);
       bytes_read = safe_read (file->fd, diff_buffer, rdsize);
-      if (bytes_read < 0)
+      if (bytes_read == SAFE_READ_ERROR)
        {
           read_diag_details (file->stat_info->orig_file_name,
                             beg,
        {
           read_diag_details (file->stat_info->orig_file_name,
                             beg,
@@ -405,8 +492,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;
        }
 
@@ -416,19 +505,19 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
 }
 
 static bool
 }
 
 static bool
-check_data_region (struct tar_sparse_file *file, size_t index)
+check_data_region (struct tar_sparse_file *file, size_t i)
 {
   size_t size_left;
 {
   size_t size_left;
-  
-  if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset,
-                      SEEK_SET))
+
+  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
     return false;
-  size_left = file->stat_info->sparse_map[index].numbytes;
+  size_left = file->stat_info->sparse_map[i].numbytes;
   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)
        {
@@ -437,12 +526,12 @@ check_data_region (struct tar_sparse_file *file, size_t index)
        }
       set_next_block_after (blk);
       bytes_read = safe_read (file->fd, diff_buffer, rdsize);
        }
       set_next_block_after (blk);
       bytes_read = safe_read (file->fd, diff_buffer, rdsize);
-      if (bytes_read < 0)
+      if (bytes_read == SAFE_READ_ERROR)
        {
           read_diag_details (file->stat_info->orig_file_name,
        {
           read_diag_details (file->stat_info->orig_file_name,
-                            file->stat_info->sparse_map[index].offset
-                                + file->stat_info->sparse_map[index].numbytes
-                                - size_left,
+                            (file->stat_info->sparse_map[i].offset
+                             + file->stat_info->sparse_map[i].numbytes
+                             - size_left),
                             rdsize);
          return false;
        }
                             rdsize);
          return false;
        }
@@ -458,14 +547,14 @@ check_data_region (struct tar_sparse_file *file, size_t index)
 }
 
 bool
 }
 
 bool
-sparse_diff_file (int fd, struct tar_stat_info *stat)
+sparse_diff_file (int fd, struct tar_stat_info *st)
 {
   bool rc = true;
   struct tar_sparse_file file;
   size_t i;
   off_t offset = 0;
 {
   bool rc = true;
   struct tar_sparse_file file;
   size_t i;
   off_t offset = 0;
-  
-  file.stat_info = stat;
+
+  file.stat_info = st;
   file.fd = fd;
 
   if (!sparse_select_optab (&file)
   file.fd = fd;
 
   if (!sparse_select_optab (&file)
@@ -482,21 +571,21 @@ sparse_diff_file (int fd, struct tar_stat_info *stat)
                + file.stat_info->sparse_map[i].numbytes;
     }
 
                + file.stat_info->sparse_map[i].numbytes;
     }
 
-  if (rc)
+  if (!rc)
     skip_file (file.stat_info->archive_file_size - file.dumped_size);
 
   tar_sparse_done (&file);
   return rc;
 }
 
     skip_file (file.stat_info->archive_file_size - file.dumped_size);
 
   tar_sparse_done (&file);
   return rc;
 }
 
-\f     
+\f
 /* Old GNU Format. The sparse file information is stored in the
    oldgnu_header in the following manner:
 
    The header is marked with type 'S'. Its `size' field contains
    the cumulative size of all non-empty blocks of the file. The
    actual file size is stored in `realsize' member of oldgnu_header.
 /* Old GNU Format. The sparse file information is stored in the
    oldgnu_header in the following manner:
 
    The header is marked with type 'S'. Its `size' field contains
    the cumulative size of all non-empty blocks of the file. The
    actual file size is stored in `realsize' member of oldgnu_header.
-   
+
    The map of the file is stored in a list of `struct sparse'.
    Each struct contains offset to the block of data and its
    size (both as octal numbers). The first file header contains
    The map of the file is stored in a list of `struct sparse'.
    Each struct contains offset to the block of data and its
    size (both as octal numbers). The first file header contains
@@ -515,8 +604,14 @@ enum oldgnu_add_status
     add_fail
   };
 
     add_fail
   };
 
+static bool
+oldgnu_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused)))
+{
+  return current_header->header.typeflag == GNUTYPE_SPARSE;
+}
+
 /* Add a sparse item to the sparse file and its obstack */
 /* Add a sparse item to the sparse file and its obstack */
-static enum oldgnu_add_status 
+static enum oldgnu_add_status
 oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
 {
   struct sp_array sp;
 oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
 {
   struct sp_array sp;
@@ -530,27 +625,31 @@ 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;
 }
 
-/* Convert old GNU format sparse data to internal representation
-   FIXME: Clubbers current_header! */
+static bool
+oldgnu_fixup_header (struct tar_sparse_file *file)
+{
+  /* NOTE! st_size was initialized from the header
+     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);
+  return true;
+}
+
+/* Convert old GNU format sparse data to internal representation */
 static bool
 oldgnu_get_sparse_info (struct tar_sparse_file *file)
 {
   size_t i;
   union block *h = current_header;
   int ext_p;
 static bool
 oldgnu_get_sparse_info (struct tar_sparse_file *file)
 {
   size_t i;
   union block *h = current_header;
   int ext_p;
-  static enum oldgnu_add_status rc;
-  
-  /* FIXME: note this! st_size was initialized from the header
-     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);
-  
-  file->stat_info->sparse_map_size = 0;
+  enum oldgnu_add_status rc;
+
+  file->stat_info->sparse_map_avail = 0;
   for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
     {
       rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]);
   for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
     {
       rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]);
@@ -601,7 +700,7 @@ oldgnu_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;
-    
+
   blk = start_header (file->stat_info);
   blk->header.typeflag = GNUTYPE_SPARSE;
   if (file->stat_info->sparse_map_avail > SPARSES_IN_OLDGNU_HEADER)
   blk = start_header (file->stat_info);
   blk->header.typeflag = GNUTYPE_SPARSE;
   if (file->stat_info->sparse_map_avail > SPARSES_IN_OLDGNU_HEADER)
@@ -618,7 +717,7 @@ oldgnu_dump_header (struct tar_sparse_file *file)
                            SPARSES_IN_OLDGNU_HEADER);
   blk->oldgnu_header.isextended = i < file->stat_info->sparse_map_avail;
   finish_header (file->stat_info, blk, block_ordinal);
                            SPARSES_IN_OLDGNU_HEADER);
   blk->oldgnu_header.isextended = i < file->stat_info->sparse_map_avail;
   finish_header (file->stat_info, blk, block_ordinal);
-  
+
   while (i < file->stat_info->sparse_map_avail)
     {
       blk = find_next_block ();
   while (i < file->stat_info->sparse_map_avail)
     {
       blk = find_next_block ();
@@ -635,10 +734,12 @@ 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 */
   NULL,  /* No init function */
   NULL,  /* No done function */
+  oldgnu_sparse_member_p,
   oldgnu_dump_header,
   oldgnu_dump_header,
+  oldgnu_fixup_header,
   oldgnu_get_sparse_info,
   NULL,  /* No scan_block function */
   sparse_dump_region,
   oldgnu_get_sparse_info,
   NULL,  /* No scan_block function */
   sparse_dump_region,
@@ -648,23 +749,33 @@ static struct tar_sparse_optab oldgnu_optab = {
 \f
 /* Star */
 
 \f
 /* Star */
 
-/* Convert STAR format sparse data to internal representation
-   FIXME: Clubbers current_header! */
+static bool
+star_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused)))
+{
+  return current_header->header.typeflag == GNUTYPE_SPARSE;
+}
+
+static bool
+star_fixup_header (struct tar_sparse_file *file)
+{
+  /* NOTE! st_size was initialized from the header
+     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->star_in_header.realsize);
+  return true;
+}
+
+/* Convert STAR format sparse data to internal representation */
 static bool
 star_get_sparse_info (struct tar_sparse_file *file)
 {
   size_t i;
   union block *h = current_header;
   int ext_p;
 static bool
 star_get_sparse_info (struct tar_sparse_file *file)
 {
   size_t i;
   union block *h = current_header;
   int ext_p;
-  static enum oldgnu_add_status rc;
-  
-  /* FIXME: note this! st_size was initialized from the header
-     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->star_in_header.realsize);
-  
-  file->stat_info->sparse_map_size = 0;
+  enum oldgnu_add_status rc = add_ok;
+
+  file->stat_info->sparse_map_avail = 0;
 
   if (h->star_in_header.prefix[0] == '\0'
       && h->star_in_header.sp[0].offset[10] != '\0')
 
   if (h->star_in_header.prefix[0] == '\0'
       && h->star_in_header.sp[0].offset[10] != '\0')
@@ -704,12 +815,67 @@ 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 */
   NULL,  /* No init function */
   NULL,  /* No done function */
+  star_sparse_member_p,
   NULL,
   NULL,
+  star_fixup_header,
   star_get_sparse_info,
   NULL,  /* No scan_block function */
   star_get_sparse_info,
   NULL,  /* No scan_block function */
-  NULL, /* No dump region function */ 
+  NULL, /* No dump region function */
+  sparse_extract_region,
+};
+
+\f
+/* GNU PAX sparse file format. 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
+   repeat numblocks time
+     GNU.sparse.offset    Offset of the next data block
+     GNU.sparse.numbytes  Size of the next data block
+   end repeat
+*/
+
+static bool
+pax_sparse_member_p (struct tar_sparse_file *file)
+{
+  return file->stat_info->archive_file_size != file->stat_info->stat.st_size;
+}
+
+static bool
+pax_dump_header (struct tar_sparse_file *file)
+{
+  off_t block_ordinal = current_block_ordinal ();
+  union block *blk;
+  size_t i;
+
+  /* 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++)
+    {
+      xheader_store ("GNU.sparse.offset", file->stat_info, &i);
+      xheader_store ("GNU.sparse.numbytes", file->stat_info, &i);
+    }
+
+  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);
+  return true;
+}
+
+static struct tar_sparse_optab const pax_optab = {
+  NULL,  /* No init function */
+  NULL,  /* No done function */
+  pax_sparse_member_p,
+  pax_dump_header,
+  NULL,  /* No decode_header function */
+  NULL,  /* No fixup_header function */
+  NULL,  /* No scan_block function */
+  sparse_dump_region,
   sparse_extract_region,
 };
   sparse_extract_region,
 };
This page took 0.040149 seconds and 4 git commands to generate.