mv would leave such symlinks behind in the source file system.
[the bug dates back to the initial implementation]
+ When creating numbered backups, cp, install, ln, and mv now avoid
+ races that could lose backup data in unlikely circumstances. Since
+ the fix relies on the renameat2 system call of Linux kernel 3.15 and
+ later, the races are still present on other platforms.
+ [the bug dates back to the initial implementation]
+
+ cp, install, ln, and mv now use a numbered instead of a simple
+ backup if copying a backup file to what might be its original.
+ E.g., 'rm -f a a~; : > a; echo data > a~; cp --backup=simple a~ ./a'
+ now makes a numbered backup file instead of losing the data.
+ [the bug dates back to the initial implementation]
+
date and touch no longer overwrite the heap with large
user specified TZ values (CVE-2017-7476).
[bug introduced in coreutils-8.27]
bool restore_dst_mode = false;
char *earlier_file = NULL;
char *dst_backup = NULL;
- bool backup_succeeded = false;
bool delayed_ok;
bool copied_as_regular = false;
bool dest_is_symlink = false;
}
}
+ char const *srcbase;
if (x->backup_type != no_backups
/* Don't try to back up a destination if the last
component of src_name is "." or "..". */
- && ! dot_or_dotdot (last_component (src_name))
+ && ! dot_or_dotdot (srcbase = last_component (src_name))
/* Create a backup of each destination directory in move mode,
but not in copy mode. FIXME: it might make sense to add an
option to suppress backup creation also for move mode.
existing hierarchy. */
&& (x->move_mode || ! S_ISDIR (dst_sb.st_mode)))
{
- char *tmp_backup = find_backup_file_name (dst_name,
- x->backup_type);
-
- /* Detect (and fail) when creating the backup file would
- destroy the source file. Before, running the commands
+ /* Silently use numbered backups if creating the backup
+ file might destroy the source file. Otherwise, the commands:
cd /tmp; rm -f a a~; : > a; echo A > a~; cp --b=simple a~ a
would leave two zero-length files: a and a~. */
- /* FIXME: but simply change e.g., the final a~ to './a~'
- and the source will still be destroyed. */
- if (STREQ (tmp_backup, src_name))
+ enum backup_type backup_type = x->backup_type;
+ if (backup_type != numbered_backups)
{
- const char *fmt;
- fmt = (x->move_mode
- ? _("backing up %s would destroy source; %s not moved")
- : _("backing up %s would destroy source; %s not copied"));
- error (0, 0, fmt,
- quoteaf_n (0, dst_name),
- quoteaf_n (1, src_name));
- free (tmp_backup);
- return false;
+ size_t srcbaselen = strlen (srcbase);
+ char const *dstbase = last_component (dst_name);
+ size_t dstbaselen = strlen (dstbase);
+ if (srcbaselen == dstbaselen + strlen (simple_backup_suffix)
+ && memcmp (srcbase, dstbase, dstbaselen) == 0
+ && STREQ (srcbase + dstbaselen, simple_backup_suffix))
+ backup_type = numbered_backups;
}
+ char *tmp_backup = backup_file_rename (dst_name, backup_type);
+
/* FIXME: use fts:
Using alloca for a file name that may be arbitrarily
long is not recommended. In fact, even forming such a name
should be discouraged. Eventually, this code will be rewritten
to use fts, so using alloca here will be less of a problem. */
- ASSIGN_STRDUPA (dst_backup, tmp_backup);
- free (tmp_backup);
- /* In move mode, when src_name and dst_name are on the
- same partition (FIXME, and when they are non-directories),
- make the operation atomic: link dest
- to backup, then rename src to dest. */
- if (rename (dst_name, dst_backup) != 0)
+ if (tmp_backup)
{
- if (errno != ENOENT)
- {
- error (0, errno, _("cannot backup %s"),
- quoteaf (dst_name));
- return false;
- }
- else
- {
- dst_backup = NULL;
- }
+ ASSIGN_STRDUPA (dst_backup, tmp_backup);
+ free (tmp_backup);
}
- else
+ else if (errno != ENOENT)
{
- backup_succeeded = true;
+ error (0, errno, _("cannot backup %s"), quoteaf (dst_name));
+ return false;
}
new_dst = true;
}
sure we'll create a directory. Also don't announce yet when moving
so we can distinguish renames versus copies. */
if (x->verbose && !x->move_mode && !S_ISDIR (src_mode))
- emit_verbose (src_name, dst_name, backup_succeeded ? dst_backup : NULL);
+ emit_verbose (src_name, dst_name, dst_backup);
/* Associate the destination file name with the source device and inode
so that if we encounter a matching dev/ino pair in the source tree
if (x->verbose)
{
printf (_("renamed "));
- emit_verbose (src_name, dst_name,
- backup_succeeded ? dst_backup : NULL);
+ emit_verbose (src_name, dst_name, dst_backup);
}
if (x->set_security_context)
if (x->verbose && !S_ISDIR (src_mode))
{
printf (_("copied "));
- emit_verbose (src_name, dst_name,
- backup_succeeded ? dst_backup : NULL);
+ emit_verbose (src_name, dst_name, dst_backup);
}
new_dst = true;
}
. "$abs_srcdir/tests/other-fs-tmpdir"
a="$other_partition_tmpdir/a"
-a2="$other_partition_tmpdir/a~"
+a2="$other_partition_tmpdir/./a~"
-rm -f "$a" "$a2" || framework_failure_
+rm -f "$a" "$a2" a20 || framework_failure_
echo a > "$a" || framework_failure_
+cp "$a" a0 || framework_failure_
echo a2 > "$a2" || framework_failure_
+cp "$a2" a20 || framework_failure_
-# This mv command should exit nonzero.
-mv --b=simple "$a2" "$a" > out 2>&1 && fail=1
+# This mv command should not trash the source.
+mv --b=simple "$a2" "$a" > out 2>&1 || fail=1
-sed \
- -e "s,mv:,XXX:," \
- -e "s,$a,YYY," \
- -e "s,$a2,ZZZ," \
- out > out2
-
-cat > exp <<\EOF
-XXX: backing up 'YYY' would destroy source; 'ZZZ' not moved
-EOF
-
-compare exp out2 || fail=1
+compare a20 "$a" || fail=1
+compare a0 "$a.~1~" || fail=1
Exit $fail