📄 copy.c
字号:
if (move_mode && rename_succeeded) *rename_succeeded = 0; *copy_into_self = 0; if ((*(x->xstat)) (src_path, &src_sb)) { error (0, errno, _("cannot stat %s"), quote (src_path)); return 1; } src_type = src_sb.st_mode; src_mode = src_sb.st_mode; if (S_ISDIR (src_type) && !x->recursive) { error (0, 0, _("omitting directory %s"), quote (src_path)); return 1; } /* Detect the case in which the same source file appears more than once on the command line and no backup option has been selected. If so, simply warn and don't copy it the second time. This check is enabled only if x->src_info is non-NULL. */ if (command_line_arg) { if ( ! S_ISDIR (src_sb.st_mode) && x->backup_type == none && seen_file (x->src_info, src_path, &src_sb)) { error (0, 0, _("warning: source file %s specified more than once"), quote (src_path)); return 0; } record_file (x->src_info, src_path, &src_sb); } if (!new_dst) { if ((*(x->xstat)) (dst_path, &dst_sb)) { if (errno != ENOENT) { error (0, errno, _("cannot stat %s"), quote (dst_path)); return 1; } else { new_dst = 1; } } else { int return_now; int ok = same_file_ok (src_path, &src_sb, dst_path, &dst_sb, x, &return_now); if (return_now) return 0; if (! ok) { error (0, 0, _("%s and %s are the same file"), quote_n (0, src_path), quote_n (1, dst_path)); return 1; } if (!S_ISDIR (dst_sb.st_mode)) { if (S_ISDIR (src_type)) { error (0, 0, _("cannot overwrite non-directory %s with directory %s"), quote_n (0, dst_path), quote_n (1, src_path)); return 1; } /* Don't let the user destroy their data, even if they try hard: This mv command must fail (likewise for cp): rm -rf a b c; mkdir a b c; touch a/f b/f; mv a/f b/f c Otherwise, the contents of b/f would be lost. In the case of `cp', b/f would be lost if the user simulated a move using cp and rm. Note that it works fine if you use --backup=numbered. */ if (command_line_arg && x->backup_type != numbered && seen_file (x->dest_info, dst_path, &dst_sb)) { error (0, 0, _("will not overwrite just-created %s with %s"), quote_n (0, dst_path), quote_n (1, src_path)); return 1; } } if (!S_ISDIR (src_type)) { if (S_ISDIR (dst_sb.st_mode)) { error (0, 0, _("cannot overwrite directory %s with non-directory"), quote (dst_path)); return 1; } if (x->update && MTIME_CMP (src_sb, dst_sb) <= 0) { /* We're using --update and the source file is older than the destination file, so there is no need to copy or move. */ /* Pretend the rename succeeded, so the caller (mv) doesn't end up removing the source file. */ if (rename_succeeded) *rename_succeeded = 1; return 0; } } /* When there is an existing destination file, we may end up returning early, and hence not copying/moving the file. This may be due to an interactive `negative' reply to the prompt about the existing file. It may also be due to the use of the --reply=no option. */ if (!S_ISDIR (src_type)) { /* cp and mv treat -i and -f differently. */ if (x->move_mode) { if ((x->interactive == I_ALWAYS_NO && UNWRITABLE (dst_path, dst_sb.st_mode)) || ((x->interactive == I_ASK_USER || (x->interactive == I_UNSPECIFIED && x->stdin_tty && UNWRITABLE (dst_path, dst_sb.st_mode))) && (overwrite_prompt (dst_path, &dst_sb), 1) && ! yesno ())) { /* Pretend the rename succeeded, so the caller (mv) doesn't end up removing the source file. */ if (rename_succeeded) *rename_succeeded = 1; return 0; } } else { if (x->interactive == I_ALWAYS_NO || (x->interactive == I_ASK_USER && (overwrite_prompt (dst_path, &dst_sb), 1) && ! yesno ())) { return 0; } } } if (move_mode) { /* In move_mode, DEST may not be an existing directory. */ if (S_ISDIR (dst_sb.st_mode)) { error (0, 0, _("cannot overwrite directory %s"), quote (dst_path)); return 1; } /* Don't allow user to move a directory onto a non-directory. */ if (S_ISDIR (src_sb.st_mode) && !S_ISDIR (dst_sb.st_mode)) { error (0, 0, _("cannot move directory onto non-directory: %s -> %s"), quote_n (0, src_path), quote_n (0, dst_path)); return 1; } } if (x->backup_type != none && !S_ISDIR (dst_sb.st_mode)) { char *tmp_backup = find_backup_file_name (dst_path, x->backup_type); if (tmp_backup == NULL) xalloc_die (); /* Detect (and fail) when creating the backup file would destroy the source file. Before, running 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_path)) { 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, quote_n (0, dst_path), quote_n (1, src_path)); free (tmp_backup); return 1; } dst_backup = (char *) alloca (strlen (tmp_backup) + 1); strcpy (dst_backup, tmp_backup); free (tmp_backup); if (rename (dst_path, dst_backup)) { if (errno != ENOENT) { error (0, errno, _("cannot backup %s"), quote (dst_path)); return 1; } else { dst_backup = NULL; } } else { backup_succeeded = 1; } new_dst = 1; } else if (! S_ISDIR (dst_sb.st_mode) && (x->unlink_dest_before_opening || (x->xstat == lstat && ! S_ISREG (src_sb.st_mode)))) { if (unlink (dst_path) && errno != ENOENT) { error (0, errno, _("cannot remove %s"), quote (dst_path)); return 1; } new_dst = 1; } } } /* If the source is a directory, we don't always create the destination directory. So --verbose should not announce anything until we're sure we'll create a directory. */ if (x->verbose && !S_ISDIR (src_type)) { printf ("%s -> %s", quote_n (0, src_path), quote_n (1, dst_path)); if (backup_succeeded) printf (_(" (backup: %s)"), quote (dst_backup)); putchar ('\n'); } /* Associate the destination path with the source device and inode so that if we encounter a matching dev/ino pair in the source tree we can arrange to create a hard link between the corresponding names in the destination tree. Sometimes, when preserving links, we have to record dev/ino even though st_nlink == 1: - when using -H and processing a command line argument; that command line argument could be a symlink pointing to another command line argument. With `cp -H --preserve=link', we hard-link those two destination files. - likewise for -L except that it applies to all files, not just command line arguments. Also record directory dev/ino when using --recursive. We'll use that info to detect this problem: cp -R dir dir. FIXME-maybe: ideally, directory info would be recorded in a separate hash table, since such entries are useful only while a single command line hierarchy is being copied -- so that separate table could be cleared between command line args. Using the same hash table to preserve hard links means that it may not be cleared. */ if ((x->preserve_links && (1 < src_sb.st_nlink || (command_line_arg && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS) || x->dereference == DEREF_ALWAYS)) || (x->recursive && S_ISDIR (src_type))) { earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev); } /* Did we copy this inode somewhere else (in this command line argument) and therefore this is a second hard link to the inode? */ if (earlier_file) { /* Avoid damaging the destination filesystem by refusing to preserve hard-linked directories (which are found at least in Netapp snapshot directories). */ if (S_ISDIR (src_type)) { /* If src_path and earlier_file refer to the same directory entry, then warn about copying a directory into itself. */ if (same_name (src_path, earlier_file)) { error (0, 0, _("cannot copy a directory, %s, into itself, %s"), quote_n (0, top_level_src_path), quote_n (1, top_level_dst_path)); *copy_into_self = 1; } else { error (0, 0, _("will not create hard link %s to directory %s"), quote_n (0, dst_path), quote_n (1, earlier_file)); } goto un_backup; } if (link (earlier_file, dst_path)) { error (0, errno, _("cannot create hard link %s to %s"), quote_n (0, dst_path), quote_n (1, earlier_file)); goto un_backup; } return 0; } /* Note that this is testing the local variable move_mode, not the x->move_mode member. */ if (move_mode) { if (rename (src_path, dst_path) == 0) { if (x->verbose && S_ISDIR (src_type)) printf ("%s -> %s\n", quote_n (0, src_path), quote_n (1, dst_path)); if (rename_succeeded) *rename_succeeded = 1; if (command_line_arg) { /* Record destination dev/ino/filename, so that if we are asked to overwrite that file again, we can detect it and fail. */ /* It's fine to use the _source_ stat buffer (src_sb) to get the _destination_ dev/ino, since the rename above can't have changed those, and `mv' always uses lstat. We could limit it further by operating only on non-directories. */ record_file (x->dest_info, dst_path, &src_sb); } return 0; } /* FIXME: someday, consider what to do when moving a directory into itself but when source and destination are on different devices. */ /* This happens when attempting to rename a directory to a subdirectory of itself. */ if (errno == EINVAL /* When src_path is on an NFS file system, some types of clients, e.g., SunOS4.1.4 and IRIX-5.3, set errno to EIO instead. Testing for this here risks misinterpreting a real I/O error as an attempt to move a directory into itself, so FIXME: consider not doing this. */ || errno == EIO /* And with SunOS-4.1.4 client and OpenBSD-2.3 server, we get ENOTEMPTY. */ || errno == ENOTEMPTY) { /* FIXME: this is a little fragile in that it relies on rename(2) failing with a specific errno value. Expect problems on non-POSIX systems. */ error (0, 0, _("cannot move %s to a subdirectory of itself, %s"), quote_n (0, top_level_src_path), quote_n (1, top_level_dst_path)); /* Note that there is no need to call forget_created here, (compare with the other calls in this file) since the destination directory didn't exist before. */ *copy_into_self = 1; /* FIXME-cleanup: Don't return zero here; adjust mv.c accordingly. The only caller that uses this code (mv.c) ends up setting its exit status to nonzero when copy_into_self is nonzero. */ return 0; } /* WARNING: there probably exist systems for which an inter-device rename fails with a value of errno not handled here. If/as those are reported, add them to the condition below. If this happens to you, please do the following and send the output to the bug-reporting address (e.g., in the output of cp --help): touch k; perl -e 'rename "k","/tmp/k" or print "$!(",$!+0,")\n"' where your current directory is on one partion and /tmp is the other. Also, please try to find the E* errno macro name corresponding to the diagnostic and parenthesized integer, and include that in your e-mail. One way to do that is to run a command like this find /usr/include/. -type f \ | xargs grep 'define.*\<E[A-Z]*\>.*\<18\>' /dev/null where you'd replace `18' with the integer in parentheses that was output from the perl one-liner above. If necessary, of course, change `/tmp' to some other directory. */ if (errno != EXDEV) { /* There are many ways this can happen due to a race condition. When something happens between the initial xstat and the subsequent rename, we can get many different types of errors. For example, if the destination is initially a non-directory or non-existent, but it is created as a directory, the rename fails. If two `mv' commands try to rename the same file at about the same time, one will succeed and the other will fail.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -