]> Dogcows Code - chaz/tar/blobdiff - src/sparse.c
* gnulib.modules: Add closeout, exitfial.
[chaz/tar] / src / sparse.c
index 0955248c492a9e75cb767edd81d17693cfde9bb0..30bf03157c962c09f51fc8b8be07eb6f287e1000 100644 (file)
@@ -22,6 +22,7 @@
 #include "common.h"
 
 struct tar_sparse_file;
+static bool sparse_select_optab (struct tar_sparse_file *file);
 
 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 */
-  struct tar_sparse_optab const *optab;
+  struct tar_sparse_optab const *optab; /* Operation table */
   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)
 {
-  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);
+
   return true;
 }
 
@@ -318,6 +324,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i)
       memset (blk->buffer + bytes_read, 0, BLOCKSIZE - 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);
     }
 
@@ -354,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;
+      mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
       file->offset += count;
       if (count != wrbytes)
        {
@@ -372,16 +380,15 @@ enum dump_status
 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 */
 
-  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)
     {
@@ -391,8 +398,10 @@ sparse_dump_file (int fd, struct tar_stat_info *st)
        {
          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);
+         mv_end ();
        }
     }
 
@@ -414,7 +423,7 @@ 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);
@@ -425,7 +434,7 @@ sparse_fixup_header (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_fixup_header (&file);
@@ -438,15 +447,14 @@ sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size)
   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;
 
-  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);
@@ -460,13 +468,12 @@ sparse_skip_file (struct tar_stat_info *st)
   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;
 
-  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;
@@ -515,6 +522,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;
+  mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
+      
   while (size_left > 0)
     {
       size_t bytes_read;
@@ -540,6 +549,7 @@ check_data_region (struct tar_sparse_file *file, size_t i)
        }
       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"));
@@ -557,26 +567,28 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
   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;
 
+  file.stat_info = st;
+  file.fd = fd;
+  file.seekable = true; /* File *must* be seekable for compare to work */
+  
   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)
-       && 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);
-
+  mv_end ();
+  
   tar_sparse_done (&file);
   return rc;
 }
@@ -836,10 +848,28 @@ static struct tar_sparse_optab const star_optab = {
 
    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
+
+   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
@@ -854,16 +884,40 @@ pax_dump_header (struct tar_sparse_file *file)
   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);
-  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"))
     {
-      xheader_store ("GNU.sparse.offset", file->stat_info, &i);
-      xheader_store ("GNU.sparse.numbytes", file->stat_info, &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);
+       }
+    }
+  else
+    {
+      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);
This page took 0.027275 seconds and 4 git commands to generate.