ソースを参照

(sparse_diff_file): New function

Sergey Poznyakoff 21 年 前
コミット
a8b2b68c33
1 ファイル変更113 行追加2 行削除
  1. 113 2
      src/sparse.c

+ 113 - 2
src/sparse.c

@@ -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);
@@ -374,6 +374,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;
 }
 
+
+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;
+}
+
      
 /* Old GNU Format. The sparse file information is stored in the
    oldgnu_header in the following manner: