]> Dogcows Code - chaz/tar/blobdiff - src/create.c
Improve listed incremental dumps.
[chaz/tar] / src / create.c
index 0d208d791c24586f50bc19c00fca7fe33d9271d5..072732a6195250bd1a9ff436971de709a590bf6f 100644 (file)
@@ -1,13 +1,13 @@
 /* Create a tar archive.
 
    Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
 /* Create a tar archive.
 
    Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006, 2007, 2009 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
    under the terms of the GNU General Public License as published by the
 
    Written by John Gilmore, on 1985-08-25.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any later
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
    version.
 
    This program is distributed in the hope that it will be useful, but
@@ -33,6 +33,112 @@ struct link
     size_t nlink;
     char name[1];
   };
     size_t nlink;
     char name[1];
   };
+
+struct exclusion_tag
+{
+  const char *name;
+  size_t length;
+  enum exclusion_tag_type type;
+  bool (*predicate) (const char *name);
+  struct exclusion_tag *next;
+};
+
+static struct exclusion_tag *exclusion_tags;
+
+void
+add_exclusion_tag (const char *name, enum exclusion_tag_type type,
+                  bool (*predicate) (const char *name))
+{
+  struct exclusion_tag *tag = xmalloc (sizeof tag[0]);
+  tag->next = exclusion_tags;
+  tag->name = name;
+  tag->type = type;
+  tag->predicate = predicate;
+  tag->length = strlen (name);
+  exclusion_tags = tag;
+}
+
+void
+exclusion_tag_warning (const char *dirname, const char *tagname,
+                      const char *message)
+{
+  if (verbose_option)
+    WARNOPT (WARN_CACHEDIR,
+            (0, 0,
+             _("%s: contains a cache directory tag %s; %s"),
+             quotearg_colon (dirname),
+             quotearg_n (1, tagname),
+             message));
+}
+
+enum exclusion_tag_type 
+check_exclusion_tags (const char *dirname, const char **tag_file_name)
+{
+  static char *tagname;
+  static size_t tagsize;
+  struct exclusion_tag *tag;
+  size_t dlen = strlen (dirname);
+  int addslash = !ISSLASH (dirname[dlen-1]);
+  char *nptr = NULL;
+  
+  for (tag = exclusion_tags; tag; tag = tag->next)
+    {
+      size_t size = dlen + addslash + tag->length + 1;
+      if (size > tagsize)
+       {
+         tagsize = size;
+         tagname = xrealloc (tagname, tagsize);
+       }
+
+      if (!nptr)
+       {
+         strcpy (tagname, dirname);
+         nptr = tagname + dlen;
+         if (addslash)
+           *nptr++ = '/';
+       }
+      strcpy (nptr, tag->name);
+      if (access (tagname, F_OK) == 0
+         && (!tag->predicate || tag->predicate (tagname)))
+       {
+         if (tag_file_name)
+           *tag_file_name = tag->name;
+         return tag->type;
+       }
+    }
+
+  return exclusion_tag_none;
+}
+
+/* Exclusion predicate to test if the named file (usually "CACHEDIR.TAG")
+   contains a valid header, as described at:
+       http://www.brynosaurus.com/cachedir
+   Applications can write this file into directories they create
+   for use as caches containing purely regenerable, non-precious data,
+   allowing us to avoid archiving them if --exclude-caches is specified. */
+
+#define CACHEDIR_SIGNATURE "Signature: 8a477f597d28d172789f06886806bc55"
+#define CACHEDIR_SIGNATURE_SIZE (sizeof CACHEDIR_SIGNATURE - 1)
+
+bool
+cachedir_file_p (const char *name)
+{
+  bool tag_present = false;
+  int fd = open (name, O_RDONLY);
+  if (fd >= 0)
+    {
+      static char tagbuf[CACHEDIR_SIGNATURE_SIZE];
+
+      if (read (fd, tagbuf, CACHEDIR_SIGNATURE_SIZE)
+         == CACHEDIR_SIGNATURE_SIZE
+         && memcmp (tagbuf, CACHEDIR_SIGNATURE, CACHEDIR_SIGNATURE_SIZE) == 0)
+       tag_present = true;
+
+      close (fd);
+    }
+  return tag_present;
+}
+
 \f
 /* The maximum uintmax_t value that can be represented with DIGITS digits,
    assuming that each digit is BITS_PER_DIGIT wide.  */
 \f
 /* The maximum uintmax_t value that can be represented with DIGITS digits,
    assuming that each digit is BITS_PER_DIGIT wide.  */
@@ -128,7 +234,7 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize,
   char const *minval_string;
   char const *maxval_string = STRINGIFY_BIGINT (maxval, maxbuf);
   char const *value_string;
   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;
   if (gnu_format)
     {
       uintmax_t m = maxval + 1 ? maxval + 1 : maxval / 2 + 1;
@@ -138,7 +244,7 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize,
     }
   else
     minval_string = "0";
     }
   else
     minval_string = "0";
-  
+
   if (negative)
     {
       char *p = STRINGIFY_BIGINT (- value, valbuf + 1);
   if (negative)
     {
       char *p = STRINGIFY_BIGINT (- value, valbuf + 1);
@@ -147,13 +253,18 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize,
     }
   else
     value_string = STRINGIFY_BIGINT (value, valbuf);
     }
   else
     value_string = STRINGIFY_BIGINT (value, valbuf);
-         
+
   if (substitute)
     {
       int negsub;
       uintmax_t sub = substitute (&negsub) & maxval;
   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. */
+      /* NOTE: This is one of the few places where GNU_FORMAT differs from
+        OLDGNU_FORMAT.  The actual differences are:
+
+        1. In OLDGNU_FORMAT all strings in a tar header end in \0
+        2. Incremental archives use oldgnu_header.
+        
+        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);
       uintmax_t s = (negsub &= archive_format == GNU_FORMAT) ? - sub : sub;
       char subbuf[UINTMAX_STRSIZE_BOUND + 1];
       char *sub_string = STRINGIFY_BIGINT (s, subbuf + 1);
@@ -236,7 +347,7 @@ to_chars (int negative, uintmax_t value, size_t valsize,
     }
   else
     substitute = NULL; /* No substitution for formats, other than GNU */
     }
   else
     substitute = NULL; /* No substitution for formats, other than GNU */
-  
+
   return to_chars_subst (negative, gnu_format, value, valsize, substitute,
                         where, size, type);
 }
   return to_chars_subst (negative, gnu_format, value, valsize, substitute,
                         where, size, type);
 }
@@ -291,7 +402,8 @@ mode_to_chars (mode_t v, char *p, size_t s)
       && S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC
       && archive_format != POSIX_FORMAT
       && archive_format != USTAR_FORMAT
       && S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC
       && archive_format != POSIX_FORMAT
       && archive_format != USTAR_FORMAT
-      && archive_format != GNU_FORMAT)
+      && archive_format != GNU_FORMAT
+      && archive_format != OLDGNU_FORMAT)
     {
       negative = v < 0;
       u = v;
     {
       negative = v < 0;
       u = v;
@@ -380,7 +492,7 @@ bool
 file_dumpable_p (struct tar_stat_info *st)
 {
   if (dev_null_output)
 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);
 }
   return !(st->archive_file_size == 0
           && (st->stat.st_mode & MODE_R) == MODE_R);
 }
@@ -602,10 +714,10 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header)
   char *p;
   int type;
 
   char *p;
   int type;
 
-  if (extended_header.buffer || extended_header.stk == NULL)
+  if (st->xhdr.buffer || st->xhdr.stk == NULL)
     return old_header;
 
     return old_header;
 
-  xheader_finish (&extended_header);
+  xheader_finish (&st->xhdr);
   memcpy (hp.buffer, old_header, sizeof (hp));
   if (global)
     {
   memcpy (hp.buffer, old_header, sizeof (hp));
   if (global)
     {
@@ -617,7 +729,7 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header)
       type = XHDTYPE;
       p = xheader_xhdr_name (st);
     }
       type = XHDTYPE;
       p = xheader_xhdr_name (st);
     }
-  xheader_write (type, p, &extended_header);
+  xheader_write (type, p, &st->xhdr);
   free (p);
   header = find_next_block ();
   memcpy (header, &hp.buffer, sizeof (hp.buffer));
   free (p);
   header = find_next_block ();
   memcpy (header, &hp.buffer, sizeof (hp.buffer));
@@ -663,7 +775,8 @@ start_header (struct tar_stat_info *st)
   if (mode_option)
     st->stat.st_mode =
       ((st->stat.st_mode & ~MODE_ALL)
   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
 
   /* Paul Eggert tried the trivial test ($WRITER cf a b; $READER tvf a)
      for a few tars and came up with the following interoperability
@@ -735,7 +848,7 @@ start_header (struct tar_stat_info *st)
       {
        if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec
            || mtime.tv_nsec != 0)
       {
        if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec
            || mtime.tv_nsec != 0)
-         xheader_store ("mtime", st, NULL);
+         xheader_store ("mtime", st, &mtime);
        if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec)
          mtime.tv_sec = 0;
       }
        if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec)
          mtime.tv_sec = 0;
       }
@@ -929,7 +1042,7 @@ dump_regular_file (int fd, struct tar_stat_info *st)
   while (size_left > 0)
     {
       size_t bufsize, count;
   while (size_left > 0)
     {
       size_t bufsize, count;
-
+      
       mv_size_left (size_left);
 
       blk = find_next_block ();
       mv_size_left (size_left);
 
       blk = find_next_block ();
@@ -954,75 +1067,36 @@ dump_regular_file (int fd, struct tar_stat_info *st)
          return dump_status_short;
        }
       size_left -= count;
          return dump_status_short;
        }
       size_left -= count;
-      if (count)
-       set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE);
+      set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE);
 
       if (count != bufsize)
        {
          char buf[UINTMAX_STRSIZE_BOUND];
          memset (blk->buffer + count, 0, bufsize - count);
 
       if (count != bufsize)
        {
          char buf[UINTMAX_STRSIZE_BOUND];
          memset (blk->buffer + count, 0, bufsize - count);
-         WARN ((0, 0,
-                ngettext ("%s: File shrank by %s byte; padding with zeros",
-                          "%s: File shrank by %s bytes; padding with zeros",
-                          size_left),
-                quotearg_colon (st->orig_file_name),
-                STRINGIFY_BIGINT (size_left, buf)));
-         if (! ignore_failed_read_option)
-           exit_status = TAREXIT_FAILURE;
-         pad_archive (size_left - (bufsize-count));
+         WARNOPT (WARN_FILE_SHRANK,
+                  (0, 0,
+                   ngettext ("%s: File shrank by %s byte; padding with zeros",
+                             "%s: File shrank by %s bytes; padding with zeros",
+                             size_left),
+                   quotearg_colon (st->orig_file_name),
+                   STRINGIFY_BIGINT (size_left, buf)));
+         if (! ignore_failed_read_option) 
+           exit_status = TAREXIT_DIFFERS;
+         pad_archive (size_left - (bufsize - count));
          return dump_status_short;
        }
     }
   return dump_status_ok;
 }
 
          return dump_status_short;
        }
     }
   return dump_status_ok;
 }
 
-/* Look in directory DIRNAME for a cache directory tag file
-   with the magic name "CACHEDIR.TAG" and a standard header,
-   as described at:
-       http://www.brynosaurus.com/cachedir
-   Applications can write this file into directories they create
-   for use as caches containing purely regenerable, non-precious data,
-   allowing us to avoid archiving them if --exclude-caches is specified. */
-
-#define CACHEDIR_SIGNATURE "Signature: 8a477f597d28d172789f06886806bc55"
-#define CACHEDIR_SIGNATURE_SIZE (sizeof CACHEDIR_SIGNATURE - 1)
-
-static bool
-check_cache_directory (char *dirname)
-{
-  static char tagname[] = "CACHEDIR.TAG";
-  char *tagpath;
-  int fd;
-  int tag_present = false;
-
-  tagpath = xmalloc (strlen (dirname) + strlen (tagname) + 1);
-  strcpy (tagpath, dirname);
-  strcat (tagpath, tagname);
-
-  fd = open (tagpath, O_RDONLY);
-  if (fd >= 0)
-    {
-      static char tagbuf[CACHEDIR_SIGNATURE_SIZE];
-
-      if (read (fd, tagbuf, CACHEDIR_SIGNATURE_SIZE)
-         == CACHEDIR_SIGNATURE_SIZE
-         && memcmp (tagbuf, CACHEDIR_SIGNATURE, CACHEDIR_SIGNATURE_SIZE) == 0)
-       tag_present = true;
-
-      close (fd);
-    }
-
-  free (tagpath);
-
-  return tag_present;
-}
-
+\f
 static void
 dump_dir0 (char *directory,
           struct tar_stat_info *st, int top_level, dev_t parent_device)
 {
   dev_t our_device = st->stat.st_dev;
 static void
 dump_dir0 (char *directory,
           struct tar_stat_info *st, int top_level, dev_t parent_device)
 {
   dev_t our_device = st->stat.st_dev;
-
+  const char *tag_file_name;
+  
   if (!is_avoided_name (st->orig_file_name))
     {
       union block *blk = NULL;
   if (!is_avoided_name (st->orig_file_name))
     {
       union block *blk = NULL;
@@ -1101,47 +1175,67 @@ dump_dir0 (char *directory,
       && parent_device != st->stat.st_dev)
     {
       if (verbose_option)
       && parent_device != st->stat.st_dev)
     {
       if (verbose_option)
-       WARN ((0, 0,
-              _("%s: file is on a different filesystem; not dumped"),
-              quotearg_colon (st->orig_file_name)));
-      return;
+       WARNOPT (WARN_XDEV,
+                (0, 0,
+                 _("%s: file is on a different filesystem; not dumped"),
+                 quotearg_colon (st->orig_file_name)));
     }
     }
-
-  if (exclude_caches_option
-      && check_cache_directory(st->orig_file_name))
+  else
     {
     {
-      if (verbose_option)
-       WARN ((0, 0,
-              _("%s: contains a cache directory tag; not dumped"),
-              quotearg_colon (st->orig_file_name)));
-      return;
-    }
-
-  {
-    char const *entry;
-    size_t entry_len;
-    char *name_buf = xstrdup (st->orig_file_name);
-    size_t name_size = strlen (name_buf);
-    size_t name_len = name_size;
+      char *name_buf;
+      size_t name_size;
+      
+      switch (check_exclusion_tags (st->orig_file_name, &tag_file_name))
+       {
+       case exclusion_tag_all:
+         /* Handled in dump_file0 */
+         break;
+         
+       case exclusion_tag_none:
+         {
+           char const *entry;
+           size_t entry_len;
+           size_t name_len;
 
 
-    /* Now output all the files in the directory.  */
-    /* FIXME: Should speed this up by cd-ing into the dir.  */
+           name_buf = xstrdup (st->orig_file_name);
+           name_size = name_len = strlen (name_buf);
 
 
-    for (entry = directory; (entry_len = strlen (entry)) != 0;
-        entry += entry_len + 1)
-      {
-       if (name_size < name_len + entry_len)
-         {
-           name_size = name_len + entry_len;
-           name_buf = xrealloc (name_buf, name_size + 1);
+           /* Now output all the files in the directory.  */
+           /* FIXME: Should speed this up by cd-ing into the dir.  */
+           for (entry = directory; (entry_len = strlen (entry)) != 0;
+                entry += entry_len + 1)
+             {
+               if (name_size < name_len + entry_len)
+                 {
+                   name_size = name_len + entry_len;
+                   name_buf = xrealloc (name_buf, name_size + 1);
+                 }
+               strcpy (name_buf + name_len, entry);
+               if (!excluded_name (name_buf))
+                 dump_file (name_buf, 0, our_device);
+             }
+           
+           free (name_buf);
          }
          }
-       strcpy (name_buf + name_len, entry);
-       if (!excluded_name (name_buf))
+         break;
+
+       case exclusion_tag_contents:
+         exclusion_tag_warning (st->orig_file_name, tag_file_name,
+                                _("contents not dumped"));
+         name_size = strlen (st->orig_file_name) + strlen (tag_file_name) + 1;
+         name_buf = xmalloc (name_size);
+         strcpy (name_buf, st->orig_file_name);
+         strcat (name_buf, tag_file_name);
          dump_file (name_buf, 0, our_device);
          dump_file (name_buf, 0, our_device);
-      }
-
-    free (name_buf);
-  }
+         free (name_buf);
+         break;
+      
+       case exclusion_tag_under:
+         exclusion_tag_warning (st->orig_file_name, tag_file_name,
+                                _("contents not dumped"));
+         break;
+       }
+    }
 }
 
 /* Ensure exactly one trailing slash.  */
 }
 
 /* Ensure exactly one trailing slash.  */
@@ -1167,9 +1261,6 @@ dump_dir (int fd, struct tar_stat_info *st, int top_level, dev_t parent_device)
       return false;
     }
 
       return false;
     }
 
-  ensure_slash (&st->orig_file_name);
-  ensure_slash (&st->file_name);
-
   dump_dir0 (directory, st, top_level, parent_device);
 
   free (directory);
   dump_dir0 (directory, st, top_level, parent_device);
 
   free (directory);
@@ -1185,7 +1276,7 @@ create_archive (void)
   const char *p;
 
   open_archive (ACCESS_WRITE);
   const char *p;
 
   open_archive (ACCESS_WRITE);
-  xheader_write_global ();
+  buffer_write_global_xheader ();
 
   if (incremental_option)
     {
 
   if (incremental_option)
     {
@@ -1212,7 +1303,7 @@ create_archive (void)
              }
            memcpy (buffer, p, plen);
            if (! ISSLASH (buffer[plen - 1]))
              }
            memcpy (buffer, p, plen);
            if (! ISSLASH (buffer[plen - 1]))
-             buffer[plen++] = '/';
+             buffer[plen++] = DIRECTORY_SEPARATOR;
            q = gnu_list_name->dir_contents;
            if (q)
              while (*q)
            q = gnu_list_name->dir_contents;
            if (q)
              while (*q)
@@ -1270,8 +1361,9 @@ compare_links (void const *entry1, void const *entry2)
 static void
 unknown_file_error (char const *p)
 {
 static void
 unknown_file_error (char const *p)
 {
-  WARN ((0, 0, _("%s: Unknown file type; file ignored"),
-        quotearg_colon (p)));
+  WARNOPT (WARN_FILE_IGNORED,
+          (0, 0, _("%s: Unknown file type; file ignored"),
+           quotearg_colon (p)));
   if (!ignore_failed_read_option)
     exit_status = TAREXIT_FAILURE;
 }
   if (!ignore_failed_read_option)
     exit_status = TAREXIT_FAILURE;
 }
@@ -1289,7 +1381,7 @@ static Hash_table *link_table;
 static bool
 dump_hard_link (struct tar_stat_info *st)
 {
 static bool
 dump_hard_link (struct tar_stat_info *st)
 {
-  if (link_table && st->stat.st_nlink > 1)
+  if (link_table && (st->stat.st_nlink > 1 || remove_files_option))
     {
       struct link lp;
       struct link *duplicate;
     {
       struct link lp;
       struct link *duplicate;
@@ -1334,22 +1426,31 @@ dump_hard_link (struct tar_stat_info *st)
 static void
 file_count_links (struct tar_stat_info *st)
 {
 static void
 file_count_links (struct tar_stat_info *st)
 {
+  if (hard_dereference_option)
+    return;
   if (st->stat.st_nlink > 1)
     {
       struct link *duplicate;
   if (st->stat.st_nlink > 1)
     {
       struct link *duplicate;
-      struct link *lp = xmalloc (offsetof (struct link, name)
-                                + strlen (st->orig_file_name) + 1);
+      char *linkname = NULL;
+      struct link *lp;
+
+      assign_string (&linkname, st->orig_file_name);
+      transform_name (&linkname, XFORM_LINK);
+      
+      lp = xmalloc (offsetof (struct link, name)
+                                + strlen (linkname) + 1);
       lp->ino = st->stat.st_ino;
       lp->dev = st->stat.st_dev;
       lp->nlink = st->stat.st_nlink;
       lp->ino = st->stat.st_ino;
       lp->dev = st->stat.st_dev;
       lp->nlink = st->stat.st_nlink;
-      strcpy (lp->name, st->orig_file_name);
-
+      strcpy (lp->name, linkname);
+      free (linkname);
+      
       if (! ((link_table
              || (link_table = hash_initialize (0, 0, hash_link,
                                                compare_links, 0)))
             && (duplicate = hash_insert (link_table, lp))))
        xalloc_die ();
       if (! ((link_table
              || (link_table = hash_initialize (0, 0, hash_link,
                                                compare_links, 0)))
             && (duplicate = hash_insert (link_table, lp))))
        xalloc_die ();
-
+      
       if (duplicate != lp)
        abort ();
       lp->nlink--;
       if (duplicate != lp)
        abort ();
       lp->nlink--;
@@ -1371,7 +1472,7 @@ check_links (void)
     {
       if (lp->nlink)
        {
     {
       if (lp->nlink)
        {
-         WARN ((0, 0, _("Missing links to %s.\n"), quote (lp->name)));
+         WARN ((0, 0, _("Missing links to %s."), quote (lp->name)));
        }
     }
 }
        }
     }
 }
@@ -1392,6 +1493,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
 {
   union block *header;
   char type;
 {
   union block *header;
   char type;
+  off_t original_size;
   struct timespec original_ctime;
   struct timespec restore_times[2];
   off_t block_ordinal = -1;
   struct timespec original_ctime;
   struct timespec restore_times[2];
   off_t block_ordinal = -1;
@@ -1404,14 +1506,14 @@ dump_file0 (struct tar_stat_info *st, const char *p,
   assign_string (&st->file_name,
                  safer_name_suffix (p, false, absolute_names_option));
 
   assign_string (&st->file_name,
                  safer_name_suffix (p, false, absolute_names_option));
 
-  transform_name (&st->file_name);
+  transform_name (&st->file_name, XFORM_REGFILE);
 
   if (deref_stat (dereference_option, p, &st->stat) != 0)
     {
       stat_diag (p);
       return;
     }
 
   if (deref_stat (dereference_option, p, &st->stat) != 0)
     {
       stat_diag (p);
       return;
     }
-  st->archive_file_size = st->stat.st_size;
+  st->archive_file_size = original_size = st->stat.st_size;
   st->atime = restore_times[0] = get_stat_atime (&st->stat);
   st->mtime = restore_times[1] = get_stat_mtime (&st->stat);
   st->ctime = original_ctime = get_stat_ctime (&st->stat);
   st->atime = restore_times[0] = get_stat_atime (&st->stat);
   st->mtime = restore_times[1] = get_stat_mtime (&st->stat);
   st->ctime = original_ctime = get_stat_ctime (&st->stat);
@@ -1431,26 +1533,28 @@ dump_file0 (struct tar_stat_info *st, const char *p,
 
   /* See if we want only new files, and check if this one is too old to
      put in the archive.
 
   /* See if we want only new files, and check if this one is too old to
      put in the archive.
-
+     
      This check is omitted if incremental_option is set *and* the
      requested file is not explicitely listed in the command line. */
      This check is omitted if incremental_option is set *and* the
      requested file is not explicitely listed in the command line. */
-
+  
   if (!(incremental_option && !is_individual_file (p))
       && !S_ISDIR (st->stat.st_mode)
       && OLDER_TAR_STAT_TIME (*st, m)
       && (!after_date_option || OLDER_TAR_STAT_TIME (*st, c)))
     {
       if (!incremental_option && verbose_option)
   if (!(incremental_option && !is_individual_file (p))
       && !S_ISDIR (st->stat.st_mode)
       && OLDER_TAR_STAT_TIME (*st, m)
       && (!after_date_option || OLDER_TAR_STAT_TIME (*st, c)))
     {
       if (!incremental_option && verbose_option)
-       WARN ((0, 0, _("%s: file is unchanged; not dumped"),
-              quotearg_colon (p)));
+       WARNOPT (WARN_FILE_UNCHANGED,
+                (0, 0, _("%s: file is unchanged; not dumped"),
+                 quotearg_colon (p)));
       return;
     }
 
   /* See if we are trying to dump the archive.  */
   if (sys_file_is_archive (st))
     {
       return;
     }
 
   /* See if we are trying to dump the archive.  */
   if (sys_file_is_archive (st))
     {
-      WARN ((0, 0, _("%s: file is the archive; not dumped"),
-            quotearg_colon (p)));
+      WARNOPT (WARN_IGNORE_ARCHIVE,
+              (0, 0, _("%s: file is the archive; not dumped"),
+               quotearg_colon (p)));
       return;
     }
 
       return;
     }
 
@@ -1479,8 +1583,9 @@ dump_file0 (struct tar_stat_info *st, const char *p,
          if (fd < 0)
            {
              if (!top_level && errno == ENOENT)
          if (fd < 0)
            {
              if (!top_level && errno == ENOENT)
-               WARN ((0, 0, _("%s: File removed before we read it"),
-                      quotearg_colon (p)));
+               WARNOPT (WARN_FILE_REMOVED,
+                        (0, 0, _("%s: File removed before we read it"),
+                         quotearg_colon (p)));
              else
                open_diag (p);
              return;
              else
                open_diag (p);
              return;
@@ -1489,6 +1594,18 @@ dump_file0 (struct tar_stat_info *st, const char *p,
 
       if (is_dir)
        {
 
       if (is_dir)
        {
+         const char *tag_file_name;
+         ensure_slash (&st->orig_file_name);
+         ensure_slash (&st->file_name);
+
+         if (check_exclusion_tags (st->orig_file_name, &tag_file_name)
+             == exclusion_tag_all)
+           {
+             exclusion_tag_warning (st->orig_file_name, tag_file_name,
+                                    _("directory not dumped"));
+             return;
+           }
+         
          ok = dump_dir (fd, st, top_level, parent_device);
 
          /* dump_dir consumes FD if successful.  */
          ok = dump_dir (fd, st, top_level, parent_device);
 
          /* dump_dir consumes FD if successful.  */
@@ -1499,7 +1616,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
        {
          enum dump_status status;
 
        {
          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)
            {
              status = sparse_dump_file (fd, st);
              if (status == dump_status_not_implemented)
@@ -1513,6 +1630,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
            case dump_status_ok:
            case dump_status_short:
              mv_end ();
            case dump_status_ok:
            case dump_status_short:
              mv_end ();
+             file_count_links (st);
              break;
 
            case dump_status_fail:
              break;
 
            case dump_status_fail:
@@ -1522,8 +1640,6 @@ dump_file0 (struct tar_stat_info *st, const char *p,
              abort ();
            }
 
              abort ();
            }
 
-         file_count_links (st);
-         
          ok = status == dump_status_ok;
        }
 
          ok = status == dump_status_ok;
        }
 
@@ -1548,9 +1664,18 @@ dump_file0 (struct tar_stat_info *st, const char *p,
 
       if (ok)
        {
 
       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)));
+         if ((timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0
+              /* Original ctime will change if the file is a directory and
+                 --remove-files is given */
+              && !(remove_files_option && is_dir))
+             || original_size < final_stat.st_size)
+           {
+             WARNOPT (WARN_FILE_CHANGED,
+                      (0, 0, _("%s: file changed as we read it"),
+                       quotearg_colon (p)));
+             if (exit_status == TAREXIT_SUCCESS)
+               exit_status = TAREXIT_DIFFERS;
+           }
          else if (atime_preserve_option == replace_atime_preserve
                   && set_file_atime (fd, p, restore_times) != 0)
            utime_error (p);
          else if (atime_preserve_option == replace_atime_preserve
                   && set_file_atime (fd, p, restore_times) != 0)
            utime_error (p);
@@ -1595,6 +1720,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
        }
       buffer[size] = '\0';
       assign_string (&st->link_name, buffer);
        }
       buffer[size] = '\0';
       assign_string (&st->link_name, buffer);
+      transform_name (&st->link_name, XFORM_SYMLINK);
       if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
        write_long_link (st);
 
       if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
        write_long_link (st);
 
@@ -1603,7 +1729,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
       header = start_header (st);
       if (!header)
        return;
       header = start_header (st);
       if (!header)
        return;
-      tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE);
+      tar_copy_str (header->header.linkname, st->link_name, NAME_FIELD_SIZE);
       header->header.typeflag = SYMTYPE;
       finish_header (st, header, block_ordinal);
       /* nothing more to do to it */
       header->header.typeflag = SYMTYPE;
       finish_header (st, header, block_ordinal);
       /* nothing more to do to it */
@@ -1625,12 +1751,14 @@ dump_file0 (struct tar_stat_info *st, const char *p,
     type = FIFOTYPE;
   else if (S_ISSOCK (st->stat.st_mode))
     {
     type = FIFOTYPE;
   else if (S_ISSOCK (st->stat.st_mode))
     {
-      WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p)));
+      WARNOPT (WARN_FILE_IGNORED,
+              (0, 0, _("%s: socket ignored"), quotearg_colon (p)));
       return;
     }
   else if (S_ISDOOR (st->stat.st_mode))
     {
       return;
     }
   else if (S_ISDOOR (st->stat.st_mode))
     {
-      WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p)));
+      WARNOPT (WARN_FILE_IGNORED,
+              (0, 0, _("%s: door ignored"), quotearg_colon (p)));
       return;
     }
   else
       return;
     }
   else
This page took 0.036588 seconds and 4 git commands to generate.