]> Dogcows Code - chaz/tar/blobdiff - src/sparse.c
Support for star sparse format
[chaz/tar] / src / sparse.c
index 4186254549cccc40d244c14e07e7b5cca838bab9..39be72db56f964ad5046553859c2fe3934b5664c 100644 (file)
@@ -130,8 +130,8 @@ zero_block_p (char *buffer, size_t size)
 {
   while (size--)
     if (*buffer++)
-      return 0;
-  return 1;
+      return false;
+  return true;
 }
 
 #define clear_block(p) memset (p, 0, BLOCKSIZE);
@@ -215,11 +215,12 @@ sparse_scan_file (struct tar_sparse_file *file)
 }
 
 static struct tar_sparse_optab oldgnu_optab;
+static struct tar_sparse_optab star_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 +232,15 @@ sparse_select_optab (struct tar_sparse_file *file)
       break;
 
     case POSIX_FORMAT:
-    case STAR_FORMAT:
-      /* FIXME: Add methods */
+      /* FIXME: Add method */
       return false;
 
-    default:
+    case STAR_FORMAT:
+      file->optab = &star_optab;
       break;
+       
+    default:
+      return false;
     }
   return true;
 }
@@ -374,6 +378,117 @@ 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;
 }
 
+\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;
+  
+  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 (bytes_read < 0)
+       {
+          read_diag_details (file->stat_info->orig_file_name,
+                            beg,
+                            rdsize);
+         return false;
+       }
+      if (!zero_block_p (diff_buffer, bytes_read))
+       {
+         report_difference (file->stat_info,
+                            _("File fragment at %lu is not a hole"), beg);
+         return false;
+       }
+
+      beg += bytes_read;
+    }
+  return true;
+}
+
+static bool
+check_data_region (struct tar_sparse_file *file, size_t index)
+{
+  size_t size_left;
+  
+  if (!lseek_or_error (file, file->stat_info->sparse_map[index].offset,
+                      SEEK_SET))
+    return false;
+  size_left = file->stat_info->sparse_map[index].numbytes;
+  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)
+       {
+         ERROR ((0, 0, _("Unexpected EOF in archive")));
+         return false;
+       }
+      set_next_block_after (blk);
+      bytes_read = safe_read (file->fd, diff_buffer, rdsize);
+      if (bytes_read < 0)
+       {
+          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,
+                            rdsize);
+         return false;
+       }
+      file->dumped_size += bytes_read;
+      size_left -= bytes_read;
+      if (memcmp (blk->buffer, diff_buffer, rdsize))
+       {
+         report_difference (file->stat_info, _("Contents differ"));
+         return false;
+       }
+    }
+  return true;
+}
+
+bool
+sparse_diff_file (int fd, struct tar_stat_info *stat)
+{
+  bool rc = true;
+  struct tar_sparse_file file;
+  size_t i;
+  off_t offset = 0;
+  
+  file.stat_info = stat;
+  file.fd = fd;
+
+  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 = check_sparse_region (&file,
+                               offset, file.stat_info->sparse_map[i].offset)
+       && 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);
+
+  tar_sparse_done (&file);
+  return rc;
+}
+
 \f     
 /* Old GNU Format. The sparse file information is stored in the
    oldgnu_header in the following manner:
@@ -529,3 +644,72 @@ 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,
+};
This page took 0.026253 seconds and 4 git commands to generate.