X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2Fcreate.c;h=c9be71cf6587d6a3150cc24e0895aad4a9611a90;hb=c6cde895e342171d76ce895e06a1afc65897f726;hp=689fd27e051597a7c54f79b5a800acb09b8ff91d;hpb=2bda83b48d8a6807632312403561b11b79048443;p=chaz%2Ftar diff --git a/src/create.c b/src/create.c index 689fd27..c9be71c 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 Free Software Foundation, Inc. + 2003, 2004, 2005 Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-25. @@ -17,9 +17,9 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "system.h" +#include #if HAVE_UTIME_H # include @@ -370,6 +370,16 @@ tar_copy_str (char *dst, const char *src, size_t len) strncpy (dst, src, len); } +/* Same as tar_copy_str, but always terminate with NUL if using + is OLDGNU format */ +static void +tar_name_copy_str (char *dst, const char *src, size_t len) +{ + tar_copy_str (dst, src, len); + if (archive_format == OLDGNU_FORMAT) + dst[len-1] = 0; +} + /* Write a "private" header */ union block * start_private_header (const char *name, size_t size) @@ -379,7 +389,7 @@ start_private_header (const char *name, size_t size) memset (header->buffer, 0, sizeof (union block)); - tar_copy_str (header->header.name, name, NAME_FIELD_SIZE); + tar_name_copy_str (header->header.name, name, NAME_FIELD_SIZE); OFF_TO_CHARS (size, header->header.size); time (&t); @@ -402,10 +412,15 @@ write_short_name (struct tar_stat_info *st) { union block *header = find_next_block (); memset (header->buffer, 0, sizeof (union block)); - tar_copy_str (header->header.name, st->file_name, NAME_FIELD_SIZE); + tar_name_copy_str (header->header.name, st->file_name, NAME_FIELD_SIZE); return header; } +#define FILL(field,byte) do { \ + memset(field, byte, sizeof(field)-1); \ + (field)[sizeof(field)-1] = 0; \ +} while (0) + /* Write a GNUTYPE_LONGLINK or GNUTYPE_LONGNAME block. */ static void write_gnu_long_link (struct tar_stat_info *st, const char *p, char type) @@ -413,8 +428,22 @@ write_gnu_long_link (struct tar_stat_info *st, const char *p, char type) size_t size = strlen (p) + 1; size_t bufsize; union block *header; + char *tmpname; header = start_private_header ("././@LongLink", size); + FILL(header->header.mtime, '0'); + FILL(header->header.mode, '0'); + FILL(header->header.uid, '0'); + FILL(header->header.gid, '0'); + FILL(header->header.devmajor, 0); + FILL(header->header.devminor, 0); + uid_to_uname (0, &tmpname); + UNAME_TO_CHARS (tmpname, header->header.uname); + free (tmpname); + gid_to_gname (0, &tmpname); + GNAME_TO_CHARS (tmpname, header->header.gname); + free (tmpname); + strcpy (header->header.magic, OLDGNU_MAGIC); header->header.typeflag = type; finish_header (st, header, -1); @@ -459,18 +488,18 @@ write_ustar_long_name (const char *name) if (length > PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1) { - WARN ((0, 0, _("%s: file name is too long (max %d); not dumped"), - quotearg_colon (name), - PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1)); + ERROR ((0, 0, _("%s: file name is too long (max %d); not dumped"), + quotearg_colon (name), + PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1)); return NULL; } i = split_long_name (name, length); if (i == 0 || length - i - 1 > NAME_FIELD_SIZE) { - WARN ((0, 0, - _("%s: file name is too long (cannot be split); not dumped"), - quotearg_colon (name))); + ERROR ((0, 0, + _("%s: file name is too long (cannot be split); not dumped"), + quotearg_colon (name))); return NULL; } @@ -495,10 +524,10 @@ write_long_link (struct tar_stat_info *st) case V7_FORMAT: /* old V7 tar format */ case USTAR_FORMAT: case STAR_FORMAT: - WARN ((0, 0, - _("%s: link name is too long; not dumped"), - quotearg_colon (st->link_name))); - break; + ERROR ((0, 0, + _("%s: link name is too long; not dumped"), + quotearg_colon (st->link_name))); + break; case OLDGNU_FORMAT: case GNU_FORMAT: @@ -522,9 +551,9 @@ write_long_name (struct tar_stat_info *st) case V7_FORMAT: if (strlen (st->file_name) > NAME_FIELD_SIZE-1) { - WARN ((0, 0, _("%s: file name is too long (max %d); not dumped"), - quotearg_colon (st->file_name), - NAME_FIELD_SIZE - 1)); + ERROR ((0, 0, _("%s: file name is too long (max %d); not dumped"), + quotearg_colon (st->file_name), + NAME_FIELD_SIZE - 1)); return NULL; } break; @@ -571,7 +600,9 @@ write_header_name (struct tar_stat_info *st) xheader_store ("path", st, NULL); return write_short_name (st); } - else if (NAME_FIELD_SIZE < strlen (st->file_name)) + else if ((archive_format == OLDGNU_FORMAT + && OLDGNU_NAME_FIELD_SIZE < strlen (st->file_name)) + || NAME_FIELD_SIZE < strlen (st->file_name)) return write_long_name (st); else return write_short_name (st); @@ -599,8 +630,9 @@ start_header (struct tar_stat_info *st) if (group_option != (gid_t) -1) st->stat.st_gid = group_option; if (mode_option) - st->stat.st_mode = ((st->stat.st_mode & ~MODE_ALL) - | mode_adjust (st->stat.st_mode, mode_option)); + st->stat.st_mode = + ((st->stat.st_mode & ~MODE_ALL) + | mode_adjust (st->stat.st_mode, mode_option, initial_umask)); /* Paul Eggert tried the trivial test ($WRITER cf a b; $READER tvf a) for a few tars and came up with the following interoperability @@ -664,7 +696,7 @@ start_header (struct tar_stat_info *st) else MAJOR_TO_CHARS (st->devminor, header->header.devminor); } - else + else if (archive_format != GNU_FORMAT && archive_format != OLDGNU_FORMAT) { MAJOR_TO_CHARS (0, header->header.devmajor); MINOR_TO_CHARS (0, header->header.devminor); @@ -828,7 +860,7 @@ dump_regular_file (int fd, struct tar_stat_info *st) if (multi_volume_option) { - assign_string (&save_name, st->file_name); + assign_string (&save_name, st->orig_file_name); save_sizeleft = size_left; save_totsize = st->stat.st_size; } @@ -854,9 +886,9 @@ dump_regular_file (int fd, struct tar_stat_info *st) return dump_status_short; } size_left -= count; - - set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE); - + if (count) + set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE); + if (count != bufsize) { char buf[UINTMAX_STRSIZE_BOUND]; @@ -869,7 +901,7 @@ dump_regular_file (int fd, struct tar_stat_info *st) STRINGIFY_BIGINT (size_left, buf))); if (! ignore_failed_read_option) exit_status = TAREXIT_FAILURE; - pad_archive (size_left); + pad_archive (size_left - (bufsize-count)); return dump_status_short; } } @@ -903,6 +935,47 @@ dump_regular_finish (int fd, struct tar_stat_info *st, time_t original_ctime) } } +/* 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) @@ -993,10 +1066,20 @@ dump_dir0 (char *directory, 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; - char *name_buf = strdup (st->orig_file_name); + char *name_buf = xstrdup (st->orig_file_name); size_t name_size = strlen (name_buf); size_t name_len = name_size; @@ -1128,8 +1211,8 @@ create_archive (void) /* Calculate the hash of a link. */ -static unsigned -hash_link (void const *entry, unsigned n_buckets) +static size_t +hash_link (void const *entry, size_t n_buckets) { struct link const *l = entry; uintmax_t num = l->dev ^ l->ino; @@ -1180,13 +1263,16 @@ dump_hard_link (struct tar_stat_info *st) if ((duplicate = hash_lookup (link_table, &lp))) { /* We found a link. */ - char const *link_name = safer_name_suffix (duplicate->name, true); + char const *link_name = safer_name_suffix (duplicate->name, true, + absolute_names_option); duplicate->nlink--; block_ordinal = current_block_ordinal (); assign_string (&st->link_name, link_name); - if (NAME_FIELD_SIZE < strlen (link_name)) + if ((archive_format == OLDGNU_FORMAT + && OLDGNU_NAME_FIELD_SIZE < strlen (link_name)) + || NAME_FIELD_SIZE < strlen (link_name)) write_long_link (st); st->stat.st_size = 0; @@ -1247,7 +1333,7 @@ check_links (void) { if (lp->nlink) { - WARN ((0, 0, _("Missing links to '%s'.\n"), lp->name)); + WARN ((0, 0, _("Missing links to %s.\n"), quote (lp->name))); } } } @@ -1276,7 +1362,8 @@ dump_file0 (struct tar_stat_info *st, char *p, return; assign_string (&st->orig_file_name, p); - assign_string (&st->file_name, safer_name_suffix (p, false)); + assign_string (&st->file_name, + safer_name_suffix (p, false, absolute_names_option)); if (deref_stat (dereference_option, p, &st->stat) != 0) { @@ -1303,16 +1390,19 @@ dump_file0 (struct tar_stat_info *st, char *p, #endif /* See if we want only new files, and check if this one is too old to - put in the archive. */ + 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. */ - if (!S_ISDIR (st->stat.st_mode) + if (!(incremental_option && !is_individual_file (p)) + && !S_ISDIR (st->stat.st_mode) && OLDER_STAT_TIME (st->stat, m) && (!after_date_option || OLDER_STAT_TIME (st->stat, c))) { - if (0 < top_level) /* equivalent to !incremental_option */ + if (!incremental_option && verbose_option) WARN ((0, 0, _("%s: file is unchanged; not dumped"), quotearg_colon (p))); - /* FIXME: recheck this return. */ return; } @@ -1324,6 +1414,8 @@ dump_file0 (struct tar_stat_info *st, char *p, return; } + if (is_avoided_name (p)) + return; if (S_ISDIR (st->stat.st_mode)) { dump_dir (st, top_level, parent_device); @@ -1331,8 +1423,6 @@ dump_file0 (struct tar_stat_info *st, char *p, utime (p, &restore_times); return; } - else if (is_avoided_name (p)) - return; else { /* Check for multiple links. */ @@ -1417,7 +1507,8 @@ dump_file0 (struct tar_stat_info *st, char *p, } buffer[size] = '\0'; assign_string (&st->link_name, buffer); - if (size > NAME_FIELD_SIZE) + if ((archive_format == OLDGNU_FORMAT && size > OLDGNU_NAME_FIELD_SIZE) + || size > NAME_FIELD_SIZE) write_long_link (st); block_ordinal = current_block_ordinal ();