]> Dogcows Code - chaz/tar/blobdiff - src/create.c
Relicense under GPLv3
[chaz/tar] / src / create.c
index 6eb6ad259e4b2d2bce8797e726f509994ab6278b..a9cf981df748827ae5033bf813db1d9f81eb691b 100644 (file)
@@ -1,13 +1,13 @@
 /* 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 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
-   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
@@ -33,6 +33,109 @@ struct link
     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;
+}
+
+static void
+exclusion_tag_warning (const char *dirname, const char *tagname,
+                      const char *message)
+{
+  if (verbose_option)
+    WARN ((0, 0,
+          _("%s: contains a cache directory tag %s; %s"),
+          quotearg_colon (dirname),
+          quotearg_n (1, tagname),
+          message));
+}
+
+static enum exclusion_tag_type 
+check_exclusion_tags (char *dirname, const char **tag_file_name)
+{
+  static char *tagname;
+  static size_t tagsize;
+  struct exclusion_tag *tag;
+  size_t dlen = strlen (dirname);
+  char *nptr = NULL;
+  char *ret = NULL;
+  
+  for (tag = exclusion_tags; tag; tag = tag->next)
+    {
+      size_t size = dlen + tag->length + 1;
+      if (size > tagsize)
+       {
+         tagsize = size;
+         tagname = xrealloc (tagname, tagsize);
+       }
+
+      if (!nptr)
+       {
+         strcpy (tagname, dirname);
+         nptr = tagname + dlen;
+       }
+      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.  */
@@ -608,10 +711,10 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header)
   char *p;
   int type;
 
-  if (extended_header.buffer || extended_header.stk == NULL)
+  if (st->xhdr.buffer || st->xhdr.stk == NULL)
     return old_header;
 
-  xheader_finish (&extended_header);
+  xheader_finish (&st->xhdr);
   memcpy (hp.buffer, old_header, sizeof (hp));
   if (global)
     {
@@ -623,7 +726,7 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header)
       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));
@@ -983,53 +1086,14 @@ dump_regular_file (int fd, struct tar_stat_info *st)
   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;
-
+  const char *tag_file_name;
+  
   if (!is_avoided_name (st->orig_file_name))
     {
       union block *blk = NULL;
@@ -1111,44 +1175,60 @@ dump_dir0 (char *directory,
        WARN ((0, 0,
               _("%s: file is on a different filesystem; not dumped"),
               quotearg_colon (st->orig_file_name)));
-      return;
     }
-
-  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_none:
+       case exclusion_tag_all:
+         {
+           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);
-      }
-
-    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.  */
@@ -1174,9 +1254,6 @@ dump_dir (int fd, struct tar_stat_info *st, int top_level, dev_t parent_device)
       return false;
     }
 
-  ensure_slash (&st->orig_file_name);
-  ensure_slash (&st->file_name);
-
   dump_dir0 (directory, st, top_level, parent_device);
 
   free (directory);
@@ -1192,7 +1269,7 @@ create_archive (void)
   const char *p;
 
   open_archive (ACCESS_WRITE);
-  xheader_write_global ();
+  buffer_write_global_xheader ();
 
   if (incremental_option)
     {
@@ -1497,6 +1574,18 @@ dump_file0 (struct tar_stat_info *st, const char *p,
 
       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.  */
@@ -1556,7 +1645,10 @@ dump_file0 (struct tar_stat_info *st, const char *p,
 
       if (ok)
        {
-         if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0
+         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)
            {
              WARN ((0, 0, _("%s: file changed as we read it"),
This page took 0.031755 seconds and 4 git commands to generate.