X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fcreate.c;h=087cacd678e04cacec6107a634a0fc39a6888e57;hb=ae00dc0d37baef4504408c9ce03997781b735d41;hp=c5a5d712ad4603f6933c7bbeff976a620e8b9ab4;hpb=3209329337c87a102fa12d7eb60a3df323ba56c1;p=chaz%2Ftar diff --git a/src/create.c b/src/create.c index c5a5d71..087cacd 100644 --- a/src/create.c +++ b/src/create.c @@ -22,7 +22,6 @@ #include #include -#include #include "common.h" #include @@ -576,19 +575,29 @@ write_long_name (struct tar_stat_info *st) return write_short_name (st); } -static union block * -write_extended (struct tar_stat_info *st, union block *old_header) +union block * +write_extended (bool global, struct tar_stat_info *st, union block *old_header) { union block *header, hp; char *p; + int type; if (extended_header.buffer || extended_header.stk == NULL) return old_header; xheader_finish (&extended_header); memcpy (hp.buffer, old_header, sizeof (hp)); - p = xheader_xhdr_name (st); - xheader_write (XHDTYPE, p, &extended_header); + if (global) + { + type = XGLTYPE; + p = xheader_ghdr_name (); + } + else + { + type = XHDTYPE; + p = xheader_xhdr_name (st); + } + xheader_write (type, p, &extended_header); free (p); header = find_next_block (); memcpy (header, &hp.buffer, sizeof (hp.buffer)); @@ -852,7 +861,7 @@ finish_header (struct tar_stat_info *st, print_header (st, block_ordinal); } - header = write_extended (st, header); + header = write_extended (false, st, header); simple_finish_header (header); } @@ -863,7 +872,7 @@ pad_archive (off_t size_left) union block *blk; while (size_left > 0) { - save_sizeleft = size_left; + mv_size_left (size_left); blk = find_next_block (); memset (blk->buffer, 0, BLOCKSIZE); set_next_block_after (blk); @@ -889,16 +898,13 @@ dump_regular_file (int fd, struct tar_stat_info *st) finish_header (st, blk, block_ordinal); + mv_begin (st); while (size_left > 0) { size_t bufsize, count; - if (multi_volume_option) - { - assign_string (&save_name, st->orig_file_name); - save_sizeleft = size_left; - save_totsize = st->stat.st_size; - } + mv_size_left (size_left); + blk = find_next_block (); bufsize = available_space_after (blk); @@ -943,35 +949,6 @@ dump_regular_file (int fd, struct tar_stat_info *st) return dump_status_ok; } -static void -dump_regular_finish (int fd, struct tar_stat_info *st, - struct timespec original_ctime) -{ - if (fd >= 0) - { - struct stat final_stat; - if (fstat (fd, &final_stat) != 0) - { - stat_diag (st->orig_file_name); - } - else if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime) - != 0) - { - WARN ((0, 0, _("%s: file changed as we read it"), - quotearg_colon (st->orig_file_name))); - } - if (close (fd) != 0) - { - close_diag (st->orig_file_name); - } - } - if (remove_files_option) - { - if (unlink (st->orig_file_name) == -1) - unlink_error (st->orig_file_name); - } -} - /* Look in directory DIRNAME for a cache directory tag file with the magic name "CACHEDIR.TAG" and a standard header, as described at: @@ -1029,7 +1006,7 @@ dump_dir0 (char *directory, if (!blk) return; - if (incremental_option) + if (incremental_option && archive_format != POSIX_FORMAT) blk->header.typeflag = GNUTYPE_DUMPDIR; else /* if (standard_option) */ blk->header.typeflag = DIRTYPE; @@ -1040,51 +1017,51 @@ dump_dir0 (char *directory, finish_header (st, blk, block_ordinal); else if (gnu_list_name->dir_contents) { - off_t size_left; - off_t totsize; - size_t bufsize; - ssize_t count; - const char *buffer, *p_buffer; - - block_ordinal = current_block_ordinal (); - buffer = gnu_list_name->dir_contents; /* FOO */ - totsize = 0; - if (buffer) - for (p_buffer = buffer; *p_buffer; ) - { - size_t size = strlen (p_buffer) + 1; - totsize += size; - p_buffer += size; - } - totsize++; - OFF_TO_CHARS (totsize, blk->header.size); - finish_header (st, blk, block_ordinal); - p_buffer = buffer; - size_left = totsize; - while (size_left > 0) + if (archive_format == POSIX_FORMAT) { - if (multi_volume_option) - { - assign_string (&save_name, st->orig_file_name); - save_sizeleft = size_left; - save_totsize = totsize; - } - blk = find_next_block (); - bufsize = available_space_after (blk); - if (size_left < bufsize) + xheader_store ("GNU.dumpdir", st, gnu_list_name->dir_contents); + 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 = gnu_list_name->dir_contents; + if (buffer) + totsize = dumpdir_size (buffer); + else + totsize = 0; + 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) { - bufsize = size_left; - count = bufsize % BLOCKSIZE; - if (count) - memset (blk->buffer + size_left, 0, BLOCKSIZE - count); + 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); } - memcpy (blk->buffer, p_buffer, bufsize); - size_left -= bufsize; - p_buffer += bufsize; - set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE); + mv_end (); } - if (multi_volume_option) - assign_string (&save_name, 0); return; } } @@ -1154,11 +1131,9 @@ ensure_slash (char **pstr) } static bool -dump_dir (struct tar_stat_info *st, int top_level, dev_t parent_device) +dump_dir (int fd, struct tar_stat_info *st, int top_level, dev_t parent_device) { - char *directory; - - directory = savedir (st->orig_file_name); + char *directory = fdsavedir (fd); if (!directory) { savedir_diag (st->orig_file_name); @@ -1266,7 +1241,7 @@ compare_links (void const *entry1, void const *entry2) } static void -unknown_file_error (char *p) +unknown_file_error (char const *p) { WARN ((0, 0, _("%s: Unknown file type; file ignored"), quotearg_colon (p))); @@ -1282,8 +1257,8 @@ unknown_file_error (char *p) again if we've done it once already. */ static Hash_table *link_table; -/* Try to dump stat as a hard link to another file in the archive. If - succeeded returns true */ +/* Try to dump stat as a hard link to another file in the archive. + Return true if successful. */ static bool dump_hard_link (struct tar_stat_info *st) { @@ -1385,7 +1360,7 @@ check_links (void) exit_status to failure, a clear diagnostic has been issued. */ static void -dump_file0 (struct tar_stat_info *st, char *p, +dump_file0 (struct tar_stat_info *st, char const *p, int top_level, dev_t parent_device) { union block *header; @@ -1393,6 +1368,7 @@ dump_file0 (struct tar_stat_info *st, char *p, struct timespec original_ctime; struct timespec restore_times[2]; off_t block_ordinal = -1; + bool is_dir; if (interactive_option && !confirm ("add", p)) return; @@ -1451,43 +1427,48 @@ dump_file0 (struct tar_stat_info *st, char *p, if (is_avoided_name (p)) return; - if (S_ISDIR (st->stat.st_mode)) - { - dump_dir (st, top_level, parent_device); - if (atime_preserve_option) - utimens (p, restore_times); - return; - } - else - { - /* Check for multiple links. */ - if (dump_hard_link (st)) - return; - /* This is not a link to a previously dumped file, so dump it. */ + is_dir = S_ISDIR (st->stat.st_mode) != 0; - if (S_ISREG (st->stat.st_mode) - || S_ISCTG (st->stat.st_mode)) - { - int fd; - enum dump_status status; + if (!is_dir && dump_hard_link (st)) + return; + + 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 (file_dumpable_p (st)) + 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) { - fd = open (st->orig_file_name, - O_RDONLY | O_BINARY); - if (fd < 0) - { - if (!top_level && errno == ENOENT) - WARN ((0, 0, _("%s: File removed before we read it"), - quotearg_colon (st->orig_file_name))); - else - open_diag (st->orig_file_name); - return; - } + if (!top_level && errno == ENOENT) + WARN ((0, 0, _("%s: File removed before we read it"), + quotearg_colon (p))); + else + open_diag (p); + return; } - else + } + + if (is_dir) + { + ok = dump_dir (fd, st, top_level, parent_device); + + /* dump_dir consumes FD if successful. */ + if (ok) fd = -1; + } + else + { + enum dump_status status; if (fd != -1 && sparse_option && sparse_file_p (st)) { @@ -1501,90 +1482,130 @@ dump_file0 (struct tar_stat_info *st, char *p, switch (status) { case dump_status_ok: - if (multi_volume_option) - assign_string (&save_name, 0); - dump_regular_finish (fd, st, original_ctime); - break; - case dump_status_short: - if (multi_volume_option) - assign_string (&save_name, 0); - close (fd); + mv_end (); break; case dump_status_fail: - close (fd); - return; + break; case dump_status_not_implemented: abort (); } - if (atime_preserve_option) - utimens (st->orig_file_name, restore_times); - file_count_links (st); - return; + ok = status == dump_status_ok; } -#ifdef HAVE_READLINK - else if (S_ISLNK (st->stat.st_mode)) + + if (ok) { - char *buffer; - int size; - size_t linklen = st->stat.st_size; - if (linklen != st->stat.st_size || linklen + 1 == 0) - xalloc_die (); - buffer = (char *) alloca (linklen + 1); - size = readlink (p, buffer, linklen + 1); - if (size < 0) + /* 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) + : fstat (fd, &final_stat)) + != 0) { - readlink_diag (p); - return; + stat_diag (p); + ok = false; } - buffer[size] = '\0'; - assign_string (&st->link_name, buffer); - if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size) - write_long_link (st); + } - block_ordinal = current_block_ordinal (); - st->stat.st_size = 0; /* force 0 size on symlink */ - header = start_header (st); - if (!header) - return; - tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE); - header->header.typeflag = SYMTYPE; - finish_header (st, header, block_ordinal); - /* nothing more to do to it */ - - if (remove_files_option) + 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))); + else if (atime_preserve_option == replace_atime_preserve + && set_file_atime (fd, p, restore_times) != 0) + utime_error (p); + } + + if (0 <= fd && close (fd) != 0) + { + close_diag (p); + ok = false; + } + + if (ok && remove_files_option) + { + if (is_dir) { - if (unlink (p) == -1) + if (rmdir (p) != 0 && errno != ENOTEMPTY) + rmdir_error (p); + } + else + { + if (unlink (p) != 0) unlink_error (p); } - file_count_links (st); - return; } -#endif - else if (S_ISCHR (st->stat.st_mode)) - type = CHRTYPE; - else if (S_ISBLK (st->stat.st_mode)) - type = BLKTYPE; - else if (S_ISFIFO (st->stat.st_mode)) - type = FIFOTYPE; - else if (S_ISSOCK (st->stat.st_mode)) - { - WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p))); - return; - } - else if (S_ISDOOR (st->stat.st_mode)) + + return; + } +#ifdef HAVE_READLINK + else if (S_ISLNK (st->stat.st_mode)) + { + char *buffer; + int size; + size_t linklen = st->stat.st_size; + if (linklen != st->stat.st_size || linklen + 1 == 0) + xalloc_die (); + buffer = (char *) alloca (linklen + 1); + size = readlink (p, buffer, linklen + 1); + if (size < 0) { - WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p))); + readlink_diag (p); return; } - else + buffer[size] = '\0'; + assign_string (&st->link_name, buffer); + if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size) + write_long_link (st); + + block_ordinal = current_block_ordinal (); + st->stat.st_size = 0; /* force 0 size on symlink */ + header = start_header (st); + if (!header) + return; + tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE); + header->header.typeflag = SYMTYPE; + finish_header (st, header, block_ordinal); + /* nothing more to do to it */ + + if (remove_files_option) { - unknown_file_error (p); - return; + if (unlink (p) == -1) + unlink_error (p); } + file_count_links (st); + return; + } +#endif + else if (S_ISCHR (st->stat.st_mode)) + type = CHRTYPE; + else if (S_ISBLK (st->stat.st_mode)) + type = BLKTYPE; + else if (S_ISFIFO (st->stat.st_mode)) + type = FIFOTYPE; + else if (S_ISSOCK (st->stat.st_mode)) + { + WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p))); + return; + } + else if (S_ISDOOR (st->stat.st_mode)) + { + WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p))); + return; + } + else + { + unknown_file_error (p); + return; } if (archive_format == V7_FORMAT) @@ -1622,5 +1643,7 @@ dump_file (char *p, int top_level, dev_t parent_device) 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); tar_stat_destroy (&st); }