X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fcreate.c;h=2ca1d6ee34ba342cf8431b88c52b2f5df8052295;hb=a59c819beb4886ee43f16dfd80ec1151fda1abe6;hp=d4b9ae7e4738bb0909d7c7905f24a6f4325c1443;hpb=4dfcd6c054a5e9e1a371c822a3be90564dd9b690;p=chaz%2Ftar diff --git a/src/create.c b/src/create.c index d4b9ae7..2ca1d6e 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, 2007, 2009 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-25. @@ -30,7 +30,7 @@ struct link { dev_t dev; ino_t ino; - size_t nlink; + nlink_t nlink; char name[1]; }; @@ -71,7 +71,7 @@ exclusion_tag_warning (const char *dirname, const char *tagname, message)); } -enum exclusion_tag_type +enum exclusion_tag_type check_exclusion_tags (const char *dirname, const char **tag_file_name) { static char *tagname; @@ -79,8 +79,8 @@ check_exclusion_tags (const char *dirname, const char **tag_file_name) struct exclusion_tag *tag; size_t dlen = strlen (dirname); int addslash = !ISSLASH (dirname[dlen-1]); - char *nptr = NULL; - + size_t noff = 0; + for (tag = exclusion_tags; tag; tag = tag->next) { size_t size = dlen + addslash + tag->length + 1; @@ -90,14 +90,14 @@ check_exclusion_tags (const char *dirname, const char **tag_file_name) tagname = xrealloc (tagname, tagsize); } - if (!nptr) + if (noff == 0) { strcpy (tagname, dirname); - nptr = tagname + dlen; + noff = dlen; if (addslash) - *nptr++ = '/'; + tagname[noff++] = '/'; } - strcpy (nptr, tag->name); + strcpy (tagname + noff, tag->name); if (access (tagname, F_OK) == 0 && (!tag->predicate || tag->predicate (tagname))) { @@ -214,6 +214,14 @@ to_base256 (int negative, uintmax_t value, char *where, size_t size) while (i); } +#define GID_TO_CHARS(val, where) gid_to_chars (val, where, sizeof (where)) +#define MAJOR_TO_CHARS(val, where) major_to_chars (val, where, sizeof (where)) +#define MINOR_TO_CHARS(val, where) minor_to_chars (val, where, sizeof (where)) +#define MODE_TO_CHARS(val, where) mode_to_chars (val, where, sizeof (where)) +#define UID_TO_CHARS(val, where) uid_to_chars (val, where, sizeof (where)) +#define UINTMAX_TO_CHARS(val, where) uintmax_to_chars (val, where, sizeof (where)) +#define UNAME_TO_CHARS(name,buf) string_to_chars (name, buf, sizeof(buf)) +#define GNAME_TO_CHARS(name,buf) string_to_chars (name, buf, sizeof(buf)) static bool to_chars (int negative, uintmax_t value, size_t valsize, @@ -263,7 +271,7 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize, 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]; @@ -368,25 +376,25 @@ gid_substitute (int *negative) return r; } -bool +static bool gid_to_chars (gid_t v, char *p, size_t s) { return to_chars (v < 0, (uintmax_t) v, sizeof v, gid_substitute, p, s, "gid_t"); } -bool +static bool major_to_chars (major_t v, char *p, size_t s) { return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "major_t"); } -bool +static bool minor_to_chars (minor_t v, char *p, size_t s) { return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "minor_t"); } -bool +static bool mode_to_chars (mode_t v, char *p, size_t s) { /* In the common case where the internal and external mode bits are the same, @@ -432,12 +440,6 @@ off_to_chars (off_t v, char *p, size_t s) return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "off_t"); } -bool -size_to_chars (size_t v, char *p, size_t s) -{ - return to_chars (0, (uintmax_t) v, sizeof v, 0, p, s, "size_t"); -} - bool time_to_chars (time_t v, char *p, size_t s) { @@ -460,19 +462,19 @@ uid_substitute (int *negative) return r; } -bool +static bool uid_to_chars (uid_t v, char *p, size_t s) { return to_chars (v < 0, (uintmax_t) v, sizeof v, uid_substitute, p, s, "uid_t"); } -bool +static bool uintmax_to_chars (uintmax_t v, char *p, size_t s) { return to_chars (0, v, sizeof v, 0, p, s, "uintmax_t"); } -void +static void string_to_chars (char const *str, char *p, size_t s) { tar_copy_str (p, str, s); @@ -487,7 +489,7 @@ string_to_chars (char const *str, char *p, size_t s) a) it is empty *and* world-readable, or b) current archive is /dev/null */ -bool +static bool file_dumpable_p (struct tar_stat_info *st) { if (dev_null_output) @@ -515,9 +517,8 @@ write_eot (void) /* Write a "private" header */ union block * -start_private_header (const char *name, size_t size) +start_private_header (const char *name, size_t size, time_t t) { - time_t t; union block *header = find_next_block (); memset (header->buffer, 0, sizeof (union block)); @@ -525,7 +526,6 @@ start_private_header (const char *name, size_t size) tar_name_copy_str (header->header.name, name, NAME_FIELD_SIZE); OFF_TO_CHARS (size, header->header.size); - time (&t); TIME_TO_CHARS (t, header->header.mtime); MODE_TO_CHARS (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, header->header.mode); UID_TO_CHARS (getuid (), header->header.uid); @@ -563,13 +563,13 @@ write_gnu_long_link (struct tar_stat_info *st, const char *p, char type) 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); + header = start_private_header ("././@LongLink", size, time (NULL)); + 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); @@ -604,8 +604,10 @@ split_long_name (const char *name, size_t length) { size_t i; - if (length > PREFIX_FIELD_SIZE) + if (length > PREFIX_FIELD_SIZE + 1) length = PREFIX_FIELD_SIZE + 1; + else if (ISSLASH (name[length - 1])) + length--; for (i = length - 1; i > 0; i--) if (ISSLASH (name[i])) break; @@ -616,7 +618,7 @@ static union block * write_ustar_long_name (const char *name) { size_t length = strlen (name); - size_t i; + size_t i, nlen; union block *header; if (length > PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1) @@ -628,7 +630,7 @@ write_ustar_long_name (const char *name) } i = split_long_name (name, length); - if (i == 0 || length - i - 1 > NAME_FIELD_SIZE) + if (i == 0 || (nlen = length - i - 1) > NAME_FIELD_SIZE || nlen == 0) { ERROR ((0, 0, _("%s: file name is too long (cannot be split); not dumped"), @@ -712,6 +714,7 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header) union block *header, hp; char *p; int type; + time_t t; if (st->xhdr.buffer || st->xhdr.stk == NULL) return old_header; @@ -722,13 +725,15 @@ write_extended (bool global, struct tar_stat_info *st, union block *old_header) { type = XGLTYPE; p = xheader_ghdr_name (); + time (&t); } else { type = XHDTYPE; p = xheader_xhdr_name (st); + t = st->stat.st_mtime; } - xheader_write (type, p, &st->xhdr); + xheader_write (type, p, t, &st->xhdr); free (p); header = find_next_block (); memcpy (header, &hp.buffer, sizeof (hp.buffer)); @@ -993,11 +998,9 @@ finish_header (struct tar_stat_info *st, && header->header.typeflag != XHDTYPE && header->header.typeflag != XGLTYPE) { - /* These globals are parameters to print_header, sigh. */ - - current_header = header; + /* FIXME: This global is used in print_header, sigh. */ current_format = archive_format; - print_header (st, block_ordinal); + print_header (st, header, block_ordinal); } header = write_extended (false, st, header); @@ -1011,7 +1014,6 @@ pad_archive (off_t size_left) union block *blk; while (size_left > 0) { - mv_size_left (size_left); blk = find_next_block (); memset (blk->buffer, 0, BLOCKSIZE); set_next_block_after (blk); @@ -1037,12 +1039,10 @@ dump_regular_file (int fd, struct tar_stat_info *st) finish_header (st, blk, block_ordinal); - mv_begin (st); + mv_begin_write (st->file_name, st->stat.st_size, st->stat.st_size); while (size_left > 0) { size_t bufsize, count; - - mv_size_left (size_left); blk = find_next_block (); @@ -1079,7 +1079,7 @@ 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) + if (! ignore_failed_read_option) set_exit_status (TAREXIT_DIFFERS); pad_archive (size_left - (bufsize - count)); return dump_status_short; @@ -1095,73 +1095,67 @@ dump_dir0 (char *directory, { 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; - off_t block_ordinal = current_block_ordinal (); - st->stat.st_size = 0; /* force 0 size on dir */ + union block *blk = NULL; + off_t block_ordinal = current_block_ordinal (); - blk = start_header (st); - if (!blk) - return; + st->stat.st_size = 0; /* force 0 size on dir */ + + blk = start_header (st); + if (!blk) + return; - if (incremental_option && archive_format != POSIX_FORMAT) - blk->header.typeflag = GNUTYPE_DUMPDIR; - else /* if (standard_option) */ - blk->header.typeflag = DIRTYPE; + if (incremental_option && archive_format != POSIX_FORMAT) + blk->header.typeflag = GNUTYPE_DUMPDIR; + else /* if (standard_option) */ + blk->header.typeflag = DIRTYPE; - /* If we're gnudumping, we aren't done yet so don't close it. */ + /* If we're gnudumping, we aren't done yet so don't close it. */ - if (!incremental_option) - finish_header (st, blk, block_ordinal); - else if (gnu_list_name->directory) + if (!incremental_option) + finish_header (st, blk, block_ordinal); + else if (gnu_list_name->directory) + { + if (archive_format == POSIX_FORMAT) { - if (archive_format == POSIX_FORMAT) - { - xheader_store ("GNU.dumpdir", st, - safe_directory_contents (gnu_list_name->directory)); - finish_header (st, blk, block_ordinal); - } - else + xheader_store ("GNU.dumpdir", st, + safe_directory_contents (gnu_list_name->directory)); + finish_header (st, blk, block_ordinal); + } + else + { + off_t size_left; + off_t totsize; + size_t bufsize; + ssize_t count; + const char *buffer, *p_buffer; + + block_ordinal = current_block_ordinal (); + buffer = safe_directory_contents (gnu_list_name->directory); + totsize = dumpdir_size (buffer); + OFF_TO_CHARS (totsize, blk->header.size); + finish_header (st, blk, block_ordinal); + p_buffer = buffer; + size_left = totsize; + + mv_begin_write (st->file_name, totsize, totsize); + while (size_left > 0) { - off_t size_left; - off_t totsize; - size_t bufsize; - ssize_t count; - const char *buffer, *p_buffer; - - block_ordinal = current_block_ordinal (); - buffer = safe_directory_contents (gnu_list_name->directory); - totsize = dumpdir_size (buffer); - OFF_TO_CHARS (totsize, blk->header.size); - finish_header (st, blk, block_ordinal); - p_buffer = buffer; - size_left = totsize; - - mv_begin (st); - mv_total_size (totsize); - while (size_left > 0) + blk = find_next_block (); + bufsize = available_space_after (blk); + if (size_left < bufsize) { - mv_size_left (size_left); - blk = find_next_block (); - bufsize = available_space_after (blk); - if (size_left < bufsize) - { - bufsize = size_left; - count = bufsize % BLOCKSIZE; - if (count) - memset (blk->buffer + size_left, 0, BLOCKSIZE - count); - } - memcpy (blk->buffer, p_buffer, bufsize); - size_left -= bufsize; - p_buffer += bufsize; - set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE); + bufsize = size_left; + count = bufsize % BLOCKSIZE; + if (count) + memset (blk->buffer + size_left, 0, BLOCKSIZE - count); } - mv_end (); + memcpy (blk->buffer, p_buffer, bufsize); + size_left -= bufsize; + p_buffer += bufsize; + set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE); } - return; } + return; } if (!recursion_option) @@ -1181,13 +1175,13 @@ dump_dir0 (char *directory, { 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; @@ -1211,7 +1205,7 @@ dump_dir0 (char *directory, if (!excluded_name (name_buf)) dump_file (name_buf, false, our_device); } - + free (name_buf); } break; @@ -1226,7 +1220,7 @@ dump_dir0 (char *directory, dump_file (name_buf, false, our_device); free (name_buf); break; - + case exclusion_tag_under: exclusion_tag_warning (st->orig_file_name, tag_file_name, _("contents not dumped")); @@ -1265,6 +1259,12 @@ dump_dir (int fd, struct tar_stat_info *st, bool top_level, return true; } + +/* Number of links a file can have without having to be entered into + the link table. Typically this is 1, but in trickier circumstances + it is 0. */ +static nlink_t trivial_link_count; + /* Main functions of this module. */ @@ -1273,6 +1273,8 @@ create_archive (void) { struct name const *p; + trivial_link_count = name_count <= 1 && ! dereference_option; + open_archive (ACCESS_WRITE); buffer_write_global_xheader (); @@ -1380,7 +1382,8 @@ static Hash_table *link_table; static bool dump_hard_link (struct tar_stat_info *st) { - if (link_table && (st->stat.st_nlink > 1 || remove_files_option)) + if (link_table + && (trivial_link_count < st->stat.st_nlink || remove_files_option)) { struct link lp; struct link *duplicate; @@ -1427,7 +1430,7 @@ file_count_links (struct tar_stat_info *st) { if (hard_dereference_option) return; - if (st->stat.st_nlink > 1) + if (trivial_link_count < st->stat.st_nlink) { struct link *duplicate; char *linkname = NULL; @@ -1435,7 +1438,7 @@ file_count_links (struct tar_stat_info *st) assign_string (&linkname, st->orig_file_name); transform_name (&linkname, XFORM_LINK); - + lp = xmalloc (offsetof (struct link, name) + strlen (linkname) + 1); lp->ino = st->stat.st_ino; @@ -1443,13 +1446,13 @@ file_count_links (struct tar_stat_info *st) lp->nlink = st->stat.st_nlink; strcpy (lp->name, linkname); free (linkname); - + if (! ((link_table || (link_table = hash_initialize (0, 0, hash_link, compare_links, 0))) && (duplicate = hash_insert (link_table, lp)))) xalloc_die (); - + if (duplicate != lp) abort (); lp->nlink--; @@ -1531,10 +1534,10 @@ dump_file0 (struct tar_stat_info *st, const char *p, /* See if we want only new files, and check if this one is too old to 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 (!(incremental_option && !is_individual_file (p)) && !S_ISDIR (st->stat.st_mode) && OLDER_TAR_STAT_TIME (*st, m) @@ -1556,9 +1559,6 @@ dump_file0 (struct tar_stat_info *st, const char *p, return; } - if (is_avoided_name (p)) - return; - is_dir = S_ISDIR (st->stat.st_mode) != 0; if (!is_dir && dump_hard_link (st)) @@ -1596,9 +1596,11 @@ dump_file0 (struct tar_stat_info *st, const char *p, { exclusion_tag_warning (st->orig_file_name, tag_file_name, _("directory not dumped")); + if (fd >= 0) + close (fd); return; } - + ok = dump_dir (fd, st, top_level, parent_device); /* dump_dir consumes FD if successful. */ @@ -1622,7 +1624,6 @@ 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;