]> Dogcows Code - chaz/tar/blobdiff - src/buffer.c
Carefully crafted invalid headers can cause buffer overrun.
[chaz/tar] / src / buffer.c
index 0d142f7907e29909c7b61718bff8dc4995ee00fa..5977f5a56ed80b17ad6723646fcb46b405928d64 100644 (file)
@@ -1,7 +1,7 @@
 /* Buffer management for tar.
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003, 2004 Free Software Foundation, Inc.
+   2003, 2004, 2005 Free Software Foundation, Inc.
 
    Written by John Gilmore, on 1985-08-25.
 
@@ -17,7 +17,7 @@
 
    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.,
-   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 #include <system.h>
 
@@ -73,7 +73,6 @@ static bool hit_eof;
 static int checkpoint;
 
 static bool read_full_records = false;
-static bool reading_from_pipe = false;
 
 /* We're reading, but we just read the last block and it's time to update.
    Declared in update.c
@@ -126,23 +125,16 @@ double duration;
 void
 set_start_time ()
 {
-#if HAVE_CLOCK_GETTIME
-  if (clock_gettime (CLOCK_REALTIME, &start_timespec) != 0)
-#endif
-    start_time = time (0);
+  gettime (&start_time);
 }
 
 void
 compute_duration ()
 {
-#if HAVE_CLOCK_GETTIME
   struct timespec now;
-  if (clock_gettime (CLOCK_REALTIME, &now) == 0)
-    duration += ((now.tv_sec - start_timespec.tv_sec)
-                + (now.tv_nsec - start_timespec.tv_nsec) / 1e9);
-  else
-#endif
-    duration += time (NULL) - start_time;
+  gettime (&now);
+  duration += ((now.tv_sec - start_time.tv_sec)
+              + (now.tv_nsec - start_time.tv_nsec) / 1e9);
   set_start_time ();
 }
 
@@ -159,17 +151,17 @@ enum compress_type {
 struct zip_magic
 {
   enum compress_type type;
-  unsigned char *magic;
   size_t length;
+  char *magic;
   char *program;
   char *option;
 };
 
-static struct zip_magic magic[] = {
+static struct zip_magic const magic[] = {
   { ct_none, },
-  { ct_compress, "\037\235", 2, "compress", "-Z" },
-  { ct_gzip,     "\037\213", 2, "gzip", "-z"  },
-  { ct_bzip2,    "BZh",      3, "bzip2", "-j" },
+  { ct_compress, 2, "\037\235", "compress", "-Z" },
+  { ct_gzip,     2, "\037\213", "gzip", "-z"  },
+  { ct_bzip2,    3, "BZh",      "bzip2", "-j" },
 };
 
 #define NMAGIC (sizeof(magic)/sizeof(magic[0]))
@@ -177,34 +169,31 @@ static struct zip_magic magic[] = {
 #define compress_option(t) magic[t].option
 #define compress_program(t) magic[t].program
 
-/* Check if the file FD is a compressed archive. FD is guaranteed to
-   represent a local file */
-enum compress_type 
-check_compressed_archive (int fd)
+/* Check if the file ARCHIVE is a compressed archive. */
+enum compress_type
+check_compressed_archive ()
 {
-  struct zip_magic *p;
-  size_t status;
-  union block buf;
-  
-  status = read (fd, &buf, sizeof buf);
-  if (status != sizeof buf)
-    {
-      archive_read_error ();
-      FATAL_ERROR ((0, 0, _("Quitting now.")));
-    }
+  struct zip_magic const *p;
+  bool sfr;
 
-  lseek (fd, 0, SEEK_SET); /* This will fail if fd==0, but that does not
-                             matter, since we do not handle compressed
-                             stdin anyway */
-  
-  if (tar_checksum (&buf) == HEADER_SUCCESS)
+  /* Prepare global data needed for find_next_block: */
+  record_end = record_start; /* set up for 1st record = # 0 */
+  sfr = read_full_records;
+  read_full_records = true; /* Suppress fatal error on reading a partial
+                              record */
+  find_next_block ();
+
+  /* Restore global values */
+  read_full_records = sfr;
+
+  if (tar_checksum (record_start, true) == HEADER_SUCCESS)
     /* Probably a valid header */
     return ct_none;
 
   for (p = magic + 1; p < magic + NMAGIC; p++)
-    if (memcmp (buf.buffer, p->magic, p->length) == 0)
+    if (memcmp (record_start->buffer, p->magic, p->length) == 0)
       return p->type;
-  
+
   return ct_none;
 }
 
@@ -214,33 +203,32 @@ check_compressed_archive (int fd)
 int
 open_compressed_archive ()
 {
-  enum compress_type type;
-  int fd = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
-                   MODE_RW, rsh_command_option);
-  if (fd == -1 || _isrmt (fd))
-    return fd;
-  
-  type = check_compressed_archive (fd);
-  
-  if (type == ct_none)
+  archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
+                    MODE_RW, rsh_command_option);
+  if (archive == -1)
+    return archive;
+
+  if (!multi_volume_option)
     {
-      if (rmtlseek (fd, (off_t) 0, SEEK_CUR) != 0)
-       {
-         /* Archive may be not seekable. Reopen it. */
-         rmtclose (fd);
-         fd = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
-                       MODE_RW, rsh_command_option);
-       }
-      return fd;
-    }
+      enum compress_type type = check_compressed_archive ();
+
+      if (type == ct_none)
+       return archive;
 
-  /* FD is not needed any more */
-  rmtclose (fd);
+      /* FD is not needed any more */
+      rmtclose (archive);
 
-  /* Open compressed archive */
-  use_compress_program_option = compress_program (type);
-  child_pid = sys_child_open_for_uncompress ();
-  read_full_records = reading_from_pipe = true;
+      hit_eof = false; /* It might have been set by find_next_block in
+                         check_compressed_archive */
+
+      /* Open compressed archive */
+      use_compress_program_option = compress_program (type);
+      child_pid = sys_child_open_for_uncompress ();
+      read_full_records = true;
+    }
+
+  records_read = 0;
+  record_end = record_start; /* set up for 1st record = # 0 */
 
   return archive;
 }
@@ -253,7 +241,7 @@ print_total_written (void)
   char bytes[sizeof (tarlong) * CHAR_BIT];
   char abbr[LONGEST_HUMAN_READABLE + 1];
   char rate[LONGEST_HUMAN_READABLE + 1];
-  
+
   int human_opts = human_autoscale | human_base_1024 | human_SI | human_B;
 
   sprintf (bytes, TARLONG_FORMAT, written);
@@ -406,15 +394,17 @@ open_archive (enum access_mode wanted_access)
   access_mode = wanted_access == ACCESS_UPDATE ? ACCESS_READ : wanted_access;
 
   read_full_records = read_full_records_option;
-  reading_from_pipe = false;
-  
+
+  records_read = 0;
+
   if (use_compress_program_option)
     {
       switch (wanted_access)
        {
        case ACCESS_READ:
          child_pid = sys_child_open_for_uncompress ();
-         read_full_records = reading_from_pipe = true;
+         read_full_records = true;
+         record_end = record_start; /* set up for 1st record = # 0 */
          break;
 
        case ACCESS_WRITE:
@@ -441,7 +431,7 @@ open_archive (enum access_mode wanted_access)
        case ACCESS_READ:
          {
            enum compress_type type;
-           
+
            archive = STDIN_FILENO;
 
            type = check_compressed_archive (archive);
@@ -509,9 +499,9 @@ open_archive (enum access_mode wanted_access)
     {
     case ACCESS_UPDATE:
       records_written = 0;
-    case ACCESS_READ:
-      records_read = 0;
       record_end = record_start; /* set up for 1st record = # 0 */
+
+    case ACCESS_READ:
       find_next_block ();      /* read it in, check for EOF */
 
       if (volume_label_option)
@@ -545,7 +535,7 @@ open_archive (enum access_mode wanted_access)
            strip_trailing_slashes (current_stat_info.file_name);
 
          record_start->header.typeflag = GNUTYPE_VOLHDR;
-         TIME_TO_CHARS (start_time, record_start->header.mtime);
+         TIME_TO_CHARS (start_time.tv_sec, record_start->header.mtime);
          finish_header (&current_stat_info, record_start, -1);
        }
       break;
@@ -560,6 +550,10 @@ flush_write (void)
   ssize_t status;
 
   if (checkpoint_option && !(++checkpoint % 10))
+    /* TRANSLATORS: This is a ``checkpoint of write operation'',
+       *not* ``Writing a checkpoint''.
+       E.g. in Spanish ``Punto de comprobaci@'on de escritura'',
+       *not* ``Escribiendo un punto de comprobaci@'on'' */
     WARN ((0, 0, _("Write checkpoint %d"), checkpoint));
 
   if (tape_length_option && tape_length_option <= bytes_written)
@@ -586,7 +580,9 @@ flush_write (void)
        {
          if (save_name)
            {
-             assign_string (&real_s_name, safer_name_suffix (save_name, false));
+             assign_string (&real_s_name,
+                            safer_name_suffix (save_name, false,
+                                               absolute_names_option));
              real_s_totsize = save_totsize;
              real_s_sizeleft = save_sizeleft;
            }
@@ -633,7 +629,7 @@ flush_write (void)
       memset (record_start, 0, BLOCKSIZE);
       sprintf (record_start->header.name, "%s Volume %d",
               volume_label_option, volno);
-      TIME_TO_CHARS (start_time, record_start->header.mtime);
+      TIME_TO_CHARS (start_time.tv_sec, record_start->header.mtime);
       record_start->header.typeflag = GNUTYPE_VOLHDR;
       finish_header (&current_stat_info, record_start, -1);
     }
@@ -646,10 +642,10 @@ flush_write (void)
        record_start++;
 
       if (strlen (real_s_name) > NAME_FIELD_SIZE)
-       FATAL_ERROR ((0, 0,
-                     _("%s: file name too long to be stored in a GNU multivolume header"),
+       WARN ((0, 0,
+             _("%s: file name too long to be stored in a GNU multivolume header, truncated"),
                      quotearg_colon (real_s_name)));
-      
+
       memset (record_start, 0, BLOCKSIZE);
 
       /* FIXME: Michael P Urban writes: [a long name file] is being written
@@ -662,7 +658,7 @@ flush_write (void)
       OFF_TO_CHARS (real_s_sizeleft, record_start->header.size);
       OFF_TO_CHARS (real_s_totsize - real_s_sizeleft,
                    record_start->oldgnu_header.offset);
-      
+
       tmp = verbose_option;
       verbose_option = 0;
       finish_header (&current_stat_info, record_start, -1);
@@ -692,7 +688,9 @@ flush_write (void)
        assign_string (&real_s_name, 0);
       else
        {
-         assign_string (&real_s_name, safer_name_suffix (save_name, false));
+         assign_string (&real_s_name,
+                        safer_name_suffix (save_name, false,
+                                           absolute_names_option));
          real_s_sizeleft = save_sizeleft;
          real_s_totsize = save_totsize;
        }
@@ -753,17 +751,7 @@ short_read (size_t status)
          archive_read_error ();
 
       if (status == 0)
-       {
-         if (!reading_from_pipe)
-           {
-             char buf[UINTMAX_STRSIZE_BOUND];
-
-             WARN((0, 0, _("Read %s bytes from %s"),
-                   STRINGIFY_BIGINT (record_size - left, buf),
-                   *archive_name_cursor));
-           }
-         break;
-       }
+       break;
 
       if (! read_full_records)
        {
@@ -807,6 +795,10 @@ flush_read (void)
   size_t status;               /* result from system call */
 
   if (checkpoint_option && !(++checkpoint % 10))
+    /* TRANSLATORS: This is a ``checkpoint of read operation'',
+       *not* ``Reading a checkpoint''.
+       E.g. in Spanish ``Punto de comprobaci@'on de lectura'',
+       *not* ``Leyendo un punto de comprobaci@'on'' */
     WARN ((0, 0, _("Read checkpoint %d"), checkpoint));
 
   /* Clear the count of errors.  This only applies to a single call to
@@ -826,7 +818,9 @@ flush_read (void)
     {
       if (save_name)
        {
-         assign_string (&real_s_name, safer_name_suffix (save_name, false));
+         assign_string (&real_s_name,
+                        safer_name_suffix (save_name, false,
+                                           absolute_names_option));
          real_s_sizeleft = save_sizeleft;
          real_s_totsize = save_totsize;
        }
@@ -1036,13 +1030,13 @@ seek_archive (off_t size)
   off_t offset;
   off_t nrec, nblk;
   off_t skipped = (blocking_factor - (current_block - record_start));
-  
+
   size -= skipped * BLOCKSIZE;
-  
+
   if (size < record_size)
     return 0;
   /* FIXME: flush? */
-  
+
   /* Compute number of records to skip */
   nrec = size / record_size;
   offset = rmtlseek (archive, nrec * record_size, SEEK_CUR);
@@ -1061,7 +1055,7 @@ seek_archive (off_t size)
   records_read += nblk / blocking_factor;
   record_start_block = offset - blocking_factor;
   current_block = record_end;
+
   return nblk;
 }
 
@@ -1075,7 +1069,7 @@ close_archive (void)
   sys_drain_input_pipe ();
 
   compute_duration ();
-  if (verify_option) 
+  if (verify_option)
     verify_volume ();
 
   if (rmtclose (archive) != 0)
@@ -1172,7 +1166,8 @@ new_volume (enum access_mode mode)
          if (volno_file_option)
            closeout_volume_number ();
          if (system (info_script_option) != 0)
-           FATAL_ERROR ((0, 0, _("`%s' command failed"), info_script_option));
+           FATAL_ERROR ((0, 0, _("%s command failed"),
+                         quote (info_script_option)));
        }
       else
        while (1)
@@ -1295,4 +1290,3 @@ new_volume (enum access_mode mode)
 
   return true;
 }
-
This page took 0.029533 seconds and 4 git commands to generate.