]> Dogcows Code - chaz/tar/blobdiff - src/sparse.c
(sparse_diff_file): Bugfix. Thanks Martin Simmons for the patch.
[chaz/tar] / src / sparse.c
index 4f936dc6faed959aa93a20f6d74df4cd821543df..a661ab18d3a47b3ed6cf5446130fc1dc957d2a76 100644 (file)
@@ -205,21 +205,21 @@ sparse_scan_file (struct tar_sparse_file *file)
     }
       
   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;
+static struct tar_sparse_optab star_optab;
+static struct tar_sparse_optab pax_optab;
 
 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:
@@ -231,12 +231,15 @@ sparse_select_optab (struct tar_sparse_file *file)
       break;
 
     case POSIX_FORMAT:
-    case STAR_FORMAT:
-      /* FIXME: Add methods */
-      return false;
+      file->optab = &pax_optab;
+      break;
 
-    default:
+    case STAR_FORMAT:
+      file->optab = &star_optab;
       break;
+       
+    default:
+      return false;
     }
   return true;
 }
@@ -251,7 +254,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t index)
                       SEEK_SET))
     return false;
 
-  do
+  while (bytes_left > 0)
     {
       size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left;
       off_t bytes_read;
@@ -273,7 +276,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t index)
       file->dumped_size += bytes_read;
       set_next_block_after (blk);
     }
-  while (bytes_left > 0);
+
   return true;
 }
 
@@ -285,8 +288,16 @@ sparse_extract_region (struct tar_sparse_file *file, size_t index)
   if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset,
                       SEEK_SET))
     return false;
+
   write_size = file->stat_info->sparse_map[index].numbytes;
-  while (write_size > 0)
+
+  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;
@@ -478,7 +489,7 @@ sparse_diff_file (int fd, struct tar_stat_info *stat)
                + 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);
@@ -640,3 +651,128 @@ static struct tar_sparse_optab oldgnu_optab = {
   sparse_dump_region,
   sparse_extract_region,
 };
+
+\f
+/* Star */
+
+/* Convert STAR format sparse data to internal representation
+   FIXME: Clubbers current_header! */
+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;
+
+  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 */
+  NULL,
+  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_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 bool
+pax_decode_header (struct tar_sparse_file *file)
+{
+  /* Restore actual size */
+  size_t s = file->stat_info->archive_file_size;
+  file->stat_info->archive_file_size = file->stat_info->stat.st_size;
+  file->stat_info->stat.st_size = s;
+  return true;
+}
+
+static struct tar_sparse_optab pax_optab = {
+  NULL,  /* No init function */
+  NULL,  /* No done function */
+  pax_dump_header,
+  pax_decode_header,
+  NULL,  /* No scan_block function */
+  sparse_dump_region,
+  sparse_extract_region,
+};
+
This page took 0.025204 seconds and 4 git commands to generate.