]> Dogcows Code - chaz/tar/blobdiff - src/sparse.c
Bugfix. Changes introduced 2004-11-26 broke extraction from stdin.
[chaz/tar] / src / sparse.c
index 4f936dc6faed959aa93a20f6d74df4cd821543df..b10daf6f69136f00367b087a02e05e7387938815 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 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
@@ -16,7 +16,7 @@
    with this program; if not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
    with this program; if not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
-#include "system.h"
+#include <system.h>
 #include <quotearg.h>
 #include "common.h"
 
 #include <quotearg.h>
 #include "common.h"
 
@@ -33,12 +33,14 @@ 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
 };
 
 struct tar_sparse_file
@@ -52,6 +54,14 @@ struct tar_sparse_file
                                       reqiure */
 };
 
                                       reqiure */
 };
 
+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)
 {
@@ -79,18 +89,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,7 +117,15 @@ 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
 }
 
 \f
@@ -171,13 +189,14 @@ sparse_scan_file (struct tar_sparse_file *file)
     return false;
   clear_block (buffer);
 
     return false;
   clear_block (buffer);
 
-  file->stat_info->sparse_map_size = 0;
+  file->stat_info->sparse_map_avail = 0;
   file->stat_info->archive_file_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 (file->fd, buffer, sizeof buffer)) != 0
+        && count != SAFE_READ_ERROR)
     {
       /* Analize the block */
       if (zero_block_p (buffer, count))
     {
       /* Analize the block */
       if (zero_block_p (buffer, count))
@@ -199,27 +218,27 @@ sparse_scan_file (struct tar_sparse_file *file)
          if (!tar_sparse_scan (file, scan_block, buffer))
            return false;
        }
          if (!tar_sparse_scan (file, scan_block, buffer))
            return false;
        }
-      
+
       offset += count;
       clear_block (buffer);
     }
       offset += count;
       clear_block (buffer);
     }
-      
+
   if (sp.numbytes == 0)
   if (sp.numbytes == 0)
-    {
-      sp.offset = offset - 1;
-      sp.numbytes = 1;
-    }
+    sp.offset = offset;
+
   sparse_add_map (file, &sp);
   file->stat_info->archive_file_size += count;
   return tar_sparse_scan (file, scan_end, NULL);
 }
 
 static struct tar_sparse_optab oldgnu_optab;
   sparse_add_map (file, &sp);
   file->stat_info->archive_file_size += count;
   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 bool
 sparse_select_optab (struct tar_sparse_file *file)
 {
 
 static bool
 sparse_select_optab (struct tar_sparse_file *file)
 {
-  switch (archive_format)
+  switch (current_format == DEFAULT_FORMAT ? archive_format : current_format)
     {
     case V7_FORMAT:
     case USTAR_FORMAT:
     {
     case V7_FORMAT:
     case USTAR_FORMAT:
@@ -231,39 +250,42 @@ sparse_select_optab (struct tar_sparse_file *file)
       break;
 
     case POSIX_FORMAT:
       break;
 
     case POSIX_FORMAT:
+      file->optab = &pax_optab;
+      break;
+
     case STAR_FORMAT:
     case STAR_FORMAT:
-      /* FIXME: Add methods */
-      return false;
+      file->optab = &star_optab;
+      break;
 
     default:
 
     default:
-      break;
+      return false;
     }
   return true;
 }
 
 static bool
     }
   return true;
 }
 
 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,
+  off_t bytes_left = file->stat_info->sparse_map[i].numbytes;
+
+  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset,
                       SEEK_SET))
     return false;
 
                       SEEK_SET))
     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 ();
       memset (blk->buffer, 0, BLOCKSIZE);
       bytes_read = safe_read (file->fd, blk->buffer, bufsize);
       blk = find_next_block ();
       memset (blk->buffer, 0, BLOCKSIZE);
       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
+                            file->stat_info->sparse_map[i].offset
+                                + file->stat_info->sparse_map[i].numbytes
                                 - bytes_left,
                     bufsize);
          return false;
                                 - bytes_left,
                     bufsize);
          return false;
@@ -273,20 +295,28 @@ sparse_dump_region (struct tar_sparse_file *file, size_t index)
       file->dumped_size += bytes_read;
       set_next_block_after (blk);
     }
       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,
+
+  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset,
                       SEEK_SET))
     return false;
                       SEEK_SET))
     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 (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;
@@ -314,12 +344,12 @@ 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;
   struct tar_sparse_file file;
 
 {
   bool rc;
   struct tar_sparse_file file;
 
-  file.stat_info = stat;
+  file.stat_info = st;
   file.fd = fd;
 
   if (!sparse_select_optab (&file)
   file.fd = fd;
 
   if (!sparse_select_optab (&file)
@@ -346,21 +376,43 @@ sparse_dump_file (int fd, struct tar_stat_info *stat)
 
 /* Returns true if the file represented by stat is a sparse one */
 bool
 
 /* 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 (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)
 {
 {
-  return (ST_NBLOCKS (stat->stat)
-         < (stat->stat.st_size / ST_NBLOCKSIZE
-            + (stat->stat.st_size % ST_NBLOCKSIZE != 0)));
+  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;
 
   if (!sparse_select_optab (&file)
   file.fd = fd;
 
   if (!sparse_select_optab (&file)
@@ -374,25 +426,43 @@ 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
 static char diff_buffer[BLOCKSIZE];
 \f
 static char diff_buffer[BLOCKSIZE];
-                  
+
 static bool
 check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
 {
   if (!lseek_or_error (file, beg, SEEK_SET))
     return false;
 static bool
 check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
 {
   if (!lseek_or_error (file, beg, SEEK_SET))
     return false;
-  
+
   while (beg < end)
     {
       size_t bytes_read;
       size_t rdsize = end - beg;
   while (beg < end)
     {
       size_t bytes_read;
       size_t rdsize = end - beg;
-      
+
       if (rdsize > BLOCKSIZE)
        rdsize = BLOCKSIZE;
       clear_block (diff_buffer);
       bytes_read = safe_read (file->fd, diff_buffer, rdsize);
       if (rdsize > BLOCKSIZE)
        rdsize = BLOCKSIZE;
       clear_block (diff_buffer);
       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,
@@ -412,19 +482,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,
+
+  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset,
                       SEEK_SET))
     return false;
                       SEEK_SET))
     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;
-      
+
       union block *blk = find_next_block ();
       if (!blk)
        {
       union block *blk = find_next_block ();
       if (!blk)
        {
@@ -433,11 +503,11 @@ 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
+                            file->stat_info->sparse_map[i].offset
+                                + file->stat_info->sparse_map[i].numbytes
                                 - size_left,
                             rdsize);
          return false;
                                 - size_left,
                             rdsize);
          return false;
@@ -454,14 +524,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)
@@ -478,21 +548,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
@@ -511,8 +581,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,8 +606,18 @@ oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
   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)
 {
 static bool
 oldgnu_get_sparse_info (struct tar_sparse_file *file)
 {
@@ -539,14 +625,8 @@ oldgnu_get_sparse_info (struct tar_sparse_file *file)
   union block *h = current_header;
   int ext_p;
   static enum oldgnu_add_status rc;
   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;
+
+  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]);
@@ -597,7 +677,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)
@@ -614,7 +694,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 ();
@@ -634,9 +714,146 @@ oldgnu_dump_header (struct tar_sparse_file *file)
 static struct tar_sparse_optab oldgnu_optab = {
   NULL,  /* No init function */
   NULL,  /* No done function */
 static struct tar_sparse_optab oldgnu_optab = {
   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,
   sparse_extract_region,
 };
   oldgnu_get_sparse_info,
   NULL,  /* No scan_block function */
   sparse_dump_region,
   sparse_extract_region,
 };
+
+\f
+/* Star */
+
+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 enum oldgnu_add_status rc;
+
+  file->stat_info->sparse_map_avail = 0;
+
+  if (h->star_in_header.prefix[0] == '\0'
+      && h->star_in_header.sp[0].offset[10] != '\0')
+    {
+      /* Old star format */
+      for (i = 0; i < SPARSES_IN_STAR_HEADER; i++)
+       {
+         rc = oldgnu_add_sparse (file, &h->star_in_header.sp[i]);
+         if (rc != add_ok)
+           break;
+       }
+      ext_p = h->star_in_header.isextended;
+    }
+  else
+    ext_p = 1;
+
+  for (; rc == add_ok && ext_p; ext_p = h->star_ext_header.isextended)
+    {
+      h = find_next_block ();
+      if (!h)
+       {
+         ERROR ((0, 0, _("Unexpected EOF in archive")));
+         return false;
+       }
+      set_next_block_after (h);
+      for (i = 0; i < SPARSES_IN_STAR_EXT_HEADER && rc == add_ok; i++)
+       rc = oldgnu_add_sparse (file, &h->star_ext_header.sp[i]);
+    }
+
+  if (rc == add_fail)
+    {
+      ERROR ((0, 0, _("%s: invalid sparse archive member"),
+             file->stat_info->orig_file_name));
+      return false;
+    }
+  return true;
+}
+
+
+static struct tar_sparse_optab star_optab = {
+  NULL,  /* No init function */
+  NULL,  /* No done function */
+  star_sparse_member_p,
+  NULL,
+  star_fixup_header,
+  star_get_sparse_info,
+  NULL,  /* No scan_block 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 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,
+};
+
This page took 0.04092 seconds and 4 git commands to generate.