X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fcreate.c;h=23e9efdbd302f8537c872d4561b69280ae04fa54;hb=b15e3f1bbd5ed220e27d3b48671c18d814b0e921;hp=7955e6b118bc9bb97e5648cc5da25ff1938618df;hpb=a169d65b94722c55b0daa20997a1cb02307159cf;p=chaz%2Ftar diff --git a/src/create.c b/src/create.c index 7955e6b..23e9efd 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 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006 Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-25. @@ -33,6 +33,65 @@ struct link size_t nlink; char name[1]; }; + +struct exclude_tag +{ + const char *name; + size_t length; + struct exclude_tag *next; +}; + +static struct exclude_tag *exclude_tags; + +void +add_exclude_tag (const char *name) +{ + struct exclude_tag *tag = xmalloc (sizeof tag[0]); + tag->next = exclude_tags; + tag->name = name; + tag->length = strlen (name); + exclude_tags = tag; +} + +static bool +check_exclude_tags (char *dirname) +{ + static char *tagname; + static size_t tagsize; + struct exclude_tag *tag; + size_t dlen = strlen (dirname); + char *nptr = NULL; + char *ret = NULL; + + for (tag = exclude_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) + { + 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; + } + } + + return false; +} + /* The maximum uintmax_t value that can be represented with DIGITS digits, assuming that each digit is BITS_PER_DIGIT wide. */ @@ -128,8 +187,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 *p; - + if (gnu_format) { uintmax_t m = maxval + 1 ? maxval + 1 : maxval / 2 + 1; @@ -139,7 +197,7 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize, } else minval_string = "0"; - + if (negative) { char *p = STRINGIFY_BIGINT (- value, valbuf + 1); @@ -148,13 +206,18 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize, } 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. */ + /* 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); @@ -163,11 +226,12 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize, 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); + 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 @@ -236,7 +300,7 @@ to_chars (int negative, uintmax_t value, size_t valsize, } else substitute = NULL; /* No substitution for formats, other than GNU */ - + return to_chars_subst (negative, gnu_format, value, valsize, substitute, where, size, type); } @@ -291,7 +355,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 - && archive_format != GNU_FORMAT) + && archive_format != GNU_FORMAT + && archive_format != OLDGNU_FORMAT) { negative = v < 0; u = v; @@ -380,7 +445,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); } @@ -494,7 +559,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; @@ -663,7 +728,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 @@ -730,12 +796,12 @@ start_header (struct tar_stat_info *st) } { - 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 || 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; } @@ -967,8 +1033,8 @@ dump_regular_file (int fd, struct tar_stat_info *st) size_left), quotearg_colon (st->orig_file_name), STRINGIFY_BIGINT (size_left, buf))); - if (! ignore_failed_read_option) - exit_status = TAREXIT_FAILURE; + if (! ignore_failed_read_option) + exit_status = TAREXIT_DIFFERS; pad_archive (size_left - (bufsize-count)); return dump_status_short; } @@ -976,6 +1042,7 @@ 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: @@ -993,7 +1060,7 @@ check_cache_directory (char *dirname) static char tagname[] = "CACHEDIR.TAG"; char *tagpath; int fd; - int tag_present = false; + bool tag_present = false; tagpath = xmalloc (strlen (dirname) + strlen (tagname) + 1); strcpy (tagpath, dirname); @@ -1106,17 +1173,7 @@ dump_dir0 (char *directory, quotearg_colon (st->orig_file_name))); return; } - - if (exclude_caches_option - && check_cache_directory(st->orig_file_name)) - { - 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; @@ -1167,9 +1224,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); @@ -1182,7 +1236,7 @@ dump_dir (int fd, 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 (); @@ -1387,11 +1441,12 @@ check_links (void) exit_status to failure, a clear diagnostic has been issued. */ static void -dump_file0 (struct tar_stat_info *st, char const *p, +dump_file0 (struct tar_stat_info *st, const char *p, int top_level, dev_t parent_device) { union block *header; char type; + off_t original_size; struct timespec original_ctime; struct timespec restore_times[2]; off_t block_ordinal = -1; @@ -1404,12 +1459,14 @@ dump_file0 (struct tar_stat_info *st, char const *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); 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); @@ -1487,6 +1544,22 @@ dump_file0 (struct tar_stat_info *st, char const *p, if (is_dir) { + ensure_slash (&st->orig_file_name); + ensure_slash (&st->file_name); + + if (exclude_caches_option + && check_cache_directory (st->orig_file_name)) + { + 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; + ok = dump_dir (fd, st, top_level, parent_device); /* dump_dir consumes FD if successful. */ @@ -1497,7 +1570,7 @@ dump_file0 (struct tar_stat_info *st, char const *p, { 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) @@ -1520,6 +1593,8 @@ dump_file0 (struct tar_stat_info *st, char const *p, abort (); } + file_count_links (st); + ok = status == dump_status_ok; } @@ -1544,9 +1619,14 @@ dump_file0 (struct tar_stat_info *st, char const *p, 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_size < final_stat.st_size) + { + WARN ((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); @@ -1665,7 +1745,7 @@ dump_file0 (struct tar_stat_info *st, char const *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);