]> Dogcows Code - chaz/tar/blobdiff - src/sparse.c
Update copyright years.
[chaz/tar] / src / sparse.c
index aa76c61c2ab232401d533005ae016fcee1bebd04..6a976763419e78a5d5041fe3a01046e787dccdc8 100644 (file)
@@ -1,10 +1,10 @@
 /* Functions for dealing with sparse files
 
-   Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   Copyright 2003-2007, 2010, 2013-2014 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any later
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
@@ -13,8 +13,7 @@
    Public License for more details.
 
    You should have received a copy of the GNU General Public License along
-   with this program; if not, write to the Free Software Foundation, Inc.,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <system.h>
 #include <inttostr.h>
@@ -216,43 +215,45 @@ sparse_scan_file (struct tar_sparse_file *file)
   struct tar_stat_info *st = file->stat_info;
   int fd = file->fd;
   char buffer[BLOCKSIZE];
-  size_t count;
+  size_t count = 0;
   off_t offset = 0;
   struct sp_array sp = {0, 0};
 
-  if (!lseek_or_error (file, 0))
-    return false;
-
   st->archive_file_size = 0;
-  
-  if (!tar_sparse_scan (file, scan_begin, NULL))
-    return false;
 
-  while ((count = safe_read (fd, buffer, sizeof buffer)) != 0
-        && count != SAFE_READ_ERROR)
+  if (ST_NBLOCKS (st->stat) == 0)
+    offset = st->stat.st_size;
+  else
     {
-      /* Analyze the block.  */
-      if (zero_block_p (buffer, count))
+      if (!tar_sparse_scan (file, scan_begin, NULL))
+       return false;
+
+      while ((count = blocking_read (fd, buffer, sizeof buffer)) != 0
+            && count != SAFE_READ_ERROR)
        {
-         if (sp.numbytes)
+         /* Analyze the block.  */
+         if (zero_block_p (buffer, count))
            {
-             sparse_add_map (st, &sp);
-             sp.numbytes = 0;
-             if (!tar_sparse_scan (file, scan_block, NULL))
+             if (sp.numbytes)
+               {
+                 sparse_add_map (st, &sp);
+                 sp.numbytes = 0;
+                 if (!tar_sparse_scan (file, scan_block, NULL))
+                   return false;
+               }
+           }
+         else
+           {
+             if (sp.numbytes == 0)
+               sp.offset = offset;
+             sp.numbytes += count;
+             st->archive_file_size += count;
+             if (!tar_sparse_scan (file, scan_block, buffer))
                return false;
            }
-       }
-      else
-       {
-         if (sp.numbytes == 0)
-           sp.offset = offset;
-         sp.numbytes += count;
-         st->archive_file_size += count;
-         if (!tar_sparse_scan (file, scan_block, buffer))
-           return false;
-       }
 
-      offset += count;
+         offset += count;
+       }
     }
 
   if (sp.numbytes == 0)
@@ -324,7 +325,6 @@ 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);
     }
 
@@ -334,7 +334,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i)
 static bool
 sparse_extract_region (struct tar_sparse_file *file, size_t i)
 {
-  size_t write_size;
+  off_t write_size;
 
   if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
@@ -358,7 +358,7 @@ sparse_extract_region (struct tar_sparse_file *file, size_t i)
          return false;
        }
       set_next_block_after (blk);
-      count = full_write (file->fd, blk->buffer, wrbytes);
+      count = blocking_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);
@@ -398,10 +398,11 @@ sparse_dump_file (int fd, struct tar_stat_info *st)
        {
          size_t i;
 
-         mv_begin (file.stat_info);
+         mv_begin_write (file.stat_info->file_name,
+                         file.stat_info->stat.st_size,
+                         file.stat_info->archive_file_size - file.dumped_size);
          for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
            rc = tar_sparse_dump_region (&file, i);
-         mv_end ();
        }
     }
 
@@ -508,13 +509,13 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
 static bool
 check_data_region (struct tar_sparse_file *file, size_t i)
 {
-  size_t size_left;
+  off_t size_left;
 
   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;
@@ -564,9 +565,9 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
   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);
+  mv_begin_read (st);
   for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
     {
       rc = check_sparse_region (&file,
@@ -579,7 +580,7 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
   if (!rc)
     skip_file (file.stat_info->archive_file_size - file.dumped_size);
   mv_end ();
-  
+
   tar_sparse_done (&file);
   return rc;
 }
@@ -588,18 +589,18 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
 /* Old GNU Format. The sparse file information is stored in the
    oldgnu_header in the following manner:
 
-   The header is marked with type 'S'. Its `size' field contains
+   The header is marked with type 'S'. Its 'size' field contains
    the cumulative size of all non-empty blocks of the file. The
-   actual file size is stored in `realsize' member of oldgnu_header.
+   actual file size is stored in 'realsize' member of oldgnu_header.
 
-   The map of the file is stored in a list of `struct sparse'.
+   The map of the file is stored in a list of 'struct sparse'.
    Each struct contains offset to the block of data and its
    size (both as octal numbers). The first file header contains
    at most 4 such structs (SPARSES_IN_OLDGNU_HEADER). If the map
-   contains more structs, then the field `isextended' of the main
-   header is set to 1 (binary) and the `struct sparse_header'
+   contains more structs, then the field 'isextended' of the main
+   header is set to 1 (binary) and the 'struct sparse_header'
    header follows, containing at most 21 following structs
-   (SPARSES_IN_SPARSE_HEADER). If more structs follow, `isextended'
+   (SPARSES_IN_SPARSE_HEADER). If more structs follow, 'isextended'
    field of the extended header is set and next  next extension header
    follows, etc... */
 
@@ -625,8 +626,9 @@ oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
   if (s->numbytes[0] == '\0')
     return add_finish;
   sp.offset = OFF_FROM_HEADER (s->offset);
-  sp.numbytes = SIZE_FROM_HEADER (s->numbytes);
-  if (sp.offset < 0
+  sp.numbytes = OFF_FROM_HEADER (s->numbytes);
+  if (sp.offset < 0 || sp.numbytes < 0
+      || INT_ADD_OVERFLOW (sp.offset, sp.numbytes)
       || file->stat_info->stat.st_size < sp.offset + sp.numbytes
       || file->stat_info->archive_file_size < 0)
     return add_fail;
@@ -640,10 +642,10 @@ 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 */
+  off_t realsize = OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
   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;
+  file->stat_info->stat.st_size = max (0, realsize);
+  return 0 <= realsize;
 }
 
 /* Convert old GNU format sparse data to internal representation */
@@ -695,8 +697,8 @@ oldgnu_store_sparse_info (struct tar_sparse_file *file, size_t *pindex,
     {
       OFF_TO_CHARS (file->stat_info->sparse_map[*pindex].offset,
                    sp->offset);
-      SIZE_TO_CHARS (file->stat_info->sparse_map[*pindex].numbytes,
-                    sp->numbytes);
+      OFF_TO_CHARS (file->stat_info->sparse_map[*pindex].numbytes,
+                   sp->numbytes);
     }
 }
 
@@ -764,10 +766,10 @@ 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 */
+  off_t realsize = OFF_FROM_HEADER (current_header->star_in_header.realsize);
   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;
+  file->stat_info->stat.st_size = max (0, realsize);
+  return 0 <= realsize;
 }
 
 /* Convert STAR format sparse data to internal representation */
@@ -807,6 +809,7 @@ star_get_sparse_info (struct tar_sparse_file *file)
       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]);
+      file->dumped_size += BLOCKSIZE;
     }
 
   if (rc == add_fail)
@@ -851,13 +854,13 @@ static struct tar_sparse_optab const star_optab = {
    instances of GNU.sparse.offset/GNU.sparse.numbytes variables, whereas
    POSIX requires the latest occurrence of the variable to override all
    previous occurrences.
-   
+
    To avoid this incompatibility two following versions were introduced.
 
    * 0.1
 
    Used by tar 1.15.2 -- 1.15.91 (alpha releases).
-   
+
    The sparse file map is stored in
    x header:
 
@@ -875,18 +878,18 @@ static struct tar_sparse_optab const star_optab = {
    Starting from this version, the exact sparse format version is specified
    explicitely in the header using the following variables:
 
-   GNU.sparse.major     Major version 
+   GNU.sparse.major     Major version
    GNU.sparse.minor     Minor version
 
    X header keeps the following variables:
-   
+
    GNU.sparse.name      Real file name of the sparse file
    GNU.sparse.realsize  Real size of the stored file (corresponds to the old
                         GNU.sparse.size variable)
 
    The name field of the ustar header is constructed using the pattern
    "%d/GNUSparseFile.%p/%f".
-   
+
    The sparse map itself is stored in the file data block, preceding the actual
    file data. It consists of a series of octal numbers of arbitrary length,
    delimited by newlines. The map is padded with nulls to the nearest block
@@ -915,6 +918,18 @@ pax_sparse_member_p (struct tar_sparse_file *file)
           || file->stat_info->sparse_major > 0;
 }
 
+/* Start a header that uses the effective (shrunken) file size.  */
+static union block *
+pax_start_header (struct tar_stat_info *st)
+{
+  off_t realsize = st->stat.st_size;
+  union block *blk;
+  st->stat.st_size = st->archive_file_size;
+  blk = start_header (st);
+  st->stat.st_size = realsize;
+  return blk;
+}
+
 static bool
 pax_dump_header_0 (struct tar_sparse_file *file)
 {
@@ -924,11 +939,11 @@ pax_dump_header_0 (struct tar_sparse_file *file)
   char nbuf[UINTMAX_STRSIZE_BOUND];
   struct sp_array *map = file->stat_info->sparse_map;
   char *save_file_name = NULL;
-  
+
   /* Store the real file size */
   xheader_store ("GNU.sparse.size", file->stat_info, NULL);
   xheader_store ("GNU.sparse.numblocks", file->stat_info, NULL);
-  
+
   if (xheader_keyword_deleted_p ("GNU.sparse.map")
       || tar_sparse_minor == 0)
     {
@@ -964,9 +979,7 @@ pax_dump_header_0 (struct tar_sparse_file *file)
          return false;
        }
     }
-  blk = start_header (file->stat_info);
-  /* Store the effective (shrunken) file size */
-  OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
+  blk = pax_start_header (file->stat_info);
   finish_header (file->stat_info, blk, block_ordinal);
   if (save_file_name)
     {
@@ -991,7 +1004,7 @@ pax_dump_header_1 (struct tar_sparse_file *file)
 #define COPY_STRING(b,dst,src) do                \
  {                                               \
    char *endp = b->buffer + BLOCKSIZE;           \
-   char *srcp = src;                             \
+   char const *srcp = src;                       \
    while (*srcp)                                 \
      {                                           \
        if (dst == endp)                          \
@@ -1003,7 +1016,7 @@ pax_dump_header_1 (struct tar_sparse_file *file)
         }                                       \
        *dst++ = *srcp++;                         \
      }                                           \
-   } while (0)                       
+   } while (0)
 
   /* Compute stored file size */
   p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
@@ -1018,19 +1031,20 @@ pax_dump_header_1 (struct tar_sparse_file *file)
   size = (size + BLOCKSIZE - 1) / BLOCKSIZE;
   file->stat_info->archive_file_size += size * BLOCKSIZE;
   file->dumped_size += size * BLOCKSIZE;
-  
+
   /* Store sparse file identification */
   xheader_store ("GNU.sparse.major", file->stat_info, NULL);
   xheader_store ("GNU.sparse.minor", file->stat_info, NULL);
   xheader_store ("GNU.sparse.name", file->stat_info, NULL);
   xheader_store ("GNU.sparse.realsize", file->stat_info, NULL);
-  
-  file->stat_info->file_name = xheader_format_name (file->stat_info,
-                                           "%d/GNUSparseFile.%p/%f", 0);
 
-  blk = start_header (file->stat_info);
-  /* Store the effective (shrunken) file size */
-  OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
+  file->stat_info->file_name =
+    xheader_format_name (file->stat_info, "%d/GNUSparseFile.%p/%f", 0);
+  /* Make sure the created header name is shorter than NAME_FIELD_SIZE: */
+  if (strlen (file->stat_info->file_name) > NAME_FIELD_SIZE)
+    file->stat_info->file_name[NAME_FIELD_SIZE] = 0;
+
+  blk = pax_start_header (file->stat_info);
   finish_header (file->stat_info, blk, block_ordinal);
   free (file->stat_info->file_name);
   file->stat_info->file_name = save_file_name;
@@ -1072,12 +1086,13 @@ decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
 
   if (!ISDIGIT (*arg))
     return false;
-  
+
+  errno = 0;
   u = strtoumax (arg, &arg_lim, 10);
 
   if (! (u <= maxval && errno != ERANGE) || *arg_lim)
     return false;
-  
+
   *num = u;
   return true;
 }
@@ -1117,7 +1132,7 @@ pax_decode_header (struct tar_sparse_file *file)
      }                                                             \
    while (*dst++ != '\n');                                         \
    dst[-1] = 0;                                                    \
- } while (0)                       
+ } while (0)
 
       set_next_block_after (current_header);
       file->dumped_size += BLOCKSIZE;
@@ -1126,7 +1141,7 @@ pax_decode_header (struct tar_sparse_file *file)
       COPY_BUF (blk,nbuf,p);
       if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
        {
-         ERROR ((0, 0, _("%s: malformed sparse archive member"), 
+         ERROR ((0, 0, _("%s: malformed sparse archive member"),
                  file->stat_info->orig_file_name));
          return false;
        }
@@ -1137,19 +1152,19 @@ pax_decode_header (struct tar_sparse_file *file)
       for (i = 0; i < file->stat_info->sparse_map_size; i++)
        {
          struct sp_array sp;
-         
+
          COPY_BUF (blk,nbuf,p);
          if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
            {
-             ERROR ((0, 0, _("%s: malformed sparse archive member"), 
+             ERROR ((0, 0, _("%s: malformed sparse archive member"),
                      file->stat_info->orig_file_name));
              return false;
            }
          sp.offset = u;
          COPY_BUF (blk,nbuf,p);
-         if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
+         if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
            {
-             ERROR ((0, 0, _("%s: malformed sparse archive member"), 
+             ERROR ((0, 0, _("%s: malformed sparse archive member"),
                      file->stat_info->orig_file_name));
              return false;
            }
@@ -1158,7 +1173,7 @@ pax_decode_header (struct tar_sparse_file *file)
        }
       set_next_block_after (blk);
     }
-  
+
   return true;
 }
 
@@ -1168,7 +1183,7 @@ static struct tar_sparse_optab const pax_optab = {
   pax_sparse_member_p,
   pax_dump_header,
   NULL,
-  pax_decode_header,  
+  pax_decode_header,
   NULL,  /* No scan_block function */
   sparse_dump_region,
   sparse_extract_region,
This page took 0.032793 seconds and 4 git commands to generate.