]> Dogcows Code - chaz/tar/blobdiff - src/buffer.c
Started merging with cpio into paxutils.
[chaz/tar] / src / buffer.c
index b735dca1858a38d5670864bb19f8964d1ac6125c..69812ec71597cfc502f207ef86cb7fab3ce71869 100644 (file)
@@ -1,7 +1,7 @@
 /* Buffer management for tar.
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003 Free Software Foundation, Inc.
+   2003, 2004 Free Software Foundation, Inc.
 
    Written by John Gilmore, on 1985-08-25.
 
@@ -19,7 +19,7 @@
    with this program; if not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
-#include "system.h"
+#include <system.h>
 
 #include <signal.h>
 
@@ -28,7 +28,7 @@
 #include <quotearg.h>
 
 #include "common.h"
-#include "rmt.h"
+#include <rmt.h>
 
 /* Number of retries before giving up on read.  */
 #define        READ_ERROR_MAX 10
@@ -40,6 +40,7 @@
 
 static tarlong prev_written;   /* bytes written on previous volumes */
 static tarlong bytes_written;  /* bytes written on this volume */
+static void *record_buffer;    /* allocated memory */
 
 /* FIXME: The following variables should ideally be static to this
    module.  However, this cannot be done yet.  The cleanup continues!  */
@@ -57,7 +58,7 @@ static off_t record_start_block; /* block ordinal at record_start */
 FILE *stdlis;
 
 static void backspace_output (void);
-static int new_volume (enum access_mode);
+static bool new_volume (enum access_mode);
 
 /* PID of child program, if compress_option or remote archive access.  */
 static pid_t child_pid;
@@ -71,12 +72,13 @@ static int hit_eof;
 /* Checkpointing counter */
 static int checkpoint;
 
-/* We're reading, but we just read the last block and its time to update.  */
-/* As least EXTERN like this one as possible.  FIXME!  */
-extern int time_to_start_writing;
+/* We're reading, but we just read the last block and it's time to update.
+   Declared in update.c
 
-int file_to_switch_to = -1;    /* if remote update, close archive, and use
-                                  this descriptor to write to */
+   As least EXTERN like this one as possible. (?? --gray)
+   FIXME: Either eliminate it or move it to common.h.
+*/
+extern bool time_to_start_writing;
 
 static int volno = 1;          /* which volume of a multi-volume tape we're
                                   on */
@@ -108,37 +110,58 @@ static off_t real_s_sizeleft;
 /* Functions.  */
 
 void
-clear_read_error_count ()
+clear_read_error_count (void)
 {
   read_error_count = 0;
 }
 
+\f
+/* Time-related functions */
+
+double duration;
+
 void
-print_total_written (void)
+set_start_time ()
 {
-  tarlong written = prev_written + bytes_written;
-  char bytes[sizeof (tarlong) * CHAR_BIT];
-  char abbr[LONGEST_HUMAN_READABLE + 1];
-  char rate[LONGEST_HUMAN_READABLE + 1];
-  double seconds;
-  int human_opts = human_autoscale | human_base_1024 | human_SI | human_B;
+#if HAVE_CLOCK_GETTIME
+  if (clock_gettime (CLOCK_REALTIME, &start_timespec) != 0)
+#endif
+    start_time = time (0);
+}
 
+void
+compute_duration ()
+{
 #if HAVE_CLOCK_GETTIME
   struct timespec now;
   if (clock_gettime (CLOCK_REALTIME, &now) == 0)
-    seconds = ((now.tv_sec - start_timespec.tv_sec)
-              + (now.tv_nsec - start_timespec.tv_nsec) / 1e9);
+    duration += ((now.tv_sec - start_timespec.tv_sec)
+                + (now.tv_nsec - start_timespec.tv_nsec) / 1e9);
   else
 #endif
-    seconds = time (0) - start_time;
+    duration += time (NULL) - start_time;
+  set_start_time ();
+}
+
+\f
+
+void
+print_total_written (void)
+{
+  tarlong written = prev_written + bytes_written;
+  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);
 
   /* Amanda 2.4.1p1 looks for "Total bytes written: [0-9][0-9]*".  */
   fprintf (stderr, _("Total bytes written: %s (%s, %s/s)\n"), bytes,
           human_readable (written, abbr, human_opts, 1, 1),
-          (0 < seconds && written / seconds < (uintmax_t) -1
-           ? human_readable (written / seconds, rate, human_opts, 1, 1)
+          (0 < duration && written / duration < (uintmax_t) -1
+           ? human_readable (written / duration, rate, human_opts, 1, 1)
            : "?"));
 }
 
@@ -182,8 +205,7 @@ find_next_block (void)
   return current_block;
 }
 
-/* Indicate that we have used all blocks up thru BLOCK.
-   FIXME: should the arg have an off-by-1?  */
+/* Indicate that we have used all blocks up thru BLOCK. */
 void
 set_next_block_after (union block *block)
 {
@@ -201,7 +223,7 @@ set_next_block_after (union block *block)
 /* Return the number of bytes comprising the space between POINTER
    through the end of the current buffer of blocks.  This space is
    available for filling with data, or taking data from.  POINTER is
-   usually (but not always) the result previous find_next_block call.  */
+   usually (but not always) the result of previous find_next_block call.  */
 size_t
 available_space_after (union block *pointer)
 {
@@ -220,20 +242,20 @@ xclose (int fd)
    pattern.  Return true if the pattern matches.  In case of failure,
    retry matching a volume sequence number before giving up in
    multi-volume mode.  */
-static int
+static bool
 check_label_pattern (union block *label)
 {
   char *string;
-  int result;
+  bool result;
 
   if (! memchr (label->header.name, '\0', sizeof label->header.name))
-    return 0;
+    return false;
 
   if (fnmatch (volume_label_option, label->header.name, 0) == 0)
-    return 1;
+    return true;
 
   if (!multi_volume_option)
-    return 0;
+    return false;
 
   string = xmalloc (strlen (volume_label_option)
                    + sizeof VOLUME_LABEL_APPEND + 1);
@@ -270,19 +292,12 @@ open_archive (enum access_mode wanted_access)
   save_name = 0;
   real_s_name = 0;
 
+  record_start =
+    page_aligned_alloc (&record_buffer,
+                       (record_size
+                        + (multi_volume_option ? 2 * BLOCKSIZE : 0)));
   if (multi_volume_option)
-    {
-      if (verify_option)
-       FATAL_ERROR ((0, 0, _("Cannot verify multi-volume archives")));
-      record_start = valloc (record_size + (2 * BLOCKSIZE));
-      if (record_start)
-       record_start += 2;
-    }
-  else
-    record_start = valloc (record_size);
-  if (!record_start)
-    FATAL_ERROR ((0, 0, _("Cannot allocate memory for blocking factor %d"),
-                 blocking_factor));
+    record_start += 2;
 
   current_block = record_start;
   record_end = record_start + blocking_factor;
@@ -291,11 +306,6 @@ open_archive (enum access_mode wanted_access)
 
   if (use_compress_program_option)
     {
-      if (multi_volume_option)
-       FATAL_ERROR ((0, 0, _("Cannot use multi-volume compressed archives")));
-      if (verify_option)
-       FATAL_ERROR ((0, 0, _("Cannot verify compressed archives")));
-
       switch (wanted_access)
        {
        case ACCESS_READ:
@@ -307,7 +317,7 @@ open_archive (enum access_mode wanted_access)
          break;
 
        case ACCESS_UPDATE:
-         FATAL_ERROR ((0, 0, _("Cannot update compressed archives")));
+         abort (); /* Should not happen */
          break;
        }
 
@@ -317,7 +327,7 @@ open_archive (enum access_mode wanted_access)
     }
   else if (strcmp (archive_name_array[0], "-") == 0)
     {
-      read_full_records_option = 1; /* could be a pipe, be safe */
+      read_full_records_option = true; /* could be a pipe, be safe */
       if (verify_option)
        FATAL_ERROR ((0, 0, _("Cannot verify stdin/stdout archive")));
 
@@ -335,7 +345,7 @@ open_archive (enum access_mode wanted_access)
        case ACCESS_UPDATE:
          archive = STDIN_FILENO;
          stdlis = stderr;
-         write_archive_to_stdout = 1;
+         write_archive_to_stdout = true;
          break;
        }
     }
@@ -415,15 +425,14 @@ open_archive (enum access_mode wanted_access)
          else
            strcpy (record_start->header.name, volume_label_option);
 
-         assign_string (&current_stat_info.file_name, record_start->header.name);
-         current_stat_info.had_trailing_slash = strip_trailing_slashes (current_stat_info.file_name);
+         assign_string (&current_stat_info.file_name,
+                        record_start->header.name);
+         current_stat_info.had_trailing_slash =
+           strip_trailing_slashes (current_stat_info.file_name);
 
          record_start->header.typeflag = GNUTYPE_VOLHDR;
          TIME_TO_CHARS (start_time, record_start->header.mtime);
          finish_header (&current_stat_info, record_start, -1);
-#if 0
-         current_block++;
-#endif
        }
       break;
     }
@@ -463,7 +472,7 @@ flush_write (void)
        {
          if (save_name)
            {
-             assign_string (&real_s_name, safer_name_suffix (save_name, 0));
+             assign_string (&real_s_name, safer_name_suffix (save_name, false));
              real_s_totsize = save_totsize;
              real_s_sizeleft = save_sizeleft;
            }
@@ -522,13 +531,18 @@ flush_write (void)
       if (volume_label_option)
        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"),
+                     quotearg_colon (real_s_name)));
+      
       memset (record_start, 0, BLOCKSIZE);
 
       /* FIXME: Michael P Urban writes: [a long name file] is being written
         when a new volume rolls around [...]  Looks like the wrong value is
         being preserved in real_s_name, though.  */
 
-      strcpy (record_start->header.name, real_s_name);
+      strncpy (record_start->header.name, real_s_name, NAME_FIELD_SIZE);
       record_start->header.typeflag = GNUTYPE_MULTIVOL;
       OFF_TO_CHARS (real_s_sizeleft, record_start->header.size);
       OFF_TO_CHARS (real_s_totsize - real_s_sizeleft,
@@ -562,7 +576,7 @@ flush_write (void)
        assign_string (&real_s_name, 0);
       else
        {
-         assign_string (&real_s_name, safer_name_suffix (save_name, 0));
+         assign_string (&real_s_name, safer_name_suffix (save_name, false));
          real_s_sizeleft = save_sizeleft;
          real_s_totsize = save_totsize;
        }
@@ -607,7 +621,7 @@ archive_read_error (void)
 }
 
 static void
-short_read (ssize_t status)
+short_read (size_t status)
 {
   size_t left;                 /* bytes left */
   char *more;                  /* pointer to next byte to read */
@@ -619,16 +633,23 @@ short_read (ssize_t status)
         || (left && status && read_full_records_option))
     {
       if (status)
-       while ((status = rmtread (archive, more, left)) < 0)
+       while ((status = rmtread (archive, more, left)) == SAFE_READ_ERROR)
          archive_read_error ();
 
       if (status == 0)
-       break;
+       {
+         char buf[UINTMAX_STRSIZE_BOUND];
+
+         WARN((0, 0, _("Read %s bytes from %s"),
+               STRINGIFY_BIGINT (record_size - left, buf),
+               *archive_name_cursor));
+         break;
+       }
 
       if (! read_full_records_option)
        {
          unsigned long rest = record_size - left;
-         
+
          FATAL_ERROR ((0, 0,
                        ngettext ("Unaligned block (%lu byte) in archive",
                                  "Unaligned block (%lu bytes) in archive",
@@ -645,8 +666,8 @@ short_read (ssize_t status)
   /* FIXME: for size=0, multi-volume support.  On the first record, warn
      about the problem.  */
 
-  if (!read_full_records_option && verbose_option
-      && record_start_block == 0 && status > 0)
+  if (!read_full_records_option && verbose_option > 1
+      && record_start_block == 0 && status != 0)
     {
       unsigned long rsize = (record_size - left) / BLOCKSIZE;
       WARN ((0, 0,
@@ -664,7 +685,7 @@ short_read (ssize_t status)
 void
 flush_read (void)
 {
-  ssize_t status;              /* result from system call */
+  size_t status;               /* result from system call */
 
   if (checkpoint_option && !(++checkpoint % 10))
     WARN ((0, 0, _("Read checkpoint %d"), checkpoint));
@@ -686,7 +707,7 @@ flush_read (void)
     {
       if (save_name)
        {
-         assign_string (&real_s_name, safer_name_suffix (save_name, 0));
+         assign_string (&real_s_name, safer_name_suffix (save_name, false));
          real_s_sizeleft = save_sizeleft;
          real_s_totsize = save_totsize;
        }
@@ -706,9 +727,14 @@ flush_read (void)
       return;
     }
 
+  /* The condition below used to include
+             || (status > 0 && !read_full_records_option)
+     This is incorrect since even if new_volume() succeeds, the
+     subsequent call to rmtread will overwrite the chunk of data
+     already read in the buffer, so the processing will fail */
+
   if ((status == 0
-       || (status < 0 && errno == ENOSPC)
-       || (status > 0 && !read_full_records_option))
+       || (status == SAFE_READ_ERROR && errno == ENOSPC))
       && multi_volume_option)
     {
       union block *cursor;
@@ -729,15 +755,12 @@ flush_read (void)
          break;
        }
 
-    vol_error:
-      status = rmtread (archive, record_start->buffer, record_size);
-      if (status < 0)
-       {
-         archive_read_error ();
-         goto vol_error;
-       }
+      while ((status = rmtread (archive, record_start->buffer, record_size))
+            == SAFE_READ_ERROR)
+       archive_read_error ();
+
       if (status != record_size)
-       short_read (status); 
+       short_read (status);
 
       cursor = record_start;
 
@@ -766,7 +789,7 @@ flush_read (void)
        {
          uintmax_t s1, s2;
          if (cursor->header.typeflag != GNUTYPE_MULTIVOL
-             || strcmp (cursor->header.name, real_s_name))
+             || strncmp (cursor->header.name, real_s_name, NAME_FIELD_SIZE))
            {
              WARN ((0, 0, _("%s is not continued on this volume"),
                     quote (real_s_name)));
@@ -805,7 +828,7 @@ flush_read (void)
       records_read++;
       return;
     }
-  else if (status < 0)
+  else if (status == SAFE_READ_ERROR)
     {
       archive_read_error ();
       goto error_loop;         /* try again */
@@ -825,17 +848,8 @@ flush_archive (void)
   if (access_mode == ACCESS_READ && time_to_start_writing)
     {
       access_mode = ACCESS_WRITE;
-      time_to_start_writing = 0;
-
-      if (file_to_switch_to >= 0)
-       {
-         if (rmtclose (archive) != 0)
-           close_warn (*archive_name_cursor);
-
-         archive = file_to_switch_to;
-       }
-      else
-       backspace_output ();
+      time_to_start_writing = false;
+      backspace_output ();
     }
 
   switch (access_mode)
@@ -896,6 +910,42 @@ backspace_output (void)
   }
 }
 
+off_t
+seek_archive (off_t size)
+{
+  off_t start = current_block_ordinal ();
+  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);
+  if (offset < 0)
+    return offset;
+
+  if (offset % record_size)
+    FATAL_ERROR ((0, 0, _("rmtlseek not stopped at a record boundary")));
+
+  /* Convert to number of records */
+  offset /= BLOCKSIZE;
+  /* Compute number of skipped blocks */
+  nblk = offset - start;
+
+  /* Update buffering info */
+  records_read += nblk / blocking_factor;
+  record_start_block = offset - blocking_factor;
+  current_block = record_end;
+  return nblk;
+}
+
 /* Close the archive file.  */
 void
 close_archive (void)
@@ -904,21 +954,22 @@ close_archive (void)
     flush_archive ();
 
   sys_drain_input_pipe ();
-  
-  if (verify_option)
+
+  compute_duration ();
+  if (verify_option) 
     verify_volume ();
 
   if (rmtclose (archive) != 0)
     close_warn (*archive_name_cursor);
 
   sys_wait_for_child (child_pid);
-  
+
   tar_stat_destroy (&current_stat_info);
   if (save_name)
     free (save_name);
   if (real_s_name)
     free (real_s_name);
-  free (multi_volume_option ? record_start - 2 : record_start);
+  free (record_buffer);
 }
 
 /* Called to initialize the global volume number.  */
@@ -961,9 +1012,10 @@ closeout_volume_number (void)
 }
 
 /* We've hit the end of the old volume.  Close it and open the next one.
-   Return nonzero on success.  */
-static int
-new_volume (enum access_mode access)
+   Return nonzero on success.
+*/
+static bool
+new_volume (enum access_mode mode)
 {
   static FILE *read_file;
   static int looped;
@@ -973,7 +1025,7 @@ new_volume (enum access_mode access)
     read_file = archive == STDIN_FILENO ? fopen (TTY_NAME, "r") : stdin;
 
   if (now_verifying)
-    return 0;
+    return false;
   if (verify_option)
     verify_volume ();
 
@@ -1034,6 +1086,7 @@ new_volume (enum access_mode access)
              {
              case '?':
                {
+                 /* FIXME: Might it be useful to disable the '!' command? */
                  fprintf (stderr, _("\
  n [name]   Give a new file name for the next (and subsequent) volume(s)\n\
  q          Abort tar\n\
@@ -1061,11 +1114,13 @@ new_volume (enum access_mode access)
                  char *name = &input_buffer[1];
                  char *cursor;
 
-                 while (*name == ' ' || *name == '\t')
-                   name++;
-                 cursor = name;
-                 while (*cursor && *cursor != '\n')
-                   cursor++;
+                 for (name = input_buffer + 1;
+                      *name == ' ' || *name == '\t';
+                      name++)
+                   ;
+
+                 for (cursor = name; *cursor && *cursor != '\n'; cursor++)
+                   ;
                  *cursor = '\0';
 
                  /* FIXME: the following allocation is never reclaimed.  */
@@ -1089,7 +1144,7 @@ new_volume (enum access_mode access)
     archive = rmtopen (*archive_name_cursor, O_RDWR | O_CREAT, MODE_RW,
                       rsh_command_option);
   else
-    switch (access)
+    switch (mode)
       {
       case ACCESS_READ:
        archive = rmtopen (*archive_name_cursor, O_RDONLY, MODE_RW,
@@ -1112,13 +1167,13 @@ new_volume (enum access_mode access)
   if (archive < 0)
     {
       open_warn (*archive_name_cursor);
-      if (!verify_option && access == ACCESS_WRITE && backup_option)
+      if (!verify_option && mode == ACCESS_WRITE && backup_option)
        undo_last_backup ();
       goto tryagain;
     }
 
   SET_BINARY_MODE (archive);
 
-  return 1;
+  return true;
 }
 
This page took 0.039037 seconds and 4 git commands to generate.