X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fcreate.c;h=eaaf8a75af5b3894f686e9b000395864b379abc8;hb=ccdbafb2769c16a55565b387acea44a247001f6a;hp=6eb6ad259e4b2d2bce8797e726f509994ab6278b;hpb=304d8b9f0cac1e25be00d14c1b643a11879a6f85;p=chaz%2Ftar diff --git a/src/create.c b/src/create.c index 6eb6ad2..eaaf8a7 100644 --- a/src/create.c +++ b/src/create.c @@ -1,7 +1,7 @@ /* 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. @@ -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; +} + /* The maximum uintmax_t value that can be represented with DIGITS digits, assuming that each digit is BITS_PER_DIGIT wide. */ @@ -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; -} - + 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); @@ -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"),