X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2Fcreate.c;h=7b20c02b79786df884a27b0d970dadc19ed4f7aa;hb=57bfbbde90dfcc18ee6b1e27c01ba915ecc56312;hp=641603afef5dd5b8e97434269a0a25d97c2fbfd0;hpb=137ebf41fd97baa7e4f2a66e0da0bdb182ca6f7b;p=chaz%2Ftar diff --git a/src/create.c b/src/create.c index 641603a..7b20c02 100644 --- a/src/create.c +++ b/src/create.c @@ -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 @@ -34,38 +34,55 @@ struct link char name[1]; }; -struct exclude_tag +struct exclusion_tag { const char *name; size_t length; - struct exclude_tag *next; + enum exclusion_tag_type type; + bool (*predicate) (const char *name); + struct exclusion_tag *next; }; -static struct exclude_tag *exclude_tags; +static struct exclusion_tag *exclusion_tags; void -add_exclude_tag (const char *name) +add_exclusion_tag (const char *name, enum exclusion_tag_type type, + bool (*predicate) (const char *name)) { - struct exclude_tag *tag = xmalloc (sizeof tag[0]); - tag->next = exclude_tags; + 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); - exclude_tags = tag; + exclusion_tags = tag; } -static bool -check_exclude_tags (char *dirname) +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)); +} + +enum exclusion_tag_type +check_exclusion_tags (char *dirname, const char **tag_file_name) { static char *tagname; static size_t tagsize; - struct exclude_tag *tag; + struct exclusion_tag *tag; size_t dlen = strlen (dirname); + int addslash = dirname[dlen-1] != '/'; char *nptr = NULL; - char *ret = NULL; - for (tag = exclude_tags; tag; tag = tag->next) + for (tag = exclusion_tags; tag; tag = tag->next) { - size_t size = dlen + tag->length + 1; + size_t size = dlen + addslash + tag->length + 1; if (size > tagsize) { tagsize = size; @@ -76,20 +93,49 @@ check_exclude_tags (char *dirname) { strcpy (tagname, dirname); nptr = tagname + dlen; + if (addslash) + *nptr++ = '/'; } strcpy (nptr, tag->name); - if (access (tagname, F_OK) == 0) + if (access (tagname, F_OK) == 0 + && (!tag->predicate || tag->predicate (tagname))) { - if (verbose_option) - WARN ((0, 0, - _("%s: contains a cache directory tag %s; not dumped"), - quotearg_colon (dirname), - quotearg_n (1, tag->name))); - return true; + if (tag_file_name) + *tag_file_name = tag->name; + return tag->type; } } - return false; + 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; } @@ -667,10 +713,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) { @@ -682,7 +728,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)); @@ -995,7 +1041,7 @@ dump_regular_file (int fd, struct tar_stat_info *st) while (size_left > 0) { size_t bufsize, count; - + mv_size_left (size_left); blk = find_next_block (); @@ -1020,8 +1066,7 @@ dump_regular_file (int fd, struct tar_stat_info *st) 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) { @@ -1035,7 +1080,7 @@ dump_regular_file (int fd, struct tar_stat_info *st) STRINGIFY_BIGINT (size_left, buf))); if (! ignore_failed_read_option) exit_status = TAREXIT_DIFFERS; - pad_archive (size_left - (bufsize-count)); + pad_archive (size_left - (bufsize - count)); return dump_status_short; } } @@ -1043,53 +1088,13 @@ dump_regular_file (int fd, struct tar_stat_info *st) } -/* 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; - bool 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; @@ -1171,47 +1176,63 @@ 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; - } - - if (check_exclude_tags (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); - } - - 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. */ @@ -1237,9 +1258,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); @@ -1255,7 +1273,7 @@ create_archive (void) const char *p; open_archive (ACCESS_WRITE); - xheader_write_global (); + buffer_write_global_xheader (); if (incremental_option) { @@ -1404,6 +1422,8 @@ dump_hard_link (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; @@ -1560,6 +1580,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. */ @@ -1584,6 +1616,7 @@ dump_file0 (struct tar_stat_info *st, const char *p, case dump_status_ok: case dump_status_short: mv_end (); + file_count_links (st); break; case dump_status_fail: @@ -1593,8 +1626,6 @@ dump_file0 (struct tar_stat_info *st, const char *p, abort (); } - file_count_links (st); - ok = status == dump_status_ok; } @@ -1619,7 +1650,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"), @@ -1671,6 +1705,8 @@ dump_file0 (struct tar_stat_info *st, const char *p, } buffer[size] = '\0'; assign_string (&st->link_name, buffer); + if (transform_symlinks_option) + transform_name (&st->link_name); if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size) write_long_link (st); @@ -1679,7 +1715,7 @@ dump_file0 (struct tar_stat_info *st, const char *p, 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 */