]> Dogcows Code - chaz/tar/blobdiff - src/create.c
Adjust to recent gnulib changes.
[chaz/tar] / src / create.c
index ff69876192dcdf8a084d4b017acb5cd64266f6da..eb528be25626e9db921a1012ed5608821888a51a 100644 (file)
@@ -1,7 +1,7 @@
 /* Create a tar archive.
 
    Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003, 2004, 2005 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
    Written by John Gilmore, on 1985-08-25.
 
@@ -22,7 +22,6 @@
 #include <system.h>
 
 #include <quotearg.h>
-#include <utimens.h>
 
 #include "common.h"
 #include <hash.h>
@@ -109,6 +108,68 @@ to_base256 (int negative, uintmax_t value, char *where, size_t size)
   while (i);
 }
 
+
+static bool
+to_chars (int negative, uintmax_t value, size_t valsize,
+         uintmax_t (*substitute) (int *),
+         char *where, size_t size, const char *type);
+
+static bool
+to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize,
+               uintmax_t (*substitute) (int *),
+               char *where, size_t size, const char *type)
+{
+  uintmax_t maxval = (gnu_format
+                     ? MAX_VAL_WITH_DIGITS (size - 1, LG_256)
+                     : MAX_VAL_WITH_DIGITS (size - 1, LG_8));
+  char valbuf[UINTMAX_STRSIZE_BOUND + 1];
+  char maxbuf[UINTMAX_STRSIZE_BOUND];
+  char minbuf[UINTMAX_STRSIZE_BOUND + 1];
+  char const *minval_string;
+  char const *maxval_string = STRINGIFY_BIGINT (maxval, maxbuf);
+  char const *value_string;
+
+  if (gnu_format)
+    {
+      uintmax_t m = maxval + 1 ? maxval + 1 : maxval / 2 + 1;
+      char *p = STRINGIFY_BIGINT (m, minbuf + 1);
+      *--p = '-';
+      minval_string = p;
+    }
+  else
+    minval_string = "0";
+
+  if (negative)
+    {
+      char *p = STRINGIFY_BIGINT (- value, valbuf + 1);
+      *--p = '-';
+      value_string = p;
+    }
+  else
+    value_string = STRINGIFY_BIGINT (value, valbuf);
+
+  if (substitute)
+    {
+      int negsub;
+      uintmax_t sub = substitute (&negsub) & maxval;
+      /* FIXME: This is the only place where GNU_FORMAT differs from
+        OLDGNU_FORMAT. Apart from this they are completely identical. */
+      uintmax_t s = (negsub &= archive_format == GNU_FORMAT) ? - sub : sub;
+      char subbuf[UINTMAX_STRSIZE_BOUND + 1];
+      char *sub_string = STRINGIFY_BIGINT (s, subbuf + 1);
+      if (negsub)
+       *--sub_string = '-';
+      WARN ((0, 0, _("value %s out of %s range %s..%s; substituting %s"),
+            value_string, type, minval_string, maxval_string,
+            sub_string));
+      return to_chars (negsub, s, valsize, 0, where, size, type);
+    }
+  else
+    ERROR ((0, 0, _("value %s out of %s range %s..%s"),
+           value_string, type, minval_string, maxval_string));
+  return false;
+}
+
 /* Convert NEGATIVE VALUE (which was originally of size VALSIZE) to
    external form, using SUBSTITUTE (...) if VALUE won't fit.  Output
    to buffer WHERE with size SIZE.  NEGATIVE is 1 iff VALUE was
@@ -123,103 +184,61 @@ to_base256 (int negative, uintmax_t value, char *where, size_t size)
    SUBSTITUTE the address of an 0-or-1 flag recording whether the
    substitute value is negative.  */
 
-static void
+static bool
 to_chars (int negative, uintmax_t value, size_t valsize,
          uintmax_t (*substitute) (int *),
          char *where, size_t size, const char *type)
 {
-  int base256_allowed = (archive_format == GNU_FORMAT
-                        || archive_format == OLDGNU_FORMAT);
+  int gnu_format = (archive_format == GNU_FORMAT
+                   || archive_format == OLDGNU_FORMAT);
 
   /* Generate the POSIX octal representation if the number fits.  */
   if (! negative && value <= MAX_VAL_WITH_DIGITS (size - 1, LG_8))
     {
       where[size - 1] = '\0';
       to_octal (value, where, size - 1);
+      return true;
     }
-
-  /* Otherwise, generate the base-256 representation if we are
-     generating an old or new GNU format and if the number fits.  */
-  else if (((negative ? -1 - value : value)
-           <= MAX_VAL_WITH_DIGITS (size - 1, LG_256))
-          && base256_allowed)
+  else if (gnu_format)
     {
-      where[0] = negative ? -1 : 1 << (LG_256 - 1);
-      to_base256 (negative, value, where + 1, size - 1);
-    }
+      /* Try to cope with the number by using traditional GNU format
+        methods */
 
-  /* 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)
-    {
-      static int warned_once;
-      if (! warned_once)
-       {
-         warned_once = 1;
-         WARN ((0, 0, _("Generating negative octal headers")));
-       }
-      where[size - 1] = '\0';
-      to_octal (value & MAX_VAL_WITH_DIGITS (valsize * CHAR_BIT, 1),
-               where, size - 1);
-    }
-
-  /* Otherwise, output a substitute value if possible (with a
-     warning), and an error message if not.  */
-  else
-    {
-      uintmax_t maxval = (base256_allowed
-                         ? MAX_VAL_WITH_DIGITS (size - 1, LG_256)
-                         : MAX_VAL_WITH_DIGITS (size - 1, LG_8));
-      char valbuf[UINTMAX_STRSIZE_BOUND + 1];
-      char maxbuf[UINTMAX_STRSIZE_BOUND];
-      char minbuf[UINTMAX_STRSIZE_BOUND + 1];
-      char const *minval_string;
-      char const *maxval_string = STRINGIFY_BIGINT (maxval, maxbuf);
-      char const *value_string;
-
-      if (base256_allowed)
+      /* Generate the base-256 representation if the number fits.  */
+      if (((negative ? -1 - value : value)
+          <= MAX_VAL_WITH_DIGITS (size - 1, LG_256)))
        {
-         uintmax_t m = maxval + 1 ? maxval + 1 : maxval / 2 + 1;
-         char *p = STRINGIFY_BIGINT (m, minbuf + 1);
-         *--p = '-';
-         minval_string = p;
-       }
-      else
-       minval_string = "0";
-
-      if (negative)
-       {
-         char *p = STRINGIFY_BIGINT (- value, valbuf + 1);
-         *--p = '-';
-         value_string = p;
+         where[0] = negative ? -1 : 1 << (LG_256 - 1);
+         to_base256 (negative, value, where + 1, size - 1);
+         return true;
        }
-      else
-       value_string = STRINGIFY_BIGINT (value, valbuf);
 
-      if (substitute)
+      /* 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)
        {
-         int negsub;
-         uintmax_t sub = substitute (&negsub) & maxval;
-         /* FIXME: This is the only place where GNU_FORMAT differs from
-             OLDGNU_FORMAT. Apart from this they are completely identical. */
-         uintmax_t s = (negsub &= archive_format == GNU_FORMAT) ? - sub : sub;
-         char subbuf[UINTMAX_STRSIZE_BOUND + 1];
-         char *sub_string = STRINGIFY_BIGINT (s, subbuf + 1);
-         if (negsub)
-           *--sub_string = '-';
-         WARN ((0, 0, _("value %s out of %s range %s..%s; substituting %s"),
-                value_string, type, minval_string, maxval_string,
-                sub_string));
-         to_chars (negsub, s, valsize, 0, where, size, type);
+         static int warned_once;
+         if (! warned_once)
+           {
+             warned_once = 1;
+             WARN ((0, 0, _("Generating negative octal headers")));
+           }
+         where[size - 1] = '\0';
+         to_octal (value & MAX_VAL_WITH_DIGITS (valsize * CHAR_BIT, 1),
+                   where, size - 1);
+         return true;
        }
-      else
-       ERROR ((0, 0, _("value %s out of %s range %s..%s"),
-               value_string, type, minval_string, maxval_string));
+      /* Otherwise fall back to substitution, if possible: */
     }
+  else
+    substitute = NULL; /* No substitution for formats, other than GNU */
+
+  return to_chars_subst (negative, gnu_format, value, valsize, substitute,
+                        where, size, type);
 }
 
 static uintmax_t
@@ -238,25 +257,25 @@ gid_substitute (int *negative)
   return r;
 }
 
-void
+bool
 gid_to_chars (gid_t v, char *p, size_t s)
 {
-  to_chars (v < 0, (uintmax_t) v, sizeof v, gid_substitute, p, s, "gid_t");
+  return to_chars (v < 0, (uintmax_t) v, sizeof v, gid_substitute, p, s, "gid_t");
 }
 
-void
+bool
 major_to_chars (major_t v, char *p, size_t s)
 {
-  to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "major_t");
+  return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "major_t");
 }
 
-void
+bool
 minor_to_chars (minor_t v, char *p, size_t s)
 {
-  to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "minor_t");
+  return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "minor_t");
 }
 
-void
+bool
 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,
@@ -293,25 +312,25 @@ mode_to_chars (mode_t v, char *p, size_t s)
           | (v & S_IWOTH ? TOWRITE : 0)
           | (v & S_IXOTH ? TOEXEC : 0));
     }
-  to_chars (negative, u, sizeof v, 0, p, s, "mode_t");
+  return to_chars (negative, u, sizeof v, 0, p, s, "mode_t");
 }
 
-void
+bool
 off_to_chars (off_t v, char *p, size_t s)
 {
-  to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "off_t");
+  return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "off_t");
 }
 
-void
+bool
 size_to_chars (size_t v, char *p, size_t s)
 {
-  to_chars (0, (uintmax_t) v, sizeof v, 0, p, s, "size_t");
+  return to_chars (0, (uintmax_t) v, sizeof v, 0, p, s, "size_t");
 }
 
-void
+bool
 time_to_chars (time_t v, char *p, size_t s)
 {
-  to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "time_t");
+  return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "time_t");
 }
 
 static uintmax_t
@@ -330,16 +349,16 @@ uid_substitute (int *negative)
   return r;
 }
 
-void
+bool
 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");
+  return to_chars (v < 0, (uintmax_t) v, sizeof v, uid_substitute, p, s, "uid_t");
 }
 
-void
+bool
 uintmax_to_chars (uintmax_t v, char *p, size_t s)
 {
-  to_chars (0, v, sizeof v, 0, p, s, "uintmax_t");
+  return to_chars (0, v, sizeof v, 0, p, s, "uintmax_t");
 }
 
 void
@@ -361,7 +380,7 @@ bool
 file_dumpable_p (struct tar_stat_info *st)
 {
   if (dev_null_output)
-    return totals_option && sparse_option && sparse_file_p (st);
+    return totals_option && sparse_option && ST_IS_SPARSE (st->stat);
   return !(st->archive_file_size == 0
           && (st->stat.st_mode & MODE_R) == MODE_R);
 }
@@ -452,7 +471,7 @@ write_gnu_long_link (struct tar_stat_info *st, const char *p, char type)
   finish_header (st, header, -1);
 
   header = find_next_block ();
-  
+
   bufsize = available_space_after (header);
 
   while (bufsize < size)
@@ -475,7 +494,7 @@ split_long_name (const char *name, size_t length)
   size_t i;
 
   if (length > PREFIX_FIELD_SIZE)
-    length = PREFIX_FIELD_SIZE+2;
+    length = PREFIX_FIELD_SIZE + 1;
   for (i = length - 1; i > 0; i--)
     if (ISSLASH (name[i]))
       break;
@@ -582,7 +601,7 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header)
   union block *header, hp;
   char *p;
   int type;
-  
+
   if (extended_header.buffer || extended_header.stk == NULL)
     return old_header;
 
@@ -644,7 +663,8 @@ start_header (struct tar_stat_info *st)
   if (mode_option)
     st->stat.st_mode =
       ((st->stat.st_mode & ~MODE_ALL)
-       | mode_adjust (st->stat.st_mode, mode_option, initial_umask));
+       | mode_adjust (st->stat.st_mode, S_ISDIR (st->stat.st_mode) != 0,
+                     initial_umask, mode_option, NULL));
 
   /* Paul Eggert tried the trivial test ($WRITER cf a b; $READER tvf a)
      for a few tars and came up with the following interoperability
@@ -682,7 +702,8 @@ start_header (struct tar_stat_info *st)
        xheader_store ("uid", st, NULL);
        uid = 0;
       }
-    UID_TO_CHARS (uid, header->header.uid);
+    if (!UID_TO_CHARS (uid, header->header.uid))
+      return NULL;
   }
 
   {
@@ -693,7 +714,8 @@ start_header (struct tar_stat_info *st)
        xheader_store ("gid", st, NULL);
        gid = 0;
       }
-    GID_TO_CHARS (gid, header->header.gid);
+    if (!GID_TO_CHARS (gid, header->header.gid))
+      return NULL;
   }
 
   {
@@ -704,11 +726,12 @@ start_header (struct tar_stat_info *st)
        xheader_store ("size", st, NULL);
        size = 0;
       }
-    OFF_TO_CHARS (size, header->header.size);
+    if (!OFF_TO_CHARS (size, header->header.size))
+      return NULL;
   }
 
   {
-    struct timespec mtime = st->mtime;
+    struct timespec mtime = set_mtime_option ? mtime_option : st->mtime;
     if (archive_format == POSIX_FORMAT)
       {
        if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec
@@ -717,7 +740,8 @@ start_header (struct tar_stat_info *st)
        if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec)
          mtime.tv_sec = 0;
       }
-    TIME_TO_CHARS (mtime.tv_sec, header->header.mtime);
+    if (!TIME_TO_CHARS (mtime.tv_sec, header->header.mtime))
+      return NULL;
   }
 
   /* FIXME */
@@ -733,7 +757,8 @@ start_header (struct tar_stat_info *st)
          xheader_store ("devmajor", st, NULL);
          devmajor = 0;
        }
-      MAJOR_TO_CHARS (devmajor, header->header.devmajor);
+      if (!MAJOR_TO_CHARS (devmajor, header->header.devmajor))
+       return NULL;
 
       if (archive_format == POSIX_FORMAT
          && MAX_OCTAL_VAL (header->header.devminor) < devminor)
@@ -741,12 +766,14 @@ start_header (struct tar_stat_info *st)
          xheader_store ("devminor", st, NULL);
          devminor = 0;
        }
-      MINOR_TO_CHARS (devminor, header->header.devminor);
+      if (!MINOR_TO_CHARS (devminor, header->header.devminor))
+       return NULL;
     }
   else if (archive_format != GNU_FORMAT && archive_format != OLDGNU_FORMAT)
     {
-      MAJOR_TO_CHARS (0, header->header.devmajor);
-      MINOR_TO_CHARS (0, header->header.devminor);
+      if (!(MAJOR_TO_CHARS (0, header->header.devmajor)
+           && MINOR_TO_CHARS (0, header->header.devminor)))
+       return NULL;
     }
 
   if (archive_format == POSIX_FORMAT)
@@ -950,35 +977,6 @@ dump_regular_file (int fd, struct tar_stat_info *st)
   return dump_status_ok;
 }
 
-static void
-dump_regular_finish (int fd, struct tar_stat_info *st,
-                    struct timespec original_ctime)
-{
-  if (fd >= 0)
-    {
-      struct stat final_stat;
-      if (fstat (fd, &final_stat) != 0)
-       {
-         stat_diag (st->orig_file_name);
-       }
-      else if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime)
-              != 0)
-       {
-         WARN ((0, 0, _("%s: file changed as we read it"),
-                quotearg_colon (st->orig_file_name)));
-       }
-      if (close (fd) != 0)
-       {
-         close_diag (st->orig_file_name);
-       }
-    }
-  if (remove_files_option)
-    {
-      if (unlink (st->orig_file_name) == -1)
-       unlink_error (st->orig_file_name);
-    }
-}
-
 /* Look in directory DIRNAME for a cache directory tag file
    with the magic name "CACHEDIR.TAG" and a standard header,
    as described at:
@@ -1059,9 +1057,9 @@ dump_dir0 (char *directory,
              size_t bufsize;
              ssize_t count;
              const char *buffer, *p_buffer;
-             
+
              block_ordinal = current_block_ordinal ();
-             buffer = gnu_list_name->dir_contents; 
+             buffer = gnu_list_name->dir_contents;
              if (buffer)
                totsize = dumpdir_size (buffer);
              else
@@ -1070,7 +1068,7 @@ dump_dir0 (char *directory,
              finish_header (st, blk, block_ordinal);
              p_buffer = buffer;
              size_left = totsize;
-             
+
              mv_begin (st);
              mv_total_size (totsize);
              while (size_left > 0)
@@ -1161,11 +1159,9 @@ ensure_slash (char **pstr)
 }
 
 static bool
-dump_dir (struct tar_stat_info *st, int top_level, dev_t parent_device)
+dump_dir (int fd, struct tar_stat_info *st, int top_level, dev_t parent_device)
 {
-  char *directory;
-
-  directory = savedir (st->orig_file_name);
+  char *directory = fdsavedir (fd);
   if (!directory)
     {
       savedir_diag (st->orig_file_name);
@@ -1187,7 +1183,7 @@ dump_dir (struct tar_stat_info *st, int top_level, dev_t parent_device)
 void
 create_archive (void)
 {
-  char *p;
+  const char *p;
 
   open_archive (ACCESS_WRITE);
   xheader_write_global ();
@@ -1273,7 +1269,7 @@ compare_links (void const *entry1, void const *entry2)
 }
 
 static void
-unknown_file_error (char *p)
+unknown_file_error (char const *p)
 {
   WARN ((0, 0, _("%s: Unknown file type; file ignored"),
         quotearg_colon (p)));
@@ -1289,8 +1285,8 @@ unknown_file_error (char *p)
    again if we've done it once already.  */
 static Hash_table *link_table;
 
-/* Try to dump stat as a hard link to another file in the archive. If
-   succeeded returns true */
+/* Try to dump stat as a hard link to another file in the archive.
+   Return true if successful.  */
 static bool
 dump_hard_link (struct tar_stat_info *st)
 {
@@ -1321,7 +1317,7 @@ dump_hard_link (struct tar_stat_info *st)
          st->stat.st_size = 0;
          blk = start_header (st);
          if (!blk)
-           return true;
+           return false;
          tar_copy_str (blk->header.linkname, link_name, NAME_FIELD_SIZE);
 
          blk->header.typeflag = LNKTYPE;
@@ -1392,7 +1388,7 @@ check_links (void)
    exit_status to failure, a clear diagnostic has been issued.  */
 
 static void
-dump_file0 (struct tar_stat_info *st, char *p,
+dump_file0 (struct tar_stat_info *st, const char *p,
            int top_level, dev_t parent_device)
 {
   union block *header;
@@ -1400,6 +1396,7 @@ dump_file0 (struct tar_stat_info *st, char *p,
   struct timespec original_ctime;
   struct timespec restore_times[2];
   off_t block_ordinal = -1;
+  bool is_dir;
 
   if (interactive_option && !confirm ("add", p))
     return;
@@ -1408,6 +1405,8 @@ dump_file0 (struct tar_stat_info *st, char *p,
   assign_string (&st->file_name,
                  safer_name_suffix (p, false, absolute_names_option));
 
+  transform_name (&st->file_name);
+
   if (deref_stat (dereference_option, p, &st->stat) != 0)
     {
       stat_diag (p);
@@ -1458,45 +1457,50 @@ dump_file0 (struct tar_stat_info *st, char *p,
 
   if (is_avoided_name (p))
     return;
-  if (S_ISDIR (st->stat.st_mode))
-    {
-      dump_dir (st, top_level, parent_device);
-      if (atime_preserve_option)
-       utimens (p, restore_times);
-      return;
-    }
-  else
-    {
-      /* Check for multiple links.  */
-      if (dump_hard_link (st))
-       return;
 
-      /* This is not a link to a previously dumped file, so dump it.  */
+  is_dir = S_ISDIR (st->stat.st_mode) != 0;
 
-      if (S_ISREG (st->stat.st_mode)
-         || S_ISCTG (st->stat.st_mode))
-       {
-         int fd;
-         enum dump_status status;
+  if (!is_dir && dump_hard_link (st))
+    return;
+
+  if (is_dir || S_ISREG (st->stat.st_mode) || S_ISCTG (st->stat.st_mode))
+    {
+      bool ok;
+      int fd = -1;
+      struct stat final_stat;
 
-         if (file_dumpable_p (st))
+      if (is_dir || file_dumpable_p (st))
+       {
+         fd = open (p,
+                    (O_RDONLY | O_BINARY
+                     | (is_dir ? O_DIRECTORY | O_NONBLOCK : 0)
+                     | (atime_preserve_option == system_atime_preserve
+                        ? O_NOATIME
+                        : 0)));
+         if (fd < 0)
            {
-             fd = open (st->orig_file_name,
-                        O_RDONLY | O_BINARY);
-             if (fd < 0)
-               {
-                 if (!top_level && errno == ENOENT)
-                   WARN ((0, 0, _("%s: File removed before we read it"),
-                          quotearg_colon (st->orig_file_name)));
-                 else
-                   open_diag (st->orig_file_name);
-                 return;
-               }
+             if (!top_level && errno == ENOENT)
+               WARN ((0, 0, _("%s: File removed before we read it"),
+                      quotearg_colon (p)));
+             else
+               open_diag (p);
+             return;
            }
-         else
+       }
+
+      if (is_dir)
+       {
+         ok = dump_dir (fd, st, top_level, parent_device);
+
+         /* dump_dir consumes FD if successful.  */
+         if (ok)
            fd = -1;
+       }
+      else
+       {
+         enum dump_status status;
 
-         if (fd != -1 && sparse_option && sparse_file_p (st))
+         if (fd != -1 && sparse_option && ST_IS_SPARSE (st->stat))
            {
              status = sparse_dump_file (fd, st);
              if (status == dump_status_not_implemented)
@@ -1508,88 +1512,132 @@ dump_file0 (struct tar_stat_info *st, char *p,
          switch (status)
            {
            case dump_status_ok:
-             mv_end ();
-             dump_regular_finish (fd, st, original_ctime);
-             break;
-
            case dump_status_short:
              mv_end ();
-             close (fd);
              break;
 
            case dump_status_fail:
-             close (fd);
-             return;
+             break;
 
            case dump_status_not_implemented:
              abort ();
            }
 
-         if (atime_preserve_option)
-           utimens (st->orig_file_name, restore_times);
          file_count_links (st);
-         return;
+
+         ok = status == dump_status_ok;
        }
-#ifdef HAVE_READLINK
-      else if (S_ISLNK (st->stat.st_mode))
+
+      if (ok)
        {
-         char *buffer;
-         int size;
-         size_t linklen = st->stat.st_size;
-         if (linklen != st->stat.st_size || linklen + 1 == 0)
-           xalloc_die ();
-         buffer = (char *) alloca (linklen + 1);
-         size = readlink (p, buffer, linklen + 1);
-         if (size < 0)
+         /* If possible, reopen a directory if we are preserving
+            atimes, so that we can set just the atime on systems with
+            _FIOSATIME.  */
+         if (fd < 0 && is_dir
+             && atime_preserve_option == replace_atime_preserve)
+           fd = open (p, O_RDONLY | O_BINARY | O_DIRECTORY | O_NONBLOCK);
+
+         if ((fd < 0
+              ? deref_stat (dereference_option, p, &final_stat)
+              : fstat (fd, &final_stat))
+             != 0)
            {
-             readlink_diag (p);
-             return;
+             stat_diag (p);
+             ok = false;
            }
-         buffer[size] = '\0';
-         assign_string (&st->link_name, buffer);
-         if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
-           write_long_link (st);
+       }
 
-         block_ordinal = current_block_ordinal ();
-         st->stat.st_size = 0; /* force 0 size on symlink */
-         header = start_header (st);
-         if (!header)
-           return;
-         tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE);
-         header->header.typeflag = SYMTYPE;
-         finish_header (st, header, block_ordinal);
-         /* nothing more to do to it */
-
-         if (remove_files_option)
+      if (ok)
+       {
+         if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0)
+           WARN ((0, 0, _("%s: file changed as we read it"),
+                  quotearg_colon (p)));
+         else if (atime_preserve_option == replace_atime_preserve
+                  && set_file_atime (fd, p, restore_times) != 0)
+           utime_error (p);
+       }
+
+      if (0 <= fd && close (fd) != 0)
+       {
+         close_diag (p);
+         ok = false;
+       }
+
+      if (ok && remove_files_option)
+       {
+         if (is_dir)
            {
-             if (unlink (p) == -1)
+             if (rmdir (p) != 0 && errno != ENOTEMPTY)
+               rmdir_error (p);
+           }
+         else
+           {
+             if (unlink (p) != 0)
                unlink_error (p);
            }
-         file_count_links (st);
-         return;
-       }
-#endif
-      else if (S_ISCHR (st->stat.st_mode))
-       type = CHRTYPE;
-      else if (S_ISBLK (st->stat.st_mode))
-       type = BLKTYPE;
-      else if (S_ISFIFO (st->stat.st_mode))
-       type = FIFOTYPE;
-      else if (S_ISSOCK (st->stat.st_mode))
-       {
-         WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p)));
-         return;
        }
-      else if (S_ISDOOR (st->stat.st_mode))
+
+      return;
+    }
+#ifdef HAVE_READLINK
+  else if (S_ISLNK (st->stat.st_mode))
+    {
+      char *buffer;
+      int size;
+      size_t linklen = st->stat.st_size;
+      if (linklen != st->stat.st_size || linklen + 1 == 0)
+       xalloc_die ();
+      buffer = (char *) alloca (linklen + 1);
+      size = readlink (p, buffer, linklen + 1);
+      if (size < 0)
        {
-         WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p)));
+         readlink_diag (p);
          return;
        }
-      else
+      buffer[size] = '\0';
+      assign_string (&st->link_name, buffer);
+      if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
+       write_long_link (st);
+
+      block_ordinal = current_block_ordinal ();
+      st->stat.st_size = 0;    /* force 0 size on symlink */
+      header = start_header (st);
+      if (!header)
+       return;
+      tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE);
+      header->header.typeflag = SYMTYPE;
+      finish_header (st, header, block_ordinal);
+      /* nothing more to do to it */
+
+      if (remove_files_option)
        {
-         unknown_file_error (p);
-         return;
+         if (unlink (p) == -1)
+           unlink_error (p);
        }
+      file_count_links (st);
+      return;
+    }
+#endif
+  else if (S_ISCHR (st->stat.st_mode))
+    type = CHRTYPE;
+  else if (S_ISBLK (st->stat.st_mode))
+    type = BLKTYPE;
+  else if (S_ISFIFO (st->stat.st_mode))
+    type = FIFOTYPE;
+  else if (S_ISSOCK (st->stat.st_mode))
+    {
+      WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p)));
+      return;
+    }
+  else if (S_ISDOOR (st->stat.st_mode))
+    {
+      WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p)));
+      return;
+    }
+  else
+    {
+      unknown_file_error (p);
+      return;
     }
 
   if (archive_format == V7_FORMAT)
@@ -1622,7 +1670,7 @@ dump_file0 (struct tar_stat_info *st, char *p,
 }
 
 void
-dump_file (char *p, int top_level, dev_t parent_device)
+dump_file (const char *p, int top_level, dev_t parent_device)
 {
   struct tar_stat_info st;
   tar_stat_init (&st);
This page took 0.045063 seconds and 4 git commands to generate.