📄 copy.c
字号:
close_src_and_dst_desc: if (close (dest_desc) < 0) { error (0, errno, _("closing %s"), quote (dst_path)); return_val = -1; }close_src_desc: if (close (source_desc) < 0) { error (0, errno, _("closing %s"), quote (src_path)); return_val = -1; } return return_val;}/* Return nonzero if it's ok that the source and destination files are the `same' by some measure. The goal is to avoid making the `copy' operation remove both copies of the file in that case, while still allowing the user to e.g., move or copy a regular file onto a symlink that points to it. Try to minimize the cost of this function in the common case. */static intsame_file_ok (const char *src_path, const struct stat *src_sb, const char *dst_path, const struct stat *dst_sb, const struct cp_options *x, int *return_now){ const struct stat *src_sb_link; const struct stat *dst_sb_link; struct stat tmp_dst_sb; struct stat tmp_src_sb; int same_link; int same = (SAME_INODE (*src_sb, *dst_sb)); *return_now = 0; /* FIXME: this should (at the very least) be moved into the following if-block. More likely, it should be removed, because it inhibits making backups. But removing it will result in a change in behavior that will probably have to be documented -- and tests will have to be updated. */ if (same && x->hard_link) { *return_now = 1; return 1; } if (x->xstat == lstat) { same_link = same; /* If both the source and destination files are symlinks (and we'll know this here IFF preserving symlinks (aka xstat == lstat), then it's ok -- as long as they are distinct. */ if (S_ISLNK (src_sb->st_mode) && S_ISLNK (dst_sb->st_mode)) return ! same_name (src_path, dst_path); src_sb_link = src_sb; dst_sb_link = dst_sb; } else { if (!same) return 1; if (lstat (dst_path, &tmp_dst_sb) || lstat (src_path, &tmp_src_sb)) return 1; src_sb_link = &tmp_src_sb; dst_sb_link = &tmp_dst_sb; same_link = SAME_INODE (*src_sb_link, *dst_sb_link); /* If both are symlinks, then it's ok, but only if the destination will be unlinked before being opened. This is like the test above, but with the addition of the unlink_dest_before_opening conjunct because otherwise, with two symlinks to the same target, we'd end up truncating the source file. */ if (S_ISLNK (src_sb_link->st_mode) && S_ISLNK (dst_sb_link->st_mode) && x->unlink_dest_before_opening) return 1; } /* The backup code ensures there's a copy, so it's usually ok to remove any destination file. One exception is when both source and destination are the same directory entry. In that case, moving the destination file aside (in making the backup) would also rename the source file and result in an error. */ if (x->backup_type != none) { if (!same_link) { /* In copy mode when dereferencing symlinks, if the source is a symlink and the dest is not, then backing up the destination (moving it aside) would make it a dangling symlink, and the subsequent attempt to open it in copy_reg would fail with a misleading diagnostic. Avoid that by returning zero in that case so the caller can make cp (or mv when it has to resort to reading the source file) fail now. */ /* FIXME-note: even with the following kludge, we can still provoke the offending diagnostic. It's just a little harder to do :-) $ rm -f a b c; touch c; ln -s c b; ln -s b a; cp -b a b cp: cannot open `a' for reading: No such file or directory That's misleading, since a subsequent `ls' shows that `a' is still there. One solution would be to open the source file *before* moving aside the destination, but that'd involve a big rewrite. */ if ( ! x->move_mode && x->dereference != DEREF_NEVER && S_ISLNK (src_sb_link->st_mode) && ! S_ISLNK (dst_sb_link->st_mode)) return 0; return 1; } return ! same_name (src_path, dst_path); }#if 0 /* FIXME: use or remove */ /* If we're making a backup, we'll detect the problem case in copy_reg because SRC_PATH will no longer exist. Allowing the test to be deferred lets cp do some useful things. But when creating hardlinks and SRC_PATH is a symlink but DST_PATH is not we must test anyway. */ if (x->hard_link || !S_ISLNK (src_sb_link->st_mode) || S_ISLNK (dst_sb_link->st_mode)) return 1; if (x->dereference != DEREF_NEVER) return 1;#endif /* They may refer to the same file if we're in move mode and the target is a symlink. That is ok, since we remove any existing destination file before opening it -- via `rename' if they're on the same file system, via `unlink (DST_PATH)' otherwise. It's also ok if they're distinct hard links to the same file. */ if ((x->move_mode || x->unlink_dest_before_opening) && (S_ISLNK (dst_sb_link->st_mode) || (same_link && !same_name (src_path, dst_path)))) return 1; /* If neither is a symlink, then it's ok as long as they aren't hard links to the same file. */ if (!S_ISLNK (src_sb_link->st_mode) && !S_ISLNK (dst_sb_link->st_mode)) { if (!SAME_INODE (*src_sb_link, *dst_sb_link)) return 1; /* If they are the same file, it's ok if we're making hard links. */ if (x->hard_link) { *return_now = 1; return 1; } } /* It's ok to remove a destination symlink. But that works only when we unlink before opening the destination and when the source and destination files are on the same partition. */ if (x->unlink_dest_before_opening && S_ISLNK (dst_sb_link->st_mode)) return dst_sb_link->st_dev == src_sb_link->st_dev; if (x->xstat == lstat) { if ( ! S_ISLNK (src_sb_link->st_mode)) tmp_src_sb = *src_sb_link; else if (stat (src_path, &tmp_src_sb)) return 1; if ( ! S_ISLNK (dst_sb_link->st_mode)) tmp_dst_sb = *dst_sb_link; else if (stat (dst_path, &tmp_dst_sb)) return 1; if ( ! SAME_INODE (tmp_src_sb, tmp_dst_sb)) return 1; /* FIXME: shouldn't this be testing whether we're making symlinks? */ if (x->hard_link) { *return_now = 1; return 1; } } return 0;}static voidoverwrite_prompt (char const *dst_path, struct stat const *dst_sb){ if (euidaccess (dst_path, W_OK) != 0) { fprintf (stderr, _("%s: overwrite %s, overriding mode %04lo? "), program_name, quote (dst_path), (unsigned long) (dst_sb->st_mode & CHMOD_MODE_BITS)); } else { fprintf (stderr, _("%s: overwrite %s? "), program_name, quote (dst_path)); }}/* Hash an F_triple. */static unsigned inttriple_hash (void const *x, unsigned int table_size){ struct F_triple const *p = x; /* Also take the name into account, so that when moving N hard links to the same file (all listed on the command line) all into the same directory, we don't experience any N^2 behavior. */ /* FIXME-maybe: is it worth the overhead of doing this just to avoid N^2 in such an unusual case? N would have to be very large to make the N^2 factor noticable, and one would probably encounter a limit on the length of a command line before it became a problem. */ unsigned int tmp = hash_pjw (p->name, table_size); /* Ignoring the device number here should be fine. */ return (tmp | p->st_ino) % table_size;}/* Hash an F_triple. */static unsigned inttriple_hash_no_name (void const *x, unsigned int table_size){ struct F_triple const *p = x; /* Ignoring the device number here should be fine. */ return p->st_ino % table_size;}/* Compare two F_triple structs. */static booltriple_compare (void const *x, void const *y){ struct F_triple const *a = x; struct F_triple const *b = y; return (SAME_INODE (*a, *b) && same_name (a->name, b->name)) ? true : false;}/* Free an F_triple. */static voidtriple_free (void *x){ struct F_triple *a = x; free ((char *) (a->name)); free (a);}/* Initialize the hash table implementing a set of F_triple entries corresponding to destination files. */voiddest_info_init (struct cp_options *x){ x->dest_info = hash_initialize (DEST_INFO_INITIAL_CAPACITY, NULL, triple_hash, triple_compare, triple_free);}/* Initialize the hash table implementing a set of F_triple entries corresponding to source files listed on the command line. */voidsrc_info_init (struct cp_options *x){ /* Note that we use triple_hash_no_name here. Contrast with the use of triple_hash above. That is necessary because a source file may be specified in many different ways. We want to warn about this cp a a d/ as well as this: cp a ./a d/ */ x->src_info = hash_initialize (DEST_INFO_INITIAL_CAPACITY, NULL, triple_hash_no_name, triple_compare, triple_free);}/* Return nonzero if there is an entry in hash table, HT, for the file described by FILENAME and STATS. Otherwise, return zero. */static intseen_file (Hash_table const *ht, char const *filename, struct stat const *stats){ struct F_triple new_ent; if (ht == NULL) return 0; new_ent.name = filename; new_ent.st_ino = stats->st_ino; new_ent.st_dev = stats->st_dev; return !!hash_lookup (ht, &new_ent);}/* Record destination filename, FILENAME, and dev/ino from *STATS, in the hash table, HT. If HT is NULL, return immediately. If STATS is NULL, call lstat on FILENAME to get the device and inode numbers. If that lstat fails, simply return. If memory allocation fails, exit immediately. */static voidrecord_file (Hash_table *ht, char const *filename, struct stat const *stats){ struct F_triple *ent; if (ht == NULL) return; ent = (struct F_triple *) xmalloc (sizeof *ent); ent->name = xstrdup (filename); if (stats) { ent->st_ino = stats->st_ino; ent->st_dev = stats->st_dev; } else { struct stat sb; if (lstat (filename, &sb)) return; ent->st_ino = sb.st_ino; ent->st_dev = sb.st_dev; } { struct F_triple *ent_from_table = hash_insert (ht, ent); if (ent_from_table == NULL) { /* Insertion failed due to lack of memory. */ xalloc_die (); } if (ent_from_table != ent) { /* There was alread a matching entry in the table, so ENT was not inserted. Free it. */ triple_free (ent); } }}/* Copy the file SRC_PATH to the file DST_PATH. The files may be of any type. NEW_DST should be nonzero if the file DST_PATH cannot exist because its parent directory was just created; NEW_DST should be zero if DST_PATH might already exist. DEVICE is the device number of the parent directory, or 0 if the parent of this file is not known. ANCESTORS points to a linked, null terminated list of devices and inodes of parent directories of SRC_PATH. COMMAND_LINE_ARG is nonzero iff SRC_PATH was specified on the command line. Set *COPY_INTO_SELF to nonzero if SRC_PATH is a parent of (or the same as) DST_PATH; otherwise, set it to zero. Return 0 if successful, 1 if an error occurs. */static intcopy_internal (const char *src_path, const char *dst_path, int new_dst, dev_t device, struct dir_list *ancestors, const struct cp_options *x, int command_line_arg, int *copy_into_self, int *rename_succeeded){ struct stat src_sb; struct stat dst_sb; mode_t src_mode; mode_t src_type; char *earlier_file = NULL; char *dst_backup = NULL; int backup_succeeded = 0; int delayed_fail; int copied_as_regular = 0; int ran_chown = 0; int preserve_metadata; /* move_mode is set to the value from the `options' parameter for the first copy_internal call. For any subsequent recursive call, it must be zero. This is because if we're moving (via mv) a hierarchy and end up having to recurse, it means the initial rename failed and so we are in the process of *copy*ing all of the parts, not renaming them. */ int move_mode = (command_line_arg ? x->move_mode : 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -