📄 copy.c
字号:
/* copy.c -- core functions for copying files and directories Copyright (C) 89, 90, 91, 1995-2002 Free Software Foundation. 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 2, 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* Extracted from cp.c and librarified by Jim Meyering. */#ifdef _AIX #pragma alloca#endif#include <config.h>#include <stdio.h>#include <assert.h>#include <sys/types.h>#if HAVE_HURD_H# include <hurd.h>#endif#include "system.h"#include "error.h"#include "backupfile.h"#include "savedir.h"#include "copy.h"#include "cp-hash.h"#include "hash.h"#include "hash-pjw.h"#include "same.h"#include "dirname.h"#include "full-write.h"#include "path-concat.h"#include "quote.h"#include "same.h"#include "xreadlink.h"#define DO_CHOWN(Chown, File, New_uid, New_gid) \ (Chown (File, New_uid, New_gid) \ /* If non-root uses -p, it's ok if we can't preserve ownership. \ But root probably wants to know, e.g. if NFS disallows it, \ or if the target system doesn't support file ownership. */ \ && ((errno != EPERM && errno != EINVAL) || x->myeuid == 0))#define SAME_OWNER(A, B) ((A).st_uid == (B).st_uid)#define SAME_GROUP(A, B) ((A).st_gid == (B).st_gid)#define SAME_OWNER_AND_GROUP(A, B) (SAME_OWNER (A, B) && SAME_GROUP (A, B))#define UNWRITABLE(File_name, File_mode) \ ( /* euidaccess is not meaningful for symlinks */ \ ! S_ISLNK (File_mode) \ && euidaccess (File_name, W_OK) != 0)struct dir_list{ struct dir_list *parent; ino_t ino; dev_t dev;};/* Describe a just-created or just-renamed destination file. */struct F_triple{ char const* name; ino_t st_ino; dev_t st_dev;};/* Initial size of the above hash table. */#define DEST_INFO_INITIAL_CAPACITY 61int euidaccess ();int yesno ();static int copy_internal PARAMS ((const char *src_path, const char *dst_path, int new_dst, dev_t device, struct dir_list *ancestors, const struct cp_options *x, int move_mode, int *copy_into_self, int *rename_succeeded));/* Pointers to the file names: they're used in the diagnostic that is issued when we detect the user is trying to copy a directory into itself. */static char const *top_level_src_path;static char const *top_level_dst_path;/* The invocation name of this program. */extern char *program_name;/* Encapsulate selection of the file mode to be applied to new non-directories. */static mode_tget_dest_mode (const struct cp_options *option, mode_t mode){ /* In some applications (e.g., install), use precisely the specified mode. */ if (option->set_mode) return option->mode; /* Honor the umask for `cp', but not for `mv' or `cp -p'. In addition, `cp' without -p must clear the set-user-ID and set-group-ID bits. POSIX requires it do that when creating new files. */ if (!option->move_mode && !option->preserve_mode) mode &= (option->umask_kill & ~(S_ISUID | S_ISGID)); return mode;}/* FIXME: describe *//* FIXME: rewrite this to use a hash table so we avoid the quadratic performance hit that's probably noticeable only on trees deeper than a few hundred levels. See use of active_dir_map in remove.c */static intis_ancestor (const struct stat *sb, const struct dir_list *ancestors){ while (ancestors != 0) { if (ancestors->ino == sb->st_ino && ancestors->dev == sb->st_dev) return 1; ancestors = ancestors->parent; } return 0;}/* Read the contents of the directory SRC_PATH_IN, and recursively copy the contents to DST_PATH_IN. NEW_DST is nonzero if DST_PATH_IN is a directory that was created previously in the recursion. SRC_SB and ANCESTORS describe SRC_PATH_IN. Set *COPY_INTO_SELF to nonzero if SRC_PATH_IN is a parent of (or the same as) DST_PATH_IN; otherwise, set it to zero. Return 0 if successful, -1 if an error occurs. */static intcopy_dir (const char *src_path_in, const char *dst_path_in, int new_dst, const struct stat *src_sb, struct dir_list *ancestors, const struct cp_options *x, int *copy_into_self){ char *name_space; char *namep; struct cp_options non_command_line_options = *x; int ret = 0; name_space = savedir (src_path_in); if (name_space == NULL) { /* This diagnostic is a bit vague because savedir can fail in several different ways. */ error (0, errno, _("cannot access %s"), quote (src_path_in)); return -1; } /* For cp's -H option, dereference command line arguments, but do not dereference symlinks that are found via recursive traversal. */ if (x->dereference == DEREF_COMMAND_LINE_ARGUMENTS) non_command_line_options.xstat = lstat; namep = name_space; while (*namep != '\0') { int local_copy_into_self; char *src_path = path_concat (src_path_in, namep, NULL); char *dst_path = path_concat (dst_path_in, namep, NULL); if (dst_path == NULL || src_path == NULL) xalloc_die (); ret |= copy_internal (src_path, dst_path, new_dst, src_sb->st_dev, ancestors, &non_command_line_options, 0, &local_copy_into_self, NULL); *copy_into_self |= local_copy_into_self; free (dst_path); free (src_path); namep += strlen (namep) + 1; } free (name_space); return -ret;}/* Copy a regular file from SRC_PATH to DST_PATH. If the source file contains holes, copies holes and blocks of zeros in the source file as holes in the destination file. (Holes are read as zeroes by the `read' system call.) Use DST_MODE as the 3rd argument in the call to open. X provides many option settings. Return 0 if successful, -1 if an error occurred. *NEW_DST is as in copy_internal. SRC_SB is the result of calling xstat (aka stat in this case) on SRC_PATH. */static intcopy_reg (const char *src_path, const char *dst_path, const struct cp_options *x, mode_t dst_mode, int *new_dst, struct stat const *src_sb){ char *buf; int buf_size; int dest_desc; int source_desc; struct stat sb; struct stat src_open_sb; char *cp; int *ip; int return_val = 0; off_t n_read_total = 0; int last_write_made_hole = 0; int make_holes = (x->sparse_mode == SPARSE_ALWAYS); source_desc = open (src_path, O_RDONLY); if (source_desc < 0) { error (0, errno, _("cannot open %s for reading"), quote (src_path)); return -1; } if (fstat (source_desc, &src_open_sb)) { error (0, errno, _("cannot fstat %s"), quote (src_path)); return_val = -1; goto close_src_desc; } /* Compare the source dev/ino from the open file to the incoming, saved ones obtained via a previous call to stat. */ if (! SAME_INODE (*src_sb, src_open_sb)) { error (0, 0, _("skipping file %s, as it was replaced while being copied"), quote (src_path)); return_val = -1; goto close_src_desc; } /* These semantics are required for cp. The if-block will be taken in move_mode. */ if (*new_dst) { dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode); } else { dest_desc = open (dst_path, O_WRONLY | O_TRUNC, dst_mode); if (dest_desc < 0 && x->unlink_dest_after_failed_open) { if (unlink (dst_path)) { error (0, errno, _("cannot remove %s"), quote (dst_path)); return_val = -1; goto close_src_desc; } /* Tell caller that the destination file was unlinked. */ *new_dst = 1; /* Try the open again, but this time with different flags. */ dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode); } } if (dest_desc < 0) { error (0, errno, _("cannot create regular file %s"), quote (dst_path)); return_val = -1; goto close_src_desc; } /* Determine the optimal buffer size. */ if (fstat (dest_desc, &sb)) { error (0, errno, _("cannot fstat %s"), quote (dst_path)); return_val = -1; goto close_src_and_dst_desc; } buf_size = ST_BLKSIZE (sb);#if HAVE_STRUCT_STAT_ST_BLOCKS if (x->sparse_mode == SPARSE_AUTO && S_ISREG (sb.st_mode)) { /* Use a heuristic to determine whether SRC_PATH contains any sparse blocks. */ if (fstat (source_desc, &sb)) { error (0, errno, _("cannot fstat %s"), quote (src_path)); return_val = -1; goto close_src_and_dst_desc; } /* If the file has fewer blocks than would normally be needed for a file of its size, then at least one of the blocks in the file is a hole. */ if (S_ISREG (sb.st_mode) && sb.st_size / ST_NBLOCKSIZE > ST_NBLOCKS (sb)) make_holes = 1; }#endif /* Make a buffer with space for a sentinel at the end. */ buf = (char *) alloca (buf_size + sizeof (int)); for (;;) { ssize_t n_read = read (source_desc, buf, buf_size); if (n_read < 0) {#ifdef EINTR if (errno == EINTR) continue;#endif error (0, errno, _("reading %s"), quote (src_path)); return_val = -1; goto close_src_and_dst_desc; } if (n_read == 0) break; n_read_total += n_read; ip = 0; if (make_holes) { buf[n_read] = 1; /* Sentinel to stop loop. */ /* Find first nonzero *word*, or the word with the sentinel. */ ip = (int *) buf; while (*ip++ == 0) ; /* Find the first nonzero *byte*, or the sentinel. */ cp = (char *) (ip - 1); while (*cp++ == 0) ; /* If we found the sentinel, the whole input block was zero, and we can make a hole. */ if (cp > buf + n_read) { /* Make a hole. */ if (lseek (dest_desc, (off_t) n_read, SEEK_CUR) < 0L) { error (0, errno, _("cannot lseek %s"), quote (dst_path)); return_val = -1; goto close_src_and_dst_desc; } last_write_made_hole = 1; } else /* Clear to indicate that a normal write is needed. */ ip = 0; } if (ip == 0) { size_t n = n_read; if (full_write (dest_desc, buf, n) != n) { error (0, errno, _("writing %s"), quote (dst_path)); return_val = -1; goto close_src_and_dst_desc; } last_write_made_hole = 0; } } /* If the file ends with a `hole', something needs to be written at the end. Otherwise the kernel would truncate the file at the end of the last write operation. */ if (last_write_made_hole) {#if HAVE_FTRUNCATE /* Write a null character and truncate it again. */ if (full_write (dest_desc, "", 1) != 1 || ftruncate (dest_desc, n_read_total) < 0)#else /* Seek backwards one character and write a null. */ if (lseek (dest_desc, (off_t) -1, SEEK_CUR) < 0L || full_write (dest_desc, "", 1) != 1)#endif { error (0, errno, _("writing %s"), quote (dst_path)); return_val = -1; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -