]> Dogcows Code - chaz/tar/blobdiff - src/create.c
Lint cleanup.
[chaz/tar] / src / create.c
index 114688fd79d31a73824ee5b984877d72557cf0fb..42c82fffe71d2a7b9716cf42ca6e31f7506f965f 100644 (file)
@@ -1,5 +1,5 @@
 /* Create a tar archive.
-   Copyright (C) 1985, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
+   Copyright 1985, 92, 93, 94, 96, 97, 1999 Free Software Foundation, Inc.
    Written by John Gilmore, on 1985-08-25.
 
    This program is free software; you can redistribute it and/or modify it
@@ -14,7 +14,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 Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "system.h"
 
@@ -55,37 +55,246 @@ struct link
 
 struct link *linklist = NULL;  /* points to first link in list */
 \f
+/* Base 64 digits; see Internet RFC 2045 Table 1.  */
+char const base_64_digits[64] =
+{
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+};
+#define base_8_digits (base_64_digits + 26 * 2)
+
+/* The maximum uintmax_t value that can be represented with DIGITS digits,
+   assuming that each digit is BITS_PER_DIGIT wide.  */
+#define MAX_VAL_WITH_DIGITS(digits, bits_per_digit) \
+   ((digits) * (bits_per_digit) < sizeof (uintmax_t) * CHAR_BIT \
+    ? ((uintmax_t) 1 << ((digits) * (bits_per_digit))) - 1 \
+    : (uintmax_t) -1)
+
+/* Convert VALUE to a representation suitable for tar headers,
+   using base 1 << BITS_PER_DIGIT.
+   Use the digits in DIGIT_CHAR[0] ... DIGIT_CHAR[base - 1].
+   Output to buffer WHERE with size SIZE.
+   The result is undefined if SIZE is 0 or if VALUE is too large to fit.  */
+
+static void
+to_base (uintmax_t value, int bits_per_digit, char const *digit_char,
+        char *where, size_t size)
+{
+  uintmax_t v = value;
+  size_t i = size;
+  unsigned digit_mask = (1 << bits_per_digit) - 1;
+
+  do
+    {
+      where[--i] = digit_char[v & digit_mask];
+      v >>= bits_per_digit;
+    }
+  while (i);
+}
+
+/* NEGATIVE is nonzero if VALUE was negative before being cast to
+   uintmax_t; its original bitpattern can be deduced from VALSIZE, its
+   original size before casting.  Convert VALUE to external form,
+   using SUBSTITUTE (...)  if VALUE won't fit.  Output to buffer WHERE
+   with size SIZE.  TYPE is the kind of value being output (useful for
+   diagnostics).  Prefer the POSIX format of SIZE - 1 octal digits
+   (with leading zero digits), followed by '\0'.  If this won't work,
+   and if GNU format is allowed, use '+' or '-' followed by SIZE - 1
+   base-64 digits.  If neither format works, use SUBSTITUTE (...)
+   instead.  Pass to SUBSTITUTE the address of an 0-or-1 flag
+   recording whether the substitute value is negative.  */
 
-/*------------------------------------------------------------------------.
-| Converts long VALUE into a DIGS-digit field at WHERE, including a       |
-| trailing space and room for a NUL.  For example, 3 for DIGS 3 means one |
-| digit, a space, and room for a NUL.                                     |
-|                                                                         |
-| We assume the trailing NUL is already there and don't fill it in.  This |
-| fact is used by start_header and finish_header, so don't change it!     |
-`------------------------------------------------------------------------*/
+static void
+to_chars (int negative, uintmax_t value, size_t valsize,
+         uintmax_t (*substitute) PARAMS ((int *)),
+         char *where, size_t size, const char *type)
+{
+  uintmax_t v = negative ? -value : value;
 
-/* This should be equivalent to: sprintf (WHERE, "%*lo ", DIGS - 2, VALUE);
-   except that sprintf fills in the trailing NUL and we don't.  */
+  /* Generate the POSIX octal representation if the number fits.  */
+  if (! negative && v <= MAX_VAL_WITH_DIGITS (size - 1, LG_8))
+    {
+      where[size - 1] = '\0';
+      to_base (v, LG_8, base_8_digits, where, size - 1);
+    }
+
+  /* Otherwise, generate the GNU base-64 representation if we are
+     generating an old or new GNU format and if the number fits.  */
+  else if (v <= MAX_VAL_WITH_DIGITS (size - 1, LG_64)
+          && (archive_format == GNU_FORMAT
+              || archive_format == OLDGNU_FORMAT))
+    {
+      where[0] = negative ? '-' : '+';
+      to_base (v, LG_64, base_64_digits, where + 1, size - 1);
+    }
+
+  /* Otherwise, if the number is negative, and if it would not cause
+     ambiguity on this host by confusing positive with negative
+     values, then generate the POSIX octal representation of the value
+     modulo 2**(field bits).  The resulting tar file is
+     machine-dependent, since it depends on the host word size.  Yuck!
+     But this is the traditional behavior.  */
+  else if (negative && valsize * CHAR_BIT <= (size - 1) * LG_8)
+    {
+      where[size - 1] = '\0';
+      to_base (value & MAX_VAL_WITH_DIGITS (valsize * CHAR_BIT, 1),
+              LG_8, base_8_digits, where, size - 1);
+    }
+
+  /* Otherwise, output a substitute value if possible (with a
+     warning), and an error message if not.  */
+  else
+    {
+      uintmax_t maxval = (archive_format == GNU_FORMAT
+                         ? MAX_VAL_WITH_DIGITS (size - 1, LG_64)
+                         : MAX_VAL_WITH_DIGITS (size - 1, LG_8));
+      char buf1[UINTMAX_STRSIZE_BOUND + 1];
+      char buf2[UINTMAX_STRSIZE_BOUND + 1];
+      char buf3[UINTMAX_STRSIZE_BOUND + 1];
+      char *value_string = STRINGIFY_BIGINT (v, buf1 + 1);
+      char *maxval_string = STRINGIFY_BIGINT (maxval, buf2 + 1);
+      char const *minval_string =
+       (archive_format == GNU_FORMAT
+        ? "0"
+        : (maxval_string[-1] = '-', maxval_string - 1));
+      if (negative)
+       *--value_string = '-';
+      if (substitute)
+       {
+         int negsub;
+         uintmax_t sub = substitute (&negsub) & maxval;
+         uintmax_t s = (negsub &= archive_format == GNU_FORMAT) ? -sub : sub;
+         char *sub_string = STRINGIFY_BIGINT (s, buf3 + 1);
+         if (negsub)
+           *--sub_string = '-';
+         WARN ((0, 0, _("%s value %s out of range %s..%s; substituting %s"),
+                type, value_string, minval_string, maxval_string,
+                sub_string));
+         to_chars (negsub, s, valsize, NULL, where, size, type);
+       }
+      else
+       ERROR ((0, 0, _("%s value %s out of range %s..%s"),
+               type, value_string, minval_string, maxval_string));
+    }
+}
+
+static uintmax_t
+gid_substitute (int *negative)
+{
+  gid_t r;
+#ifdef GID_NOBODY
+  r = GID_NOBODY;
+#else
+  static gid_t gid_nobody;
+  if (!gid_nobody && !gname_to_gid ("nobody", &gid_nobody))
+    gid_nobody = -2;
+  r = gid_nobody;
+#endif
+  *negative = r < 0;
+  return r;
+}
 
 void
-to_oct (long value, int digs, char *where)
+gid_to_chars (gid_t v, char *p, size_t s)
 {
-  --digs;                      /* Trailing null slot is left alone */
-  where[--digs] = ' ';         /* put in the space, though */
+  to_chars (v < 0, (uintmax_t) v, sizeof v, gid_substitute, p, s, "gid_t");
+}
 
-  /* Produce the digits -- at least one.  */
+void
+major_to_chars (major_t v, char *p, size_t s)
+{
+  to_chars (v < 0, (uintmax_t) v, sizeof v, NULL, p, s, "major_t");
+}
 
-  do
+void
+minor_to_chars (minor_t v, char *p, size_t s)
+{
+  to_chars (v < 0, (uintmax_t) v, sizeof v, NULL, p, s, "minor_t");
+}
+
+void
+mode_to_chars (mode_t v, char *p, size_t s)
+{
+  /* In the common case where the internal and external mode bits are the same,
+     propagate all unknown bits to the external mode.
+     This matches historical practice.
+     Otherwise, just copy the bits we know about.  */
+  int negative;
+  uintmax_t u;
+  if (S_ISUID == TSUID && S_ISGID == TSGID && S_ISVTX == TSVTX
+      && S_IRUSR == TUREAD && S_IWUSR == TUWRITE && S_IXUSR == TUEXEC
+      && S_IRGRP == TGREAD && S_IWGRP == TGWRITE && S_IXGRP == TGEXEC
+      && S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC)
+    {
+      negative = v < 0;
+      u = v;
+    }
+  else
     {
-      where[--digs] = '0' + (char) (value & 7);        /* one octal digit */
-      value >>= 3;
+      negative = 0;
+      u = ((v & S_ISUID ? TSUID : 0)
+          | (v & S_ISGID ? TSGID : 0)
+          | (v & S_ISVTX ? TSVTX : 0)
+          | (v & S_IRUSR ? TUREAD : 0)
+          | (v & S_IWUSR ? TUWRITE : 0)
+          | (v & S_IXUSR ? TUEXEC : 0)
+          | (v & S_IRGRP ? TGREAD : 0)
+          | (v & S_IWGRP ? TGWRITE : 0)
+          | (v & S_IXGRP ? TGEXEC : 0)
+          | (v & S_IROTH ? TOREAD : 0)
+          | (v & S_IWOTH ? TOWRITE : 0)
+          | (v & S_IXOTH ? TOEXEC : 0));
     }
-  while (digs > 0 && value != 0);
+  to_chars (negative, u, sizeof v, NULL, p, s, "mode_t");
+}
+
+void
+off_to_chars (off_t v, char *p, size_t s)
+{
+  to_chars (v < 0, (uintmax_t) v, sizeof v, NULL, p, s, "off_t");
+}
 
-  /* Leading spaces, if necessary.  */
-  while (digs > 0)
-    where[--digs] = ' ';
+void
+size_to_chars (size_t v, char *p, size_t s)
+{
+  to_chars (0, (uintmax_t) v, sizeof v, NULL, p, s, "size_t");
+}
+
+void
+time_to_chars (time_t v, char *p, size_t s)
+{
+  to_chars (v < 0, (uintmax_t) v, sizeof v, NULL, p, s, "time_t");
+}
+
+static uintmax_t
+uid_substitute (int *negative)
+{
+  uid_t r;
+#ifdef UID_NOBODY
+  r = UID_NOBODY;
+#else
+  static uid_t uid_nobody;
+  if (!uid_nobody && !uname_to_uid ("nobody", &uid_nobody))
+    uid_nobody = -2;
+  r = uid_nobody;
+#endif
+  *negative = r < 0;
+  return r;
+}
+
+void
+uid_to_chars (uid_t v, char *p, size_t s)
+{
+  to_chars (v < 0, (uintmax_t) v, sizeof v, uid_substitute, p, s, "uid_t");
+}
+
+void
+uintmax_to_chars (uintmax_t v, char *p, size_t s)
+{
+  to_chars (0, v, sizeof v, NULL, p, s, "uintmax_t");
 }
 \f
 /* Writing routines.  */
@@ -102,7 +311,7 @@ clear_buffer (char *buffer)
 }
 
 /*-------------------------------------------------------------------------.
-| Write the EOT block(s).  We actually zero at least one block, through           |
+| Write the EOT block(s).  We zero at least two blocks, through                   |
 | the end of the record.  Old tar, as previous versions of GNU tar, writes |
 | garbage after two zeroed blocks.                                        |
 `-------------------------------------------------------------------------*/
@@ -111,14 +320,11 @@ void
 write_eot (void)
 {
   union block *pointer = find_next_block ();
-
-  if (pointer)
-    {
-      int space = available_space_after (pointer);
-
-      memset (pointer->buffer, 0, (size_t) space);
-      set_next_block_after (pointer);
-    }
+  memset (pointer->buffer, 0, BLOCKSIZE);
+  set_next_block_after (pointer);
+  pointer = find_next_block ();
+  memset (pointer->buffer, 0, available_space_after (pointer));
+  set_next_block_after (pointer);
 }
 
 /*-----------------------------------------------------.
@@ -132,8 +338,8 @@ static union block *start_header PARAMS ((const char *, struct stat *));
 static void
 write_long (const char *p, char type)
 {
-  int size = strlen (p) + 1;
-  int bufsize;
+  size_t size = strlen (p) + 1;
+  size_t bufsize;
   union block *header;
   struct stat foo;
 
@@ -150,15 +356,15 @@ write_long (const char *p, char type)
 
   while (bufsize < size)
     {
-      memcpy (header->buffer, p, (size_t) bufsize);
+      memcpy (header->buffer, p, bufsize);
       p += bufsize;
       size -= bufsize;
       set_next_block_after (header + (bufsize - 1) / BLOCKSIZE);
       header = find_next_block ();
       bufsize = available_space_after (header);
     }
-  memcpy (header->buffer, p, (size_t) size);
-  memset (header->buffer + size, 0, (size_t) (bufsize - size));
+  memcpy (header->buffer, p, size);
+  memset (header->buffer + size, 0, bufsize - size);
   set_next_block_after (header + (size - 1) / BLOCKSIZE);
 }
 \f
@@ -176,29 +382,29 @@ start_header (const char *name, struct stat *st)
 
   if (!absolute_names_option)
     {
-      static int warned_once = 0;
+      size_t prefix_len = FILESYSTEM_PREFIX_LEN (name);
 
-#if MSDOS
-      if (name[1] == ':')
+      if (prefix_len)
        {
-         name += 2;
+         static int warned_once;
          if (!warned_once)
            {
              warned_once = 1;
-             WARN ((0, 0, _("Removing drive spec from names in the archive")));
+             WARN ((0, 0, _("Removing `%.*s' prefix from archive names"),
+                    (int) prefix_len, name));
            }
+         name += prefix_len;
        }
-#endif
 
       while (*name == '/')
        {
-         name++;               /* force relative path */
+         static int warned_once;
          if (!warned_once)
            {
              warned_once = 1;
-             WARN ((0, 0, _("\
-Removing leading `/' from absolute path names in the archive")));
+             WARN ((0, 0, _("Removing leading `/' from archive names")));
            }
+         name++;
        }
     }
 
@@ -221,7 +427,7 @@ Removing leading `/' from absolute path names in the archive")));
   if (group_option != (gid_t) -1)
     st->st_gid = group_option;
   if (mode_option)
-    st->st_mode = ((st->st_mode & S_IFMT)
+    st->st_mode = ((st->st_mode & ~MODE_ALL)
                   | mode_adjust (st->st_mode, mode_option));
 
   /* Paul Eggert tried the trivial test ($WRITER cf a b; $READER tvf a)
@@ -248,27 +454,26 @@ Removing leading `/' from absolute path names in the archive")));
      acceptor for Paul's test.  */
 
   if (archive_format == V7_FORMAT)
-    to_oct ((long) st->st_mode & 07777, 8, header->header.mode);
+    MODE_TO_CHARS (st->st_mode & MODE_ALL, header->header.mode);
   else
-    to_oct ((long) st->st_mode, 8, header->header.mode);
+    MODE_TO_CHARS (st->st_mode, header->header.mode);
 
-  to_oct ((long) st->st_uid, 8, header->header.uid);
-  to_oct ((long) st->st_gid, 8, header->header.gid);
-  to_oct ((long) st->st_size, 1 + 12, header->header.size);
-  to_oct ((long) st->st_mtime, 1 + 12, header->header.mtime);
+  UID_TO_CHARS (st->st_uid, header->header.uid);
+  GID_TO_CHARS (st->st_gid, header->header.gid);
+  OFF_TO_CHARS (st->st_size, header->header.size);
+  TIME_TO_CHARS (st->st_mtime, header->header.mtime);
 
   if (incremental_option)
     if (archive_format == OLDGNU_FORMAT)
       {
-       to_oct ((long) st->st_atime, 1 + 12, header->oldgnu_header.atime);
-       to_oct ((long) st->st_ctime, 1 + 12, header->oldgnu_header.ctime);
+       TIME_TO_CHARS (st->st_atime, header->oldgnu_header.atime);
+       TIME_TO_CHARS (st->st_ctime, header->oldgnu_header.ctime);
       }
 
   header->header.typeflag = archive_format == V7_FORMAT ? AREGTYPE : REGTYPE;
 
   switch (archive_format)
     {
-    case DEFAULT_FORMAT:
     case V7_FORMAT:
       break;
 
@@ -282,6 +487,9 @@ Removing leading `/' from absolute path names in the archive")));
       strncpy (header->header.magic, TMAGIC, TMAGLEN);
       strncpy (header->header.version, TVERSION, TVERSLEN);
       break;
+
+    default:
+      abort ();
     }
 
   if (archive_format == V7_FORMAT || numeric_owner_option)
@@ -305,29 +513,29 @@ Removing leading `/' from absolute path names in the archive")));
 void
 finish_header (union block *header)
 {
-  int i, sum;
+  size_t i;
+  int sum;
   char *p;
 
   memcpy (header->header.chksum, CHKBLANKS, sizeof (header->header.chksum));
 
   sum = 0;
   p = header->buffer;
-  for (i = sizeof (*header); --i >= 0; )
+  for (i = sizeof (*header); i-- != 0; )
     /* We can't use unsigned char here because of old compilers, e.g. V7.  */
     sum += 0xFF & *p++;
 
   /* Fill in the checksum field.  It's formatted differently from the
      other fields: it has [6] digits, a null, then a space -- rather than
-     digits, a space, then a null.  We use to_oct then write the null in
-     over to_oct's space.  The final space is already there, from
-     checksumming, and to_oct doesn't modify it.
+     digits, then a null.  We use to_chars.
+     The final space is already there, from
+     checksumming, and to_chars doesn't modify it.
 
      This is a fast way to do:
 
      sprintf(header->header.chksum, "%6o", sum);  */
 
-  to_oct ((long) sum, 8, header->header.chksum);
-  header->header.chksum[6] = '\0';     /* zap the space */
+  uintmax_to_chars ((uintmax_t) sum, header->header.chksum, 7);
 
   set_next_block_after (header);
 
@@ -390,7 +598,7 @@ init_sparsearray (void)
 `---*/
 
 static void
-find_new_file_size (int *filesize, int highest_index)
+find_new_file_size (off_t *filesize, int highest_index)
 {
   int counter;
 
@@ -417,11 +625,11 @@ find_new_file_size (int *filesize, int highest_index)
 static int
 deal_with_sparse (char *name, union block *header)
 {
-  long numbytes = 0;
-  long offset = 0;
+  size_t numbytes = 0;
+  off_t offset = 0;
   int file;
   int sparse_index = 0;
-  int count;
+  ssize_t count;
   char buffer[BLOCKSIZE];
 
   if (archive_format == OLDGNU_FORMAT)
@@ -434,7 +642,7 @@ deal_with_sparse (char *name, union block *header)
   init_sparsearray ();
   clear_buffer (buffer);
 
-  while (count = read (file, buffer, sizeof buffer), count != 0)
+  while (count = safe_read (file, buffer, sizeof buffer), count != 0)
     {
       /* Realloc the scratch area as necessary.  FIXME: should reallocate
         only at beginning of a new instance of non-zero data.  */
@@ -511,14 +719,12 @@ deal_with_sparse (char *name, union block *header)
 `---*/
 
 static int
-finish_sparse_file (int file, long *sizeleft, long fullsize, char *name)
+finish_sparse_file (int file, off_t *sizeleft, off_t fullsize, char *name)
 {
   union block *start;
-  int bufsize;
+  size_t bufsize;
   int sparse_index = 0;
-  int count;
-  long pos;
-  long nwritten = 0;
+  ssize_t count;
 
   while (*sizeleft > 0)
     {
@@ -528,12 +734,24 @@ finish_sparse_file (int file, long *sizeleft, long fullsize, char *name)
       if (!bufsize)
        {
          /* We blew it, maybe.  */
+         char buf1[UINTMAX_STRSIZE_BOUND];
+         char buf2[UINTMAX_STRSIZE_BOUND];
+
+         ERROR ((0, 0, _("Wrote %s of %s bytes to file %s"),
+                 STRINGIFY_BIGINT (fullsize - *sizeleft, buf1),
+                 STRINGIFY_BIGINT (fullsize, buf2),
+                 name));
+         break;
+       }
 
-         ERROR ((0, 0, _("Wrote %ld of %ld bytes to file %s"),
-                 fullsize - *sizeleft, fullsize, name));
+      if (lseek (file, sparsearray[sparse_index++].offset, SEEK_SET) < 0)
+       {
+         char buf[UINTMAX_STRSIZE_BOUND];
+         ERROR ((0, errno, _("lseek error at byte %s in file %s"),
+                 STRINGIFY_BIGINT (sparsearray[sparse_index - 1].offset, buf),
+                 name));
          break;
        }
-      pos = lseek (file, sparsearray[sparse_index++].offset, 0);
 
       /* If the number of bytes to be written here exceeds the size of
         the temporary buffer, do it in steps.  */
@@ -543,8 +761,8 @@ finish_sparse_file (int file, long *sizeleft, long fullsize, char *name)
 #if 0
          if (amount_read)
            {
-             count = read (file, start->buffer + amount_read,
-                           BLOCKSIZE - amount_read);
+             count = safe_read (file, start->buffer + amount_read,
+                                BLOCKSIZE - amount_read);
              bufsize -= BLOCKSIZE - amount_read;
              amount_read = 0;
              set_next_block_after (start);
@@ -554,18 +772,19 @@ finish_sparse_file (int file, long *sizeleft, long fullsize, char *name)
 #endif
          /* Store the data.  */
 
-         count = read (file, start->buffer, BLOCKSIZE);
+         count = safe_read (file, start->buffer, BLOCKSIZE);
          if (count < 0)
            {
-             ERROR ((0, errno, _("\
-Read error at byte %ld, reading %d bytes, in file %s"),
-                        fullsize - *sizeleft, bufsize, name));
+             char buf[UINTMAX_STRSIZE_BOUND];
+             ERROR ((0, errno,
+                     _("Read error at byte %s, reading %lu bytes, in file %s"),
+                     STRINGIFY_BIGINT (fullsize - *sizeleft, buf),
+                     (unsigned long) bufsize, name));
              return 1;
            }
          bufsize -= count;
          *sizeleft -= count;
          set_next_block_after (start);
-         nwritten += BLOCKSIZE;        /* FIXME: ??? */
          start = find_next_block ();
          memset (start->buffer, 0, BLOCKSIZE);
        }
@@ -574,15 +793,18 @@ Read error at byte %ld, reading %d bytes, in file %s"),
        char buffer[BLOCKSIZE];
 
        clear_buffer (buffer);
-       count = read (file, buffer, (size_t) bufsize);
+       count = safe_read (file, buffer, bufsize);
        memcpy (start->buffer, buffer, BLOCKSIZE);
       }
 
       if (count < 0)
        {
+         char buf[UINTMAX_STRSIZE_BOUND];
+         
          ERROR ((0, errno,
-                 _("Read error at byte %ld, reading %d bytes, in file %s"),
-                 fullsize - *sizeleft, bufsize, name));
+                 _("Read error at byte %s, reading %lu bytes, in file %s"),
+                 STRINGIFY_BIGINT (fullsize - *sizeleft, buf),
+                 (unsigned long) bufsize, name));
          return 1;
        }
 #if 0
@@ -593,8 +815,8 @@ Read error at byte %ld, reading %d bytes, in file %s"),
          if (count != bufsize)
            {
              ERROR ((0, 0,
-                     _("File %s shrunk by %d bytes, padding with zeros"),
-                     name, sizeleft));
+                     _("File %s shrunk, padding with zeros"),
+                     name));
              return 1;
            }
          start = find_next_block ();
@@ -602,14 +824,12 @@ Read error at byte %ld, reading %d bytes, in file %s"),
       else
        amount_read += bufsize;
 #endif
-      nwritten += count;       /* FIXME: ??? */
       *sizeleft -= count;
       set_next_block_after (start);
 
     }
   free (sparsearray);
 #if 0
-  printf (_("Amount actually written is (I hope) %d.\n"), nwritten);
   set_next_block_after (start + (count - 1) / BLOCKSIZE);
 #endif
   return 0;
@@ -631,37 +851,39 @@ create_archive (void)
   if (incremental_option)
     {
       char *buffer = xmalloc (PATH_MAX);
-      char *q, *bufp;
+      const char *q;
+      char *bufp;
 
       collect_and_sort_names ();
 
       while (p = name_from_list (), p)
-       dump_file (p, -1, 1);
+       if (!excluded_name (p))
+         dump_file (p, (dev_t) -1, 1);
 
       blank_name_list ();
       while (p = name_from_list (), p)
-       {
-         strcpy (buffer, p);
-         if (p[strlen (p) - 1] != '/')
-           strcat (buffer, "/");
-         bufp = buffer + strlen (buffer);
-         for (q = gnu_list_name->dir_contents;
-              q && *q;
-              q += strlen (q) + 1)
-           {
-             if (*q == 'Y')
-               {
-                 strcpy (bufp, q + 1);
-                 dump_file (buffer, -1, 1);
-               }
-           }
-       }
+       if (!excluded_name (p))
+         {
+           strcpy (buffer, p);
+           if (p[strlen (p) - 1] != '/')
+             strcat (buffer, "/");
+           bufp = buffer + strlen (buffer);
+           q = gnu_list_name->dir_contents;
+           if (q)
+             for (; *q; q += strlen (q) + 1)
+               if (*q == 'Y')
+                 {
+                   strcpy (bufp, q + 1);
+                   dump_file (buffer, (dev_t) -1, 1);
+                 }
+         }
       free (buffer);
     }
   else
     {
       while (p = name_next (1), p)
-       dump_file (p, -1, 1);
+       if (!excluded_name (p))
+         dump_file (p, (dev_t) -1, 1);
     }
 
   write_eot ();
@@ -683,13 +905,14 @@ create_archive (void)
    exit_status to failure, a clear diagnostic has been issued.  */
 
 void
-dump_file (char *p, int parent_device, int top_level)
+dump_file (char *p, dev_t parent_device, int top_level)
 {
   union block *header;
   char type;
   union block *exhdr;
   char save_typeflag;
   struct utimbuf restore_times;
+  off_t restore_size;
 
   /* FIXME: `header' and `upperbound' might be used uninitialized in this
      function.  Reported by Bruno Haible.  */
@@ -701,7 +924,7 @@ dump_file (char *p, int parent_device, int top_level)
      Otherwise, use lstat (which falls back to stat if no symbolic links).  */
 
   if (dereference_option != 0
-#ifdef STX_HIDDEN              /* AIX */
+#if STX_HIDDEN && !_LARGE_FILES /* AIX */
       ? statx (p, &current_stat, STATSIZE, STX_HIDDEN)
       : statx (p, &current_stat, STATSIZE, STX_HIDDEN | STX_LINK)
 #else
@@ -717,6 +940,7 @@ dump_file (char *p, int parent_device, int top_level)
 
   restore_times.actime = current_stat.st_atime;
   restore_times.modtime = current_stat.st_mtime;
+  restore_size = current_stat.st_size;
 
 #ifdef S_ISHIDDEN
   if (S_ISHIDDEN (current_stat.st_mode))
@@ -731,14 +955,15 @@ dump_file (char *p, int parent_device, int top_level)
     }
 #endif
 
-  /* See if we only want new files, and check if this one is too old to
+  /* See if we want only new files, and check if this one is too old to
      put in the archive.  */
 
-  if (!incremental_option && !S_ISDIR (current_stat.st_mode)
+  if ((!incremental_option || listed_incremental_option)
+      && !S_ISDIR (current_stat.st_mode)
       && current_stat.st_mtime < newer_mtime_option
       && (!after_date_option || current_stat.st_ctime < newer_ctime_option))
     {
-      if (parent_device == -1)
+      if (!listed_incremental_option && parent_device == (dev_t) -1)
        WARN ((0, 0, _("%s: is unchanged; not dumped"), p));
       /* FIXME: recheck this return.  */
       return;
@@ -762,19 +987,10 @@ dump_file (char *p, int parent_device, int top_level)
 
   if (current_stat.st_nlink > 1
       && (S_ISREG (current_stat.st_mode)
-#ifdef S_ISCTG
          || S_ISCTG (current_stat.st_mode)
-#endif
-#ifdef S_ISCHR
          || S_ISCHR (current_stat.st_mode)
-#endif
-#ifdef S_ISBLK
          || S_ISBLK (current_stat.st_mode)
-#endif
-#ifdef S_ISFIFO
-         || S_ISFIFO (current_stat.st_mode)
-#endif
-      ))
+         || S_ISFIFO (current_stat.st_mode)))
     {
       struct link *lp;
 
@@ -789,13 +1005,11 @@ dump_file (char *p, int parent_device, int top_level)
 
            while (!absolute_names_option && *link_name == '/')
              {
-               static int warned_once = 0;
-
+               static int warned_once;
                if (!warned_once)
                  {
                    warned_once = 1;
-                   WARN ((0, 0, _("\
-Removing leading `/' from absolute links")));
+                   WARN ((0, 0, _("Removing leading `/' from link names")));
                  }
                link_name++;
              }
@@ -844,14 +1058,12 @@ Removing leading `/' from absolute links")));
   /* This is not a link to a previously dumped file, so dump it.  */
 
   if (S_ISREG (current_stat.st_mode)
-#ifdef S_ISCTG
-      || S_ISCTG (current_stat.st_mode)
-#endif
-      )
+      || S_ISCTG (current_stat.st_mode))
     {
       int f;                   /* file descriptor */
-      long bufsize, count;
-      long sizeleft;
+      size_t bufsize;
+      ssize_t count;
+      off_t sizeleft;
       union block *start;
       int header_moved;
       char isextended = 0;
@@ -873,10 +1085,6 @@ Removing leading `/' from absolute links")));
             files not having more hole blocks than indirect blocks, the
             sparseness will go undetected.  */
 
-         /* tar.h defines ST_NBLOCKS in term of 512 byte sectors, even
-            for HP-UX's which count in 1024 byte units and AIX's which
-            count in 4096 byte units.  So this should work...  */
-
          /* Bruno Haible sent me these statistics for Linux.  It seems
             that some filesystems count indirect blocks in st_blocks,
             while others do not seem to:
@@ -899,9 +1107,11 @@ Removing leading `/' from absolute links")));
             st_blocks, so `du' and `ls -s' give wrong results.  So, the
             --sparse option would not work on a minix filesystem.  */
 
-         if (current_stat.st_size > ST_NBLOCKS (current_stat) * BLOCKSIZE)
+         if (ST_NBLOCKS (current_stat)
+             < (current_stat.st_size / ST_NBLOCKSIZE
+                + (current_stat.st_size % ST_NBLOCKSIZE != 0)))
            {
-             int filesize = current_stat.st_size;
+             off_t filesize = current_stat.st_size;
              int counter;
 
              header = start_header (p, &current_stat);
@@ -930,8 +1140,8 @@ Removing leading `/' from absolute links")));
                 <file>.  It might be kind of disconcerting if the
                 shrunken file size was the one that showed up.  */
 
-             to_oct ((long) current_stat.st_size, 1 + 12,
-                     header->oldgnu_header.realsize);
+             OFF_TO_CHARS (current_stat.st_size,
+                           header->oldgnu_header.realsize);
 
              /* This will be the new "size" of the file, i.e., the size
                 of the file minus the blocks of holes that we're
@@ -939,17 +1149,17 @@ Removing leading `/' from absolute links")));
 
              find_new_file_size (&filesize, upperbound);
              current_stat.st_size = filesize;
-             to_oct ((long) filesize, 1 + 12, header->header.size);
+             OFF_TO_CHARS (filesize, header->header.size);
 
              for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
                {
                  if (!sparsearray[counter].numbytes)
                    break;
 
-                 to_oct (sparsearray[counter].offset, 1 + 12,
-                         header->oldgnu_header.sp[counter].offset);
-                 to_oct (sparsearray[counter].numbytes, 1 + 12,
-                         header->oldgnu_header.sp[counter].numbytes);
+                 OFF_TO_CHARS (sparsearray[counter].offset,
+                               header->oldgnu_header.sp[counter].offset);
+                 SIZE_TO_CHARS (sparsearray[counter].numbytes,
+                                header->oldgnu_header.sp[counter].numbytes);
                }
 
            }
@@ -963,7 +1173,8 @@ Removing leading `/' from absolute links")));
         files when archive is meant for /dev/null.  */
 
       if (dev_null_output
-         || (sizeleft == 0 && 0444 == (0444 & current_stat.st_mode)))
+         || (sizeleft == 0
+             && MODE_R == (MODE_R & current_stat.st_mode)))
        f = -1;
       else
        {
@@ -990,12 +1201,12 @@ Removing leading `/' from absolute links")));
              return;
            }
        }
-#ifdef S_ISCTG
+
       /* Mark contiguous files, if we support them.  */
 
       if (archive_format != V7_FORMAT && S_ISCTG (current_stat.st_mode))
        header->header.typeflag = CONTTYPE;
-#endif
+
       isextended = header->oldgnu_header.isextended;
       save_typeflag = header->header.typeflag;
       finish_header (header);
@@ -1025,12 +1236,10 @@ Removing leading `/' from absolute links")));
              if (counter + index_offset > upperbound)
                break;
 
-             to_oct ((long) sparsearray[counter + index_offset].numbytes,
-                     1 + 12,
-                     exhdr->sparse_header.sp[counter].numbytes);
-             to_oct ((long) sparsearray[counter + index_offset].offset,
-                     1 + 12,
-                     exhdr->sparse_header.sp[counter].offset);
+             SIZE_TO_CHARS (sparsearray[counter + index_offset].numbytes,
+                            exhdr->sparse_header.sp[counter].numbytes);
+             OFF_TO_CHARS (sparsearray[counter + index_offset].offset,
+                           exhdr->sparse_header.sp[counter].offset);
            }
          set_next_block_after (exhdr);
 #if 0
@@ -1048,7 +1257,8 @@ Removing leading `/' from absolute links")));
        }
       if (save_typeflag == GNUTYPE_SPARSE)
        {
-         if (finish_sparse_file (f, &sizeleft, current_stat.st_size, p))
+         if (f < 0
+             || finish_sparse_file (f, &sizeleft, current_stat.st_size, p))
            goto padit;
        }
       else
@@ -1068,7 +1278,7 @@ Removing leading `/' from absolute links")));
              {
                /* Last read -- zero out area beyond.  */
 
-               bufsize = (int) sizeleft;
+               bufsize = sizeleft;
                count = bufsize % BLOCKSIZE;
                if (count)
                  memset (start->buffer + sizeleft, 0,
@@ -1077,12 +1287,15 @@ Removing leading `/' from absolute links")));
            if (f < 0)
              count = bufsize;
            else
-             count = read (f, start->buffer, (size_t) bufsize);
+             count = safe_read (f, start->buffer, bufsize);
            if (count < 0)
              {
-               ERROR ((0, errno, _("\
-Read error at byte %ld, reading %d bytes, in file %s"),
-                       (long) (current_stat.st_size - sizeleft), bufsize, p));
+               char buf[UINTMAX_STRSIZE_BOUND];
+               ERROR ((0, errno,
+                       _("Read error at byte %s, reading %lu bytes, in file %s"),
+                       STRINGIFY_BIGINT (current_stat.st_size - sizeleft,
+                                         buf),
+                       (unsigned long) bufsize, p));
                goto padit;
              }
            sizeleft -= count;
@@ -1093,9 +1306,14 @@ Read error at byte %ld, reading %d bytes, in file %s"),
 
            if (count == bufsize)
              continue;
-           ERROR ((0, 0, _("File %s shrunk by %d bytes, padding with zeros"),
-                   p, sizeleft));
-           goto padit;         /* short read */
+           else
+             {
+               char buf[UINTMAX_STRSIZE_BOUND];
+               ERROR ((0, 0,
+                       _("File %s shrunk by %s bytes, padding with zeros"),
+                       p, STRINGIFY_BIGINT (sizeleft, buf)));
+               goto padit;             /* short read */
+             }
          }
 
       if (multi_volume_option)
@@ -1103,7 +1321,14 @@ Read error at byte %ld, reading %d bytes, in file %s"),
 
       if (f >= 0)
        {
-         close (f);
+         struct stat final_stat;
+         if (fstat (f, &final_stat) != 0)
+           ERROR ((0, errno, "%s: fstat", p));
+         else if (final_stat.st_mtime != restore_times.modtime
+                  || final_stat.st_size != restore_size)
+           ERROR ((0, errno, _("%s: file changed as we read it"), p));
+         if (close (f) != 0)
+           ERROR ((0, errno, _("%s: close"), p));
          if (atime_preserve_option)
            utime (p, &restore_times);
        }
@@ -1137,7 +1362,7 @@ Read error at byte %ld, reading %d bytes, in file %s"),
       return;
     }
 
-#ifdef S_ISLNK
+#ifdef HAVE_READLINK
   else if (S_ISLNK (current_stat.st_mode))
     {
       int size;
@@ -1174,16 +1399,16 @@ Read error at byte %ld, reading %d bytes, in file %s"),
        }
       return;
     }
-#endif /* S_ISLNK */
+#endif
 
   else if (S_ISDIR (current_stat.st_mode))
     {
       DIR *directory;
       struct dirent *entry;
       char *namebuf;
-      int buflen;
-      int len;
-      int our_device = current_stat.st_dev;
+      size_t buflen;
+      size_t len;
+      dev_t our_device = current_stat.st_dev;
 
       /* If this tar program is installed suid root, like for Amanda, the
         access might look like denied, while it is not really.
@@ -1204,8 +1429,8 @@ Read error at byte %ld, reading %d bytes, in file %s"),
 
       len = strlen (p);
       buflen = len + NAME_FIELD_SIZE;
-      namebuf = xmalloc ((size_t) (buflen + 1));
-      strncpy (namebuf, p, (size_t) buflen);
+      namebuf = xmalloc (buflen + 1);
+      strncpy (namebuf, p, buflen);
       while (len >= 1 && namebuf[len - 1] == '/')
        len--;
       namebuf[len++] = '/';
@@ -1254,25 +1479,25 @@ Read error at byte %ld, reading %d bytes, in file %s"),
 
       if (incremental_option && gnu_list_name->dir_contents)
        {
-         int sizeleft;
-         int totsize;
-         int bufsize;
+         off_t sizeleft;
+         off_t totsize;
+         size_t bufsize;
          union block *start;
-         int count;
-         char *buffer, *p_buffer;
+         ssize_t count;
+         const char *buffer, *p_buffer;
 
          buffer = gnu_list_name->dir_contents; /* FOO */
          totsize = 0;
          for (p_buffer = buffer; p_buffer && *p_buffer;)
            {
-             int tmp;
+             size_t tmp;
 
              tmp = strlen (p_buffer) + 1;
              totsize += tmp;
              p_buffer += tmp;
            }
          totsize++;
-         to_oct ((long) totsize, 1 + 12, header->header.size);
+         OFF_TO_CHARS (totsize, header->header.size);
          finish_header (header);
          p_buffer = buffer;
          sizeleft = totsize;
@@ -1294,7 +1519,7 @@ Read error at byte %ld, reading %d bytes, in file %s"),
                    memset (start->buffer + sizeleft, 0,
                           (size_t) (BLOCKSIZE - count));
                }
-             memcpy (start->buffer, p_buffer, (size_t) bufsize);
+             memcpy (start->buffer, p_buffer, bufsize);
              sizeleft -= bufsize;
              p_buffer += bufsize;
              set_next_block_after (start + (bufsize - 1) / BLOCKSIZE);
@@ -1334,16 +1559,11 @@ Read error at byte %ld, reading %d bytes, in file %s"),
          return;
        }
 
-      /* Hack to remove "./" from the front of all the file names.  */
-
-      if (len == 2 && namebuf[0] == '.' && namebuf[1] == '/')
-       len = 0;
-
       /* FIXME: Should speed this up by cd-ing into the dir.  */
 
       while (entry = readdir (directory), entry)
        {
-         /* Skip `.' and `..'.  */
+         /* Skip `.', `..', and excluded file names.  */
 
          if (is_dot_or_dotdot (entry->d_name))
            continue;
@@ -1351,7 +1571,7 @@ Read error at byte %ld, reading %d bytes, in file %s"),
          if ((int) NAMLEN (entry) + len >= buflen)
            {
              buflen = len + NAMLEN (entry);
-             namebuf = (char *) xrealloc (namebuf, (size_t) (buflen + 1));
+             namebuf = (char *) xrealloc (namebuf, buflen + 1);
 #if 0
              namebuf[len] = '\0';
              ERROR ((0, 0, _("File name %s%s too long"),
@@ -1360,9 +1580,8 @@ Read error at byte %ld, reading %d bytes, in file %s"),
 #endif
            }
          strcpy (namebuf + len, entry->d_name);
-         if (exclude_option && check_exclude (namebuf))
-           continue;
-         dump_file (namebuf, our_device, 0);
+         if (!excluded_name (namebuf))
+           dump_file (namebuf, our_device, 0);
        }
 
       closedir (directory);
@@ -1372,28 +1591,13 @@ Read error at byte %ld, reading %d bytes, in file %s"),
       return;
     }
 
-#ifdef S_ISCHR
   else if (S_ISCHR (current_stat.st_mode))
     type = CHRTYPE;
-#endif
-
-#ifdef S_ISBLK
   else if (S_ISBLK (current_stat.st_mode))
     type = BLKTYPE;
-#endif
-
-  /* Avoid screwy apollo lossage where S_IFIFO == S_IFSOCK.  */
-
-#if (_ISP__M68K == 0) && (_ISP__A88K == 0) && defined(S_ISFIFO)
-  else if (S_ISFIFO (current_stat.st_mode))
-    type = FIFOTYPE;
-#endif
-
-#ifdef S_ISSOCK
-  else if (S_ISSOCK (current_stat.st_mode))
+  else if (S_ISFIFO (current_stat.st_mode)
+          || S_ISSOCK (current_stat.st_mode))
     type = FIFOTYPE;
-#endif
-
   else
     goto unknown;
 
@@ -1410,15 +1614,11 @@ Read error at byte %ld, reading %d bytes, in file %s"),
 
   header->header.typeflag = type;
 
-#if defined(S_IFBLK) || defined(S_IFCHR)
   if (type != FIFOTYPE)
     {
-      to_oct ((long) major (current_stat.st_rdev), 8,
-             header->header.devmajor);
-      to_oct ((long) minor (current_stat.st_rdev), 8,
-             header->header.devminor);
+      MAJOR_TO_CHARS (major (current_stat.st_rdev), header->header.devmajor);
+      MINOR_TO_CHARS (minor (current_stat.st_rdev), header->header.devminor);
     }
-#endif
 
   finish_header (header);
   if (remove_files_option)
This page took 0.07854 seconds and 4 git commands to generate.