X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fcreate.c;h=e137325484e83efd8dd819722e64bda87e1e04fc;hb=de328a580ab6f5ff4a3237ce21f1ef0b7dd12984;hp=79c80ce08282cc65d9bde8ef25b0811e7bac64d7;hpb=2947023d277cb0a787c73721d6190a75444cd65f;p=chaz%2Ftar diff --git a/src/create.c b/src/create.c index 79c80ce..e137325 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]; }; @@ -39,7 +39,7 @@ struct exclusion_tag const char *name; size_t length; enum exclusion_tag_type type; - bool (*predicate) (const char *name); + bool (*predicate) (int fd); struct exclusion_tag *next; }; @@ -47,7 +47,7 @@ static struct exclusion_tag *exclusion_tags; void add_exclusion_tag (const char *name, enum exclusion_tag_type type, - bool (*predicate) (const char *name)) + bool (*predicate) (int fd)) { struct exclusion_tag *tag = xmalloc (sizeof tag[0]); tag->next = exclusion_tags; @@ -71,39 +71,24 @@ exclusion_tag_warning (const char *dirname, const char *tagname, message)); } -enum exclusion_tag_type -check_exclusion_tags (const char *dirname, const char **tag_file_name) +enum exclusion_tag_type +check_exclusion_tags (int fd, char const **tag_file_name) { - static char *tagname; - static size_t tagsize; struct exclusion_tag *tag; - size_t dlen = strlen (dirname); - int addslash = !ISSLASH (dirname[dlen-1]); - char *nptr = NULL; - + for (tag = exclusion_tags; tag; tag = tag->next) { - size_t size = dlen + addslash + tag->length + 1; - if (size > tagsize) - { - tagsize = size; - tagname = xrealloc (tagname, tagsize); - } - - if (!nptr) + int tagfd = openat (fd, tag->name, open_read_flags); + if (0 <= tagfd) { - strcpy (tagname, dirname); - nptr = tagname + dlen; - if (addslash) - *nptr++ = '/'; - } - 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; + bool satisfied = !tag->predicate || tag->predicate (tagfd); + close (tagfd); + if (satisfied) + { + if (tag_file_name) + *tag_file_name = tag->name; + return tag->type; + } } } @@ -121,22 +106,13 @@ check_exclusion_tags (const char *dirname, const char **tag_file_name) #define CACHEDIR_SIGNATURE_SIZE (sizeof CACHEDIR_SIGNATURE - 1) bool -cachedir_file_p (const char *name) +cachedir_file_p (int fd) { - bool tag_present = false; - int fd = open (name, O_RDONLY); - if (fd >= 0) - { - static char tagbuf[CACHEDIR_SIGNATURE_SIZE]; + 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; + return + (read (fd, tagbuf, CACHEDIR_SIGNATURE_SIZE) == CACHEDIR_SIGNATURE_SIZE + && memcmp (tagbuf, CACHEDIR_SIGNATURE, CACHEDIR_SIGNATURE_SIZE) == 0); } @@ -214,6 +190,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 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 +247,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 +352,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 +416,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 +438,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); @@ -480,20 +458,25 @@ string_to_chars (char const *str, char *p, size_t s) } -/* A file is considered dumpable if it is sparse and both --sparse and --totals +/* A directory is always considered dumpable. + Otherwise, only regular and contiguous files are considered dumpable. + Such a file is dumpable if it is sparse and both --sparse and --totals are specified. Otherwise, it is dumpable unless any of the following conditions occur: a) it is empty *and* world-readable, or b) current archive is /dev/null */ -bool -file_dumpable_p (struct tar_stat_info *st) +static bool +file_dumpable_p (struct stat const *st) { + if (S_ISDIR (st->st_mode)) + return true; + if (! (S_ISREG (st->st_mode) || S_ISCTG (st->st_mode))) + return false; if (dev_null_output) - return totals_option && sparse_option && ST_IS_SPARSE (st->stat); - return !(st->archive_file_size == 0 - && (st->stat.st_mode & MODE_R) == MODE_R); + return totals_option && sparse_option && ST_IS_SPARSE (*st); + return ! (st->st_size == 0 && (st->st_mode & MODE_R) == MODE_R); } @@ -515,9 +498,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 +507,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 +544,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 +585,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 +599,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 +611,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 +695,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 +706,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 +979,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 +995,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 +1020,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 +1060,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; @@ -1089,79 +1070,75 @@ dump_regular_file (int fd, struct tar_stat_info *st) } +/* Copy info from the directory identified by ST into the archive. + DIRECTORY contains the directory's entries. */ + static void -dump_dir0 (char *directory, - struct tar_stat_info *st, bool top_level, dev_t parent_device) +dump_dir0 (struct tar_stat_info *st, char const *directory) { - dev_t our_device = st->stat.st_dev; + bool top_level = ! st->parent; 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 */ - if (incremental_option && archive_format != POSIX_FORMAT) - blk->header.typeflag = GNUTYPE_DUMPDIR; - else /* if (standard_option) */ - blk->header.typeflag = DIRTYPE; + blk = start_header (st); + if (!blk) + return; - /* If we're gnudumping, we aren't done yet so don't close it. */ + if (incremental_option && archive_format != POSIX_FORMAT) + blk->header.typeflag = GNUTYPE_DUMPDIR; + else /* if (standard_option) */ + blk->header.typeflag = DIRTYPE; - if (!incremental_option) - finish_header (st, blk, block_ordinal); - else if (gnu_list_name->directory) + /* 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 (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) @@ -1169,7 +1146,7 @@ dump_dir0 (char *directory, if (one_file_system_option && !top_level - && parent_device != st->stat.st_dev) + && st->parent->stat.st_dev != st->stat.st_dev) { if (verbose_option) WARNOPT (WARN_XDEV, @@ -1181,13 +1158,13 @@ dump_dir0 (char *directory, { char *name_buf; size_t name_size; - - switch (check_exclusion_tags (st->orig_file_name, &tag_file_name)) + + switch (check_exclusion_tags (st->fd, &tag_file_name)) { case exclusion_tag_all: /* Handled in dump_file0 */ break; - + case exclusion_tag_none: { char const *entry; @@ -1198,7 +1175,6 @@ dump_dir0 (char *directory, name_size = name_len = strlen (name_buf); /* 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) { @@ -1209,9 +1185,9 @@ dump_dir0 (char *directory, } strcpy (name_buf + name_len, entry); if (!excluded_name (name_buf)) - dump_file (name_buf, false, our_device); + dump_file (st, entry, name_buf); } - + free (name_buf); } break; @@ -1223,10 +1199,10 @@ dump_dir0 (char *directory, name_buf = xmalloc (name_size); strcpy (name_buf, st->orig_file_name); strcat (name_buf, tag_file_name); - dump_file (name_buf, false, our_device); + dump_file (st, tag_file_name, name_buf); free (name_buf); break; - + case exclusion_tag_under: exclusion_tag_warning (st->orig_file_name, tag_file_name, _("contents not dumped")); @@ -1249,22 +1225,38 @@ ensure_slash (char **pstr) } static bool -dump_dir (int fd, struct tar_stat_info *st, bool top_level, - dev_t parent_device) +dump_dir (struct tar_stat_info *st) { - char *directory = fdsavedir (fd); - if (!directory) + char *directory = 0; + int dupfd = dup (st->fd); + if (0 <= dupfd) + { + directory = fdsavedir (dupfd); + if (! directory) + { + int e = errno; + close (dupfd); + errno = e; + } + } + if (! directory) { savedir_diag (st->orig_file_name); return false; } - dump_dir0 (directory, st, top_level, parent_device); + dump_dir0 (st, directory); free (directory); 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 +1265,8 @@ create_archive (void) { struct name const *p; + trivial_link_count = name_count <= 1 && ! dereference_option; + open_archive (ACCESS_WRITE); buffer_write_global_xheader (); @@ -1286,12 +1280,13 @@ create_archive (void) while ((p = name_from_list ()) != NULL) if (!excluded_name (p->name)) - dump_file (p->name, p->cmdline, (dev_t) 0); + dump_file (0, p->name, p->name); blank_name_list (); while ((p = name_from_list ()) != NULL) if (!excluded_name (p->name)) { + struct tar_stat_info st; size_t plen = strlen (p->name); if (buffer_size <= plen) { @@ -1302,6 +1297,7 @@ create_archive (void) memcpy (buffer, p->name, plen); if (! ISSLASH (buffer[plen - 1])) buffer[plen++] = DIRECTORY_SEPARATOR; + tar_stat_init (&st); q = directory_contents (gnu_list_name->directory); if (q) while (*q) @@ -1309,6 +1305,24 @@ create_archive (void) size_t qlen = strlen (q); if (*q == 'Y') { + if (! st.orig_file_name) + { + st.orig_file_name = xstrdup (p->name); + st.fd = open (st.orig_file_name, + ((open_read_flags - O_RDONLY + + O_SEARCH) + | O_DIRECTORY)); + if (st.fd < 0) + { + open_diag (p->name); + break; + } + if (fstat (st.fd, &st.stat) != 0) + { + stat_diag (p->name); + break; + } + } if (buffer_size < plen + qlen) { while ((buffer_size *=2 ) < plen + qlen) @@ -1316,10 +1330,11 @@ create_archive (void) buffer = xrealloc (buffer, buffer_size); } strcpy (buffer + plen, q + 1); - dump_file (buffer, false, (dev_t) 0); + dump_file (&st, q + 1, buffer); } q += qlen + 1; } + tar_stat_destroy (&st); } free (buffer); } @@ -1328,12 +1343,12 @@ create_archive (void) const char *name; while ((name = name_next (1)) != NULL) if (!excluded_name (name)) - dump_file (name, true, (dev_t) 0); + dump_file (0, name, name); } write_eot (); close_archive (); - + finish_deferred_unlinks (); if (listed_incremental_option) write_directory_file (); } @@ -1380,7 +1395,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; @@ -1413,8 +1429,8 @@ dump_hard_link (struct tar_stat_info *st) blk->header.typeflag = LNKTYPE; finish_header (st, blk, block_ordinal); - if (remove_files_option && unlink (st->orig_file_name) != 0) - unlink_error (st->orig_file_name); + if (remove_files_option) + queue_deferred_unlink (st->orig_file_name, false); return true; } @@ -1427,7 +1443,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 +1451,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 +1459,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--; @@ -1476,18 +1492,15 @@ check_links (void) } } -/* Dump a single file, recursing on directories. P is the file name - to dump. TOP_LEVEL tells whether this is a top-level call; zero - means no, positive means yes, and negative means the top level - of an incremental dump. PARENT_DEVICE is the device of P's - parent directory; it is examined only if TOP_LEVEL is zero. */ +/* Dump a single file, recursing on directories. ST is the file's + status info, NAME its name relative to the parent directory, and P + its full name (which may be relative to the working directory). */ /* FIXME: One should make sure that for *every* path leading to setting exit_status to failure, a clear diagnostic has been issued. */ static void -dump_file0 (struct tar_stat_info *st, const char *p, - bool top_level, dev_t parent_device) +dump_file0 (struct tar_stat_info *st, char const *name, char const *p) { union block *header; char type; @@ -1495,7 +1508,11 @@ dump_file0 (struct tar_stat_info *st, const char *p, struct timespec original_ctime; struct timespec restore_times[2]; off_t block_ordinal = -1; + int fd = -1; bool is_dir; + bool top_level = ! st->parent; + int parentfd = top_level ? AT_FDCWD : st->parent->fd; + void (*diag) (char const *) = 0; if (interactive_option && !confirm ("add", p)) return; @@ -1506,11 +1523,22 @@ dump_file0 (struct tar_stat_info *st, const char *p, transform_name (&st->file_name, XFORM_REGFILE); - if (deref_stat (dereference_option, p, &st->stat) != 0) + if (fstatat (parentfd, name, &st->stat, fstatat_flags) != 0) + diag = stat_diag; + else if (file_dumpable_p (&st->stat)) + { + fd = st->fd = openat (parentfd, name, open_read_flags); + if (fd < 0) + diag = open_diag; + else if (fstat (fd, &st->stat) != 0) + diag = stat_diag; + } + if (diag) { - file_removed_diag (p, top_level, stat_diag); + file_removed_diag (p, top_level, diag); return; } + 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); @@ -1531,10 +1559,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 +1584,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)) @@ -1567,43 +1592,22 @@ dump_file0 (struct tar_stat_info *st, const char *p, if (is_dir || S_ISREG (st->stat.st_mode) || S_ISCTG (st->stat.st_mode)) { bool ok; - int fd = -1; struct stat final_stat; - if (is_dir || file_dumpable_p (st)) - { - fd = open (p, - (O_RDONLY | O_BINARY - | (is_dir ? O_DIRECTORY | O_NONBLOCK : 0) - | (atime_preserve_option == system_atime_preserve - ? O_NOATIME - : 0))); - if (fd < 0) - { - file_removed_diag (p, top_level, open_diag); - return; - } - } - 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) + if (check_exclusion_tags (fd, &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. */ - if (ok) - fd = -1; + ok = dump_dir (st); } else { @@ -1622,7 +1626,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; @@ -1638,15 +1641,8 @@ dump_file0 (struct tar_stat_info *st, const char *p, if (ok) { - /* If possible, reopen a directory if we are preserving - atimes, so that we can set just the atime on systems with - _FIOSATIME. */ - if (fd < 0 && is_dir - && atime_preserve_option == replace_atime_preserve) - fd = open (p, O_RDONLY | O_BINARY | O_DIRECTORY | O_NONBLOCK); - if ((fd < 0 - ? deref_stat (dereference_option, p, &final_stat) + ? fstatat (parentfd, name, &final_stat, fstatat_flags) : fstat (fd, &final_stat)) != 0) { @@ -1673,26 +1669,19 @@ dump_file0 (struct tar_stat_info *st, const char *p, utime_error (p); } - if (0 <= fd && close (fd) != 0) - { - close_diag (p); - ok = false; - } - - if (ok && remove_files_option) + if (0 < fd) { - if (is_dir) + if (close (fd) != 0) { - if (rmdir (p) != 0 && errno != ENOTEMPTY) - rmdir_error (p); - } - else - { - if (unlink (p) != 0) - unlink_error (p); + close_diag (p); + ok = false; } + st->fd = 0; } + if (ok && remove_files_option) + queue_deferred_unlink (p, is_dir); + return; } #ifdef HAVE_READLINK @@ -1704,7 +1693,7 @@ dump_file0 (struct tar_stat_info *st, const char *p, if (linklen != st->stat.st_size || linklen + 1 == 0) xalloc_die (); buffer = (char *) alloca (linklen + 1); - size = readlink (p, buffer, linklen + 1); + size = readlinkat (parentfd, name, buffer, linklen + 1); if (size < 0) { file_removed_diag (p, top_level, readlink_diag); @@ -1727,10 +1716,8 @@ dump_file0 (struct tar_stat_info *st, const char *p, /* nothing more to do to it */ if (remove_files_option) - { - if (unlink (p) == -1) - unlink_error (p); - } + queue_deferred_unlink (p, false); + file_count_links (st); return; } @@ -1782,19 +1769,23 @@ dump_file0 (struct tar_stat_info *st, const char *p, finish_header (st, header, block_ordinal); if (remove_files_option) - { - if (unlink (p) == -1) - unlink_error (p); - } + queue_deferred_unlink (p, false); } +/* Dump a file, recursively. PARENT describes the file's parent + directory, NAME is the file's name relative to PARENT, and FULLNAME + its full name, possibly relative to the working directory. NAME + may contain slashes at the top level of invocation. */ + void -dump_file (const char *p, bool top_level, dev_t parent_device) +dump_file (struct tar_stat_info *parent, char const *name, + char const *fullname) { struct tar_stat_info st; tar_stat_init (&st); - dump_file0 (&st, p, top_level, parent_device); - if (listed_incremental_option) - update_parent_directory (p); + st.parent = parent; + dump_file0 (&st, name, fullname); + if (parent && listed_incremental_option) + update_parent_directory (parent); tar_stat_destroy (&st); }