diff --git a/doc/tar.texi b/doc/tar.texi index d70d113..a0e3d5f 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -1881,6 +1881,7 @@ The other operations of @command{tar} (@option{--list}, @option{--extract}, @option{--compare}, and @option{--update}) will act on the entire contents of the archive. +@anchor{exit status} @cindex exit status @cindex return status Besides successful exits, @GNUTAR{} may fail for @@ -2815,7 +2816,10 @@ when extracting files from an archive. @item --keep-old-files @itemx -k -Do not overwrite existing files when extracting files from an archive. +Do not overwrite existing files when extracting files from an +archive. Return error if such files exist. See also +@ref{--skip-old-files}. + @xref{Keep Old Files}. @opsummary{label} @@ -3268,6 +3272,20 @@ the archive creation operations it instructs @command{tar} to list the member names stored in the archive, as opposed to the actual file names. @xref{listing member and file names}. +@opsummary{skip-old-files} +@item --skip-old-files + +Do not overwrite existing files when extracting files from an +archive. @xref{Keep Old Files}. + +This option differs from @option{--keep-old-files} in that it does not +treat such files as an error, instead it just silently avoids +overwriting them. + +The @option{--warning=existing-file} option can be used together with +this option to produce warning messages about existing old files +(@pxref{warnings}). + @opsummary{sparse} @item --sparse @itemx -S @@ -4443,11 +4461,11 @@ in the archive; the most recently archived members will be extracted last. Additionally, an extracted member will @emph{replace} a file of the same name which existed in the directory already, and @command{tar} will not prompt you about this@footnote{Unless you give it -@option{--keep-old-files} option, or the disk copy is newer than -the one in the archive and you invoke @command{tar} with -@option{--keep-newer-files} option.}. Thus, only the most recently archived -member will end up being extracted, as it will replace the one -extracted before it, and so on. +@option{--keep-old-files} (or @option{--skip-old-files}) option, or +the disk copy is newer than the one in the archive and you invoke +@command{tar} with @option{--keep-newer-files} option.}. Thus, only +the most recently archived member will end up being extracted, as it +will replace the one extracted before it, and so on. @cindex extracting @var{n}th copy of the file @xopindex{occurrence, described} @@ -5123,10 +5141,25 @@ such a directory, use the @option{--no-overwrite-dir} option. @cindex Overwriting old files, prevention @xopindex{keep-old-files, introduced} To be even more cautious and prevent existing files from being replaced, use -the @option{--keep-old-files} (@option{-k}) option. It causes @command{tar} to refuse -to replace or update a file that already exists, i.e., a file with the -same name as an archive member prevents extraction of that archive -member. Instead, it reports an error. +the @option{--keep-old-files} (@option{-k}) option. It causes +@command{tar} to refuse to replace or update a file that already +exists, i.e., a file with the same name as an archive member prevents +extraction of that archive member. Instead, it reports an error. For +example: + +@example +$ @kbd{ls} +blues +$ @kbd{tar -x -k -f archive.tar} +tar: blues: Cannot open: File exists +tar: Exiting with failure status due to previous errors +@end example + +@xopindex{skip-old-files, introduced} +If you wish to preserve old files untouched, but don't want +@command{tar} to treat them as errors, use the +@option{--skip-old-files} option. This option causes @command{tar} to +silently skip extracting over existing files. @xopindex{overwrite, introduced} To be more aggressive about altering existing files, use the @@ -5192,16 +5225,24 @@ archive, but remove other files before extracting. @node Keep Old Files @unnumberedsubsubsec Keep Old Files +@GNUTAR{} provides two options to control its actions in a situation +when it is about to extract a file which already exists on disk. + @table @option @opindex keep-old-files @item --keep-old-files @itemx -k -Do not replace existing files from archive. The -@option{--keep-old-files} (@option{-k}) option prevents @command{tar} -from replacing existing files with files with the same name from the -archive. The @option{--keep-old-files} option is meaningless with -@option{--list} (@option{-t}). Prevents @command{tar} from replacing -files in the file system during extraction. +Do not replace existing files from archive. When such a file is +encountered, @command{tar} issues an error message. Upon end of +extraction, @command{tar} exits with code 2 (@pxref{exit status}). + +@item --skip-old-files +Do not replace existing files from archive, but do not treat that +as error. Such files are silently skipped and do not affect +@command{tar} exit status. + +Additional verbosity can be obtained using @option{--warning=existing-file} +together with that option (@pxref{warnings}). @end table @node Keep Newer Files diff --git a/src/common.h b/src/common.h index 0b9bd7a..2409413 100644 --- a/src/common.h +++ b/src/common.h @@ -182,6 +182,7 @@ enum old_files OVERWRITE_OLD_FILES, /* --overwrite */ UNLINK_FIRST_OLD_FILES, /* --unlink-first */ KEEP_OLD_FILES, /* --keep-old-files */ + SKIP_OLD_FILES, /* --skip-old-files */ KEEP_NEWER_FILES /* --keep-newer-files */ }; GLOBAL enum old_files old_files_option; @@ -807,11 +808,12 @@ void checkpoint_run (bool do_write); #define WARN_UNKNOWN_KEYWORD 0x00020000 #define WARN_XDEV 0x00040000 #define WARN_DECOMPRESS_PROGRAM 0x00080000 +#define WARN_EXISTING_FILE 0x00100000 /* The warnings composing WARN_VERBOSE_WARNINGS are enabled by default in verbose mode */ #define WARN_VERBOSE_WARNINGS (WARN_RENAME_DIRECTORY|WARN_NEW_DIRECTORY|\ - WARN_DECOMPRESS_PROGRAM) + WARN_DECOMPRESS_PROGRAM|WARN_EXISTING_FILE) #define WARN_ALL (~WARN_VERBOSE_WARNINGS) void set_warning_option (const char *arg); diff --git a/src/extract.c b/src/extract.c index aaea56e..662ea0b 100644 --- a/src/extract.c +++ b/src/extract.c @@ -639,9 +639,14 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made) switch (old_files_option) { - case KEEP_OLD_FILES: + case SKIP_OLD_FILES: + WARNOPT (WARN_EXISTING_FILE, + (0, 0, _("%s: skipping existing file"), file_name)); return RECOVER_SKIP; + case KEEP_OLD_FILES: + return RECOVER_NO; + case KEEP_NEWER_FILES: if (file_newer_p (file_name, stp, ¤t_stat_info)) break; diff --git a/src/tar.c b/src/tar.c index 7b62996..7a673e0 100644 --- a/src/tar.c +++ b/src/tar.c @@ -328,6 +328,7 @@ enum SHOW_DEFAULTS_OPTION, SHOW_OMITTED_DIRS_OPTION, SHOW_TRANSFORMED_NAMES_OPTION, + SKIP_OLD_FILES_OPTION, SPARSE_VERSION_OPTION, STRIP_COMPONENTS_OPTION, SUFFIX_OPTION, @@ -452,7 +453,11 @@ static struct argp_option options[] = { {"remove-files", REMOVE_FILES_OPTION, 0, 0, N_("remove files after adding them to the archive"), GRID+1 }, {"keep-old-files", 'k', 0, 0, - N_("don't replace existing files when extracting"), GRID+1 }, + N_("don't replace existing files when extracting, " + "treat them as errors"), GRID+1 }, + {"skip-old-files", SKIP_OLD_FILES_OPTION, 0, 0, + N_("don't replace existing files when extracting, silently skip over them"), + GRID+1 }, {"keep-newer-files", KEEP_NEWER_FILES_OPTION, 0, 0, N_("don't replace existing files that are newer than their archive copies"), GRID+1 }, {"overwrite", OVERWRITE_OPTION, 0, 0, @@ -1618,6 +1623,10 @@ parse_opt (int key, char *arg, struct argp_state *state) sparse_option = true; break; + case SKIP_OLD_FILES_OPTION: + old_files_option = SKIP_OLD_FILES; + break; + case SPARSE_VERSION_OPTION: sparse_option = true; { diff --git a/src/warning.c b/src/warning.c index 5d1bcab..ee9d684 100644 --- a/src/warning.c +++ b/src/warning.c @@ -42,6 +42,7 @@ static char const *const warning_args[] = { "unknown-keyword", "xdev", "decompress-program", + "existing-file", NULL }; @@ -66,7 +67,8 @@ static int warning_types[] = { WARN_UNKNOWN_CAST, WARN_UNKNOWN_KEYWORD, WARN_XDEV, - WARN_DECOMPRESS_PROGRAM + WARN_DECOMPRESS_PROGRAM, + WARN_EXISTING_FILE, }; ARGMATCH_VERIFY (warning_args, warning_types); diff --git a/tests/Makefile.am b/tests/Makefile.am index 119f1f3..3d78ea2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -77,6 +77,8 @@ TESTSUITE_AT = \ extrac07.at\ extrac08.at\ extrac09.at\ + extrac18.at\ + extrac19.at\ extrac10.at\ extrac11.at\ extrac12.at\ diff --git a/tests/extrac18.at b/tests/extrac18.at new file mode 100644 index 0000000..8b42ef7 --- /dev/null +++ b/tests/extrac18.at @@ -0,0 +1,60 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# +# Test suite for GNU tar. +# Copyright (C) 2011 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Description: Check the functionality of the --keep-old-files option. +# It should report an error and cause tar to exit with status 2. +# +# There was a regression in versions 1.23 to 1.26 inclusive, where +# this option silently skipped such files. +# Reported by: Doug McLaren , +# Gary Partis , +# Jim Meyering +# +# References: <20111117045433.GA8245@algol.frenzied.us>, +# <4F3D824717847C4487F77228F83329A3514CBB@server.Partis.local>, +# <87wrar6zzz.fsf@rho.meyering.net> + +AT_SETUP([keep-old-files]) +AT_KEYWORDS([extract extrac18 old-files keep-old-files]) + +AT_TAR_CHECK([ +mkdir dir +cd dir +echo 'Old file a' > a +echo 'Old file b' > b + +tar cf ../archive . + +rm b +echo 'File a' > a + +tar -x -k -f ../archive +echo status=$? + +cat a +], +[0], +[status=2 +File a +], +[tar: ./a: Cannot open: File exists +tar: Exiting with failure status due to previous errors +]) + +AT_CLEANUP + diff --git a/tests/extrac19.at b/tests/extrac19.at new file mode 100644 index 0000000..43c4c50 --- /dev/null +++ b/tests/extrac19.at @@ -0,0 +1,44 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# +# Test suite for GNU tar. +# Copyright (C) 2011 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +AT_SETUP([skip-old-files]) +AT_KEYWORDS([extract extrac19 old-files skip-old-files]) + +AT_TAR_CHECK([ +mkdir dir +cd dir +echo 'Old file a' > a +echo 'Old file b' > b + +tar cf ../archive . + +rm b +echo 'File a' > a + +tar -x --skip-old-files -f ../archive +echo status=$? + +cat a +], +[0], +[status=0 +File a +]) + +AT_CLEANUP + diff --git a/tests/testsuite.at b/tests/testsuite.at index d1dab36..e43653e 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -166,6 +166,9 @@ m4_include([extrac15.at]) m4_include([extrac16.at]) m4_include([extrac17.at]) +m4_include([extrac18.at]) +m4_include([extrac19.at]) + m4_include([label01.at]) m4_include([label02.at]) m4_include([label03.at])