]> Dogcows Code - chaz/tar/blobdiff - src/sparse.c
(struct tar_sparse_optab.sparse_member_p)
[chaz/tar] / src / sparse.c
index 39be72db56f964ad5046553859c2fe3934b5664c..2f5330df3344d4036f51582dff2e932da6cdd1c9 100644 (file)
@@ -33,7 +33,9 @@ struct tar_sparse_optab
 {
   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 (*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 *);
@@ -52,6 +54,14 @@ struct tar_sparse_file
                                       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)
 {
@@ -107,7 +117,15 @@ tar_sparse_decode_header (struct tar_sparse_file *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
@@ -205,10 +223,8 @@ 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);
@@ -216,6 +232,7 @@ sparse_scan_file (struct tar_sparse_file *file)
 
 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)
@@ -232,8 +249,8 @@ sparse_select_optab (struct tar_sparse_file *file)
       break;
 
     case POSIX_FORMAT:
-      /* FIXME: Add method */
-      return false;
+      file->optab = &pax_optab;
+      break;
 
     case STAR_FORMAT:
       file->optab = &star_optab;
@@ -255,7 +272,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;
@@ -277,7 +294,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;
 }
 
@@ -289,8 +306,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;
@@ -357,6 +382,28 @@ sparse_file_p (struct tar_stat_info *stat)
             + (stat->stat.st_size % ST_NBLOCKSIZE != 0)));
 }
 
+bool
+sparse_member_p (struct tar_stat_info *stat)
+{
+  struct tar_sparse_file file;
+  
+  if (!sparse_select_optab (&file))
+    return false;
+  file.stat_info = stat;
+  return tar_sparse_member_p (&file);
+}
+
+bool
+sparse_fixup_header (struct tar_stat_info *stat)
+{
+  struct tar_sparse_file file;
+  
+  if (!sparse_select_optab (&file))
+    return false;
+  file.stat_info = stat;
+  return tar_sparse_fixup_header (&file);
+}
+
 enum dump_status
 sparse_extract_file (int fd, struct tar_stat_info *stat, off_t *size)
 {
@@ -378,6 +425,24 @@ 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;
 }
 
+enum dump_status
+sparse_skip_file (struct tar_stat_info *stat)
+{
+  bool rc = true;
+  struct tar_sparse_file file;
+  
+  file.stat_info = stat;
+  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];
                   
@@ -482,7 +547,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);
@@ -515,6 +580,12 @@ enum oldgnu_add_status
     add_fail
   };
 
+static bool
+oldgnu_sparse_member_p (struct tar_sparse_file *file_unused)
+{
+  return current_header->header.typeflag == GNUTYPE_SPARSE;
+}
+
 /* Add a sparse item to the sparse file and its obstack */
 static enum oldgnu_add_status 
 oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
@@ -534,8 +605,18 @@ oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
   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)
 {
@@ -544,12 +625,6 @@ oldgnu_get_sparse_info (struct tar_sparse_file *file)
   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;
   for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
     {
@@ -638,7 +713,9 @@ oldgnu_dump_header (struct tar_sparse_file *file)
 static struct tar_sparse_optab oldgnu_optab = {
   NULL,  /* No init function */
   NULL,  /* No done function */
+  oldgnu_sparse_member_p,
   oldgnu_dump_header,
+  oldgnu_fixup_header,
   oldgnu_get_sparse_info,
   NULL,  /* No scan_block function */
   sparse_dump_region,
@@ -648,8 +725,24 @@ static struct tar_sparse_optab oldgnu_optab = {
 \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_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)
 {
@@ -658,12 +751,6 @@ star_get_sparse_info (struct tar_sparse_file *file)
   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'
@@ -707,9 +794,65 @@ star_get_sparse_info (struct tar_sparse_file *file)
 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.026792 seconds and 4 git commands to generate.