diff options
author | Jim Meyering <jim@meyering.net> | 1997-02-02 18:33:19 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 1997-02-02 18:33:19 +0000 |
commit | 1a375e421b5f587bad67b0ae409a6de07b1af9b8 (patch) | |
tree | 1123648ca89499976bb6106e502bd2f14b6b7b99 /src/cp.c | |
parent | 09e2cc2fdf91f1df19ec5c20f69fb91f419e126d (diff) | |
download | coreutils-1a375e421b5f587bad67b0ae409a6de07b1af9b8.tar.xz |
Gutted code -- pieces are in copy.[ch].
Diffstat (limited to 'src/cp.c')
-rw-r--r-- | src/cp.c | 1442 |
1 files changed, 382 insertions, 1060 deletions
@@ -23,33 +23,26 @@ #include <config.h> #include <stdio.h> +#include <sys/types.h> #define NDEBUG #include <assert.h> #include <getopt.h> -#include "cp.h" +#include "system.h" #include "backupfile.h" #include "argmatch.h" #include "path-concat.h" +#include "cp-hash.h" +#include "copy.h" +#include "error.h" + +#define CP_OPTIONS NULL #ifndef _POSIX_VERSION uid_t geteuid (); #endif -/* On Linux (from slackware-1.2.13 to 2.0.2?) there is no lchown function. - To change ownership of symlinks, you must run chown with an effective - UID of 0. */ -#ifdef __linux__ -# define ROOT_CHOWN_AFFECTS_SYMLINKS -#endif - -#define DO_CHOWN(Chown, File, New_uid, New_gid) \ - (Chown ((File), (myeuid == 0 ? (New_uid) : myeuid), (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. */ \ - && (errno != EPERM || myeuid == 0)) - /* Used by do_copy, make_path_private, and re_protect to keep a list of leading directories whose protections need to be fixed after copying. */ @@ -60,46 +53,15 @@ struct dir_attr struct dir_attr *next; }; -/* Control creation of sparse files (files with holes). */ -enum Sparse_type -{ - /* Never create holes in DEST. */ - SPARSE_NEVER, - - /* This is the default. Use a crude (and sometimes inaccurate) - heuristic to determine if SOURCE has holes. If so, try to create - holes in DEST. */ - SPARSE_AUTO, - - /* For every sufficiently long sequence of bytes in SOURCE, try to - create a corresponding hole in DEST. There is a performance penalty - here because CP has to search for holes in SRC. But if the holes are - big enough, that penalty can be offset by the decrease in the amount - of data written to disk. */ - SPARSE_ALWAYS -}; - int stat (); int lstat (); +char *basename (); char *dirname (); -char *xstrdup (); enum backup_type get_version (); -int euidaccess (); -int full_write (); - -static int do_copy __P ((int argc, char **argv)); -static int copy __P ((const char *src_path, const char *dst_path, int new_dst, - dev_t device, struct dir_list *ancestors)); -static int copy_dir __P ((const char *src_path_in, const char *dst_path_in, - int new_dst, const struct stat *src_sb, - struct dir_list *ancestors)); -static int make_path_private __P ((const char *const_dirpath, int src_offset, - int mode, const char *verbose_fmt_string, - struct dir_attr **attr_list, int *new_dst)); -static int copy_reg __P ((const char *src_path, const char *dst_path)); -static int re_protect __P ((const char *const_dst_path, int src_offset, - struct dir_attr *attr_list)); +void strip_trailing_slashes (); +char *xmalloc (); +char *xstrdup (); /* Initial number of entries in each hash table entry's table of inodes. */ #define INITIAL_HASH_MODULE 100 @@ -110,55 +72,10 @@ static int re_protect __P ((const char *const_dst_path, int src_offset, /* The invocation name of this program. */ char *program_name; -/* A pointer to either lstat or stat, depending on - whether dereferencing of symlinks is done. */ -static int (*xstat) (); - -/* If nonzero, copy all files except (directories and, if not dereferencing - them, symbolic links,) as if they were regular files. */ -static int flag_copy_as_regular = 1; - -/* If nonzero, dereference symbolic links (copy the files they point to). */ -static int flag_dereference = 1; - -/* If nonzero, remove existing destination nondirectories. */ -static int flag_force = 0; - -/* If nonzero, create hard links instead of copying files. - Create destination directories as usual. */ -static int flag_hard_link = 0; - -/* If nonzero, query before overwriting existing destinations - with regular files. */ -static int flag_interactive = 0; - /* If nonzero, the command "cp x/e_file e_dir" uses "e_dir/x/e_file" as its destination instead of the usual "e_dir/e_file." */ static int flag_path = 0; -/* If nonzero, give the copies the original files' permissions, - ownership, and timestamps. */ -static int flag_preserve = 0; - -/* If nonzero, copy directories recursively and copy special files - as themselves rather than copying their contents. */ -static int flag_recursive = 0; - -/* If nonzero, create symbolic links instead of copying files. - Create destination directories as usual. */ -static int flag_symbolic_link = 0; - -/* If nonzero, when copying recursively, skip any subdirectories that are - on different filesystems from the one we started on. */ -static int flag_one_file_system = 0; - -/* If nonzero, do not copy a nondirectory that has an existing destination - with the same or newer modification time. */ -static int flag_update = 0; - -/* If nonzero, display the names of the files before copying them. */ -static int flag_verbose = 0; - static char const *const sparse_type_string[] = { "never", "auto", "always", 0 @@ -169,18 +86,9 @@ static enum Sparse_type const sparse_type[] = SPARSE_NEVER, SPARSE_AUTO, SPARSE_ALWAYS }; -/* Control creation of sparse files. */ -static int flag_sparse = SPARSE_AUTO; - /* The error code to return to the system. */ static int exit_status = 0; -/* The bits to preserve in created files' modes. */ -static int umask_kill; - -/* This process's effective user ID. */ -static uid_t myeuid; - /* If nonzero, display usage information and exit. */ static int show_help; @@ -195,206 +103,291 @@ static struct option const long_opts[] = {"sparse", required_argument, NULL, 2}, {"interactive", no_argument, NULL, 'i'}, {"link", no_argument, NULL, 'l'}, - {"no-dereference", no_argument, &flag_dereference, 0}, - {"one-file-system", no_argument, &flag_one_file_system, 1}, + {"no-dereference", no_argument, NULL, 'd'}, + {"one-file-system", no_argument, NULL, 'x'}, {"parents", no_argument, &flag_path, 1}, {"path", no_argument, &flag_path, 1}, - {"preserve", no_argument, &flag_preserve, 1}, + {"preserve", no_argument, NULL, 'p'}, {"recursive", no_argument, NULL, 'R'}, {"suffix", required_argument, NULL, 'S'}, {"symbolic-link", no_argument, NULL, 's'}, - {"update", no_argument, &flag_update, 1}, - {"verbose", no_argument, &flag_verbose, 1}, + {"update", no_argument, NULL, 'u'}, + {"verbose", no_argument, NULL, 'v'}, {"version-control", required_argument, NULL, 'V'}, {"help", no_argument, &show_help, 1}, {"version", no_argument, &show_version, 1}, {NULL, 0, NULL, 0} }; -static int -is_ancestor (const struct stat *sb, const struct dir_list *ancestors) +static void +usage (int status) { - while (ancestors != 0) + if (status != 0) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else { - if (ancestors->ino == sb->st_ino && ancestors->dev == sb->st_dev) - return 1; - ancestors = ancestors->parent; - } - return 0; + printf (_("\ +Usage: %s [OPTION]... SOURCE DEST\n\ + or: %s [OPTION]... SOURCE... DIRECTORY\n\ +"), + program_name, program_name); + printf (_("\ +Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\ +\n\ + -a, --archive same as -dpR\n\ + -b, --backup make backup before removal\n\ + -d, --no-dereference preserve links\n\ + -f, --force remove existing destinations, never prompt\n\ + -i, --interactive prompt before overwrite\n\ + -l, --link link files instead of copying\n\ + -p, --preserve preserve file attributes if possible\n\ + -P, --parents append source path to DIRECTORY\n\ + -r copy recursively, non-directories as files\n\ + --sparse=WHEN control creation of sparse files\n\ + -R, --recursive copy directories recursively\n\ + -s, --symbolic-link make symbolic links instead of copying\n\ + -S, --suffix=SUFFIX override the usual backup suffix\n\ + -u, --update copy only when the SOURCE file is newer\n\ + than the destination file or when the\n\ + destination file is missing\n\ + -v, --verbose explain what is being done\n\ + -V, --version-control=WORD override the usual version control\n\ + -x, --one-file-system stay on this file system\n\ + --help display this help and exit\n\ + --version output version information and exit\n\ +\n\ +By default, sparse SOURCE files are detected by a crude heuristic and the\n\ +corresponding DEST file is made sparse as well. That is the behavior\n\ +selected by --sparse=auto. Specify --sparse=always to create a sparse DEST\n\ +file whenever the SOURCE file contains a long enough sequence of zero bytes.\n\ +Use --sparse=never to inhibit creation of sparse files.\n\ +\n\ +")); + printf (_("\ +The backup suffix is ~, unless set with SIMPLE_BACKUP_SUFFIX. The\n\ +version control may be set with VERSION_CONTROL, values are:\n\ +\n\ + t, numbered make numbered backups\n\ + nil, existing numbered if numbered backups exist, simple otherwise\n\ + never, simple always make simple backups\n\ +")); + printf (_("\ +\n\ +As a special case, cp makes a backup of SOURCE when the force and backup\n\ +options are given and SOURCE and DEST are the same name for an existing,\n\ +regular file.\n\ +")); + puts (_("\nReport bugs to <fileutils-bugs@gnu.ai.mit.edu>.")); + } + exit (status); } -int -main (int argc, char **argv) -{ - int c; - int make_backups = 0; - char *version; +/* Ensure that the parent directories of CONST_DST_PATH have the + correct protections, for the --parents option. This is done + after all copying has been completed, to allow permissions + that don't include user write/execute. - program_name = argv[0]; - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + SRC_OFFSET is the index in CONST_DST_PATH of the beginning of the + source directory name. - myeuid = geteuid (); + ATTR_LIST is a null-terminated linked list of structures that + indicates the end of the filename of each intermediate directory + in CONST_DST_PATH that may need to have its attributes changed. + The command `cp --parents --preserve a/b/c d/e_dir' changes the + attributes of the directories d/e_dir/a and d/e_dir/a/b to match + the corresponding source directories regardless of whether they + existed before the `cp' command was given. - version = getenv ("SIMPLE_BACKUP_SUFFIX"); - if (version) - simple_backup_suffix = version; - version = getenv ("VERSION_CONTROL"); + Return 0 if the parent of CONST_DST_PATH and any intermediate + directories specified by ATTR_LIST have the proper permissions + when done, otherwise 1. */ - /* Find out the current file creation mask, to knock the right bits - when using chmod. The creation mask is set to to be liberal, so - that created directories can be written, even if it would not - have been allowed with the mask this process was started with. */ +static int +re_protect (const char *const_dst_path, int src_offset, + struct dir_attr *attr_list, const struct cp_options *x) +{ + struct dir_attr *p; + char *dst_path; /* A copy of CONST_DST_PATH we can change. */ + char *src_path; /* The source name in `dst_path'. */ + uid_t myeuid = geteuid (); - umask_kill = 0777777 ^ umask (0); + dst_path = (char *) alloca (strlen (const_dst_path) + 1); + strcpy (dst_path, const_dst_path); + src_path = dst_path + src_offset; - while ((c = getopt_long (argc, argv, "abdfilprsuvxPRS:V:", long_opts, - NULL)) != -1) + for (p = attr_list; p; p = p->next) { - switch (c) + struct stat src_sb; + + dst_path[p->slash_offset] = '\0'; + + if ((*(x->xstat)) (src_path, &src_sb)) { - case 0: - break; + error (0, errno, "%s", src_path); + return 1; + } - case 2: - { - int i; + /* Adjust the times (and if possible, ownership) for the copy. + chown turns off set[ug]id bits for non-root, + so do the chmod last. */ - /* --sparse={never,auto,always} */ - i = argmatch (optarg, sparse_type_string); - if (i < 0) - { - invalid_arg (_("sparse type"), optarg, i); - usage (1); - } - flag_sparse = sparse_type[i]; - } - break; + if (x->preserve) + { + struct utimbuf utb; - case 'a': /* Like -dpR. */ - flag_dereference = 0; - flag_preserve = 1; - flag_recursive = 1; - flag_copy_as_regular = 0; - break; + utb.actime = src_sb.st_atime; + utb.modtime = src_sb.st_mtime; - case 'b': - make_backups = 1; - break; + if (utime (dst_path, &utb)) + { + error (0, errno, "%s", dst_path); + return 1; + } - case 'd': - flag_dereference = 0; - break; + /* 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. */ + if (chown (dst_path, src_sb.st_uid, src_sb.st_gid) + && (errno != EPERM || myeuid == 0)) + { + error (0, errno, "%s", dst_path); + return 1; + } + } - case 'f': - flag_force = 1; - flag_interactive = 0; - break; + if (x->preserve || p->is_new_dir) + { + if (chmod (dst_path, src_sb.st_mode & x->umask_kill)) + { + error (0, errno, "%s", dst_path); + return 1; + } + } - case 'i': - flag_force = 0; - flag_interactive = 1; - break; + dst_path[p->slash_offset] = '/'; + } + return 0; +} - case 'l': - flag_hard_link = 1; - break; +/* Ensure that the parent directory of CONST_DIRPATH exists, for + the --parents option. - case 'p': - flag_preserve = 1; - break; + SRC_OFFSET is the index in CONST_DIRPATH (which is a destination + path) of the beginning of the source directory name. + Create any leading directories that don't already exist, + giving them permissions MODE. + If VERBOSE_FMT_STRING is nonzero, use it as a printf format + string for printing a message after successfully making a directory. + The format should take two string arguments: the names of the + source and destination directories. + Creates a linked list of attributes of intermediate directories, + *ATTR_LIST, for re_protect to use after calling copy. + Sets *NEW_DST to 1 if this function creates parent of CONST_DIRPATH. - case 'P': - flag_path = 1; - break; + Return 0 if parent of CONST_DIRPATH exists as a directory with the proper + permissions when done, otherwise 1. */ - case 'r': - flag_recursive = 1; - flag_copy_as_regular = 1; - break; +static int +make_path_private (const char *const_dirpath, int src_offset, int mode, + const char *verbose_fmt_string, struct dir_attr **attr_list, + int *new_dst, int (*xstat)()) +{ + struct stat stats; + char *dirpath; /* A copy of CONST_DIRPATH we can change. */ + char *src; /* Source name in `dirpath'. */ + char *tmp_dst_dirname; /* Leading path of `dirpath', malloc. */ + char *dst_dirname; /* Leading path of `dirpath', alloca. */ - case 'R': - flag_recursive = 1; - flag_copy_as_regular = 0; - break; + dirpath = (char *) alloca (strlen (const_dirpath) + 1); + strcpy (dirpath, const_dirpath); - case 's': -#ifdef S_ISLNK - flag_symbolic_link = 1; -#else - error (1, 0, _("symbolic links are not supported on this system")); -#endif - break; + src = dirpath + src_offset; - case 'u': - flag_update = 1; - break; + tmp_dst_dirname = dirname (dirpath); + dst_dirname = (char *) alloca (strlen (tmp_dst_dirname) + 1); + strcpy (dst_dirname, tmp_dst_dirname); + free (tmp_dst_dirname); - case 'v': - flag_verbose = 1; - break; + *attr_list = NULL; - case 'x': - flag_one_file_system = 1; - break; + if ((*xstat) (dst_dirname, &stats)) + { + /* Parent of CONST_DIRNAME does not exist. + Make all missing intermediate directories. */ + char *slash; - case 'S': - simple_backup_suffix = optarg; - break; + slash = src; + while (*slash == '/') + slash++; + while ((slash = strchr (slash, '/'))) + { + /* Add this directory to the list of directories whose modes need + fixing later. */ + struct dir_attr *new = + (struct dir_attr *) xmalloc (sizeof (struct dir_attr)); + new->slash_offset = slash - dirpath; + new->next = *attr_list; + *attr_list = new; - case 'V': - version = optarg; - break; + *slash = '\0'; + if ((*xstat) (dirpath, &stats)) + { + /* This element of the path does not exist. We must set + *new_dst and new->is_new_dir inside this loop because, + for example, in the command `cp --parents ../a/../b/c e_dir', + make_path_private creates only e_dir/../a if ./b already + exists. */ + *new_dst = 1; + new->is_new_dir = 1; + if (mkdir (dirpath, mode)) + { + error (0, errno, _("cannot make directory `%s'"), dirpath); + return 1; + } + else + { + if (verbose_fmt_string != NULL) + printf (verbose_fmt_string, src, dirpath); + } + } + else if (!S_ISDIR (stats.st_mode)) + { + error (0, 0, _("`%s' exists but is not a directory"), dirpath); + return 1; + } + else + { + new->is_new_dir = 0; + *new_dst = 0; + } + *slash++ = '/'; - default: - usage (1); + /* Avoid unnecessary calls to `stat' when given + pathnames containing multiple adjacent slashes. */ + while (*slash == '/') + slash++; } } - if (show_version) - { - printf ("cp (%s) %s\n", GNU_PACKAGE, VERSION); - exit (0); - } - - if (show_help) - usage (0); + /* We get here if the parent of `dirpath' already exists. */ - if (flag_hard_link && flag_symbolic_link) + else if (!S_ISDIR (stats.st_mode)) { - error (0, 0, _("cannot make both hard and symbolic links")); - usage (1); + error (0, 0, _("`%s' exists but is not a directory"), dst_dirname); + return 1; } - - if (make_backups) - backup_type = get_version (version); - - if (flag_preserve == 1) - umask_kill = 0777777; - - /* The key difference between -d (--no-dereference) and not is the version - of `stat' to call. */ - - if (flag_dereference) - xstat = stat; else - xstat = lstat; - - /* Allocate space for remembering copied and created files. */ - - hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE); - - exit_status |= do_copy (argc, argv); - - exit (exit_status); + { + *new_dst = 0; + } + return 0; } /* Scan the arguments, and copy each by calling copy. Return 0 if successful, 1 if any errors occur. */ static int -do_copy (int argc, char **argv) +do_copy (int argc, char **argv, const struct cp_options *x) { char *dest; struct stat sb; @@ -467,9 +460,10 @@ do_copy (int argc, char **argv) parent_exists = !make_path_private (dst_path, arg_in_concat - dst_path, 0700, - (flag_verbose + (x->verbose ? "%s -> %s\n" : NULL), - &attr_list, &new_dst); + &attr_list, &new_dst, + x->xstat); } else { @@ -489,13 +483,13 @@ do_copy (int argc, char **argv) } else { - ret |= copy (arg, dst_path, new_dst, 0, (struct dir_list *) 0); + ret |= copy (arg, dst_path, new_dst, CP_OPTIONS); forget_all (); if (flag_path) { ret |= re_protect (dst_path, arg_in_concat - dst_path, - attr_list); + attr_list, x); } } @@ -527,7 +521,7 @@ do_copy (int argc, char **argv) `cp --force --backup foo foo' to `cp --force foo fooSUFFIX' where SUFFIX is determined by any version control options used. */ - if (flag_force + if (x->force && backup_type != none && STREQ (source, dest) && !new_dst && S_ISREG (sb.st_mode)) @@ -571,7 +565,7 @@ do_copy (int argc, char **argv) new_dest = dest; } - return copy (source, new_dest, new_dst, 0, (struct dir_list *) 0); + return copy (source, new_dest, new_dst, CP_OPTIONS); } else { @@ -583,860 +577,188 @@ is not a directory"), } } -/* 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. - Return 0 if successful, 1 if an error occurs. */ - -static int -copy (const char *src_path, const char *dst_path, int new_dst, dev_t device, - struct dir_list *ancestors) +static void +cp_option_init (struct cp_options *x) { - struct stat src_sb; - struct stat dst_sb; - int src_mode; - int src_type; - char *earlier_file; - char *dst_backup = NULL; - int fix_mode = 0; - - if ((*xstat) (src_path, &src_sb)) - { - error (0, errno, "%s", src_path); - return 1; - } - - /* Are we crossing a file system boundary? */ - if (flag_one_file_system && device != 0 && device != src_sb.st_dev) - return 0; - - /* We wouldn't insert a node unless nlink > 1, except that we need to - find created files so as to not copy infinitely if a directory is - copied into itself. */ - - earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev); - - /* Did we just create this file? */ - - if (earlier_file == &new_file) - return 0; - - src_mode = src_sb.st_mode; - src_type = src_sb.st_mode; + x->copy_as_regular = 1; + x->dereference = 1; + x->force = 0; + x->hard_link = 0; + x->interactive = 0; + x->myeuid = geteuid (); + x->one_file_system = 0; + x->preserve = 0; + x->recursive = 0; + x->sparse_mode = SPARSE_AUTO; + x->symbolic_link = 0; - if (S_ISDIR (src_type) && !flag_recursive) - { - error (0, 0, _("%s: omitting directory"), src_path); - return 1; - } - - if (!new_dst) - { - if ((*xstat) (dst_path, &dst_sb)) - { - if (errno != ENOENT) - { - error (0, errno, "%s", dst_path); - return 1; - } - else - new_dst = 1; - } - else - { - /* The file exists already. */ - - if (src_sb.st_ino == dst_sb.st_ino && src_sb.st_dev == dst_sb.st_dev) - { - if (flag_hard_link) - return 0; - - error (0, 0, _("`%s' and `%s' are the same file"), - src_path, dst_path); - return 1; - } - - if (!S_ISDIR (src_type)) - { - if (S_ISDIR (dst_sb.st_mode)) - { - error (0, 0, - _("%s: cannot overwrite directory with non-directory"), - dst_path); - return 1; - } - - if (flag_update && src_sb.st_mtime <= dst_sb.st_mtime) - return 0; - } - - if (!S_ISDIR (src_type) && !flag_force && flag_interactive) - { - if (euidaccess (dst_path, W_OK) != 0) - { - fprintf (stderr, - _("%s: overwrite `%s', overriding mode %04o? "), - program_name, dst_path, - (unsigned int) (dst_sb.st_mode & 07777)); - } - else - { - fprintf (stderr, _("%s: overwrite `%s'? "), - program_name, dst_path); - } - if (!yesno ()) - return 0; - } + /* Find out the current file creation mask, to knock the right bits + when using chmod. The creation mask is set to be liberal, so + that created directories can be written, even if it would not + have been allowed with the mask this process was started with. */ + x->umask_kill = 0777777 ^ umask (0); - if (backup_type != none && !S_ISDIR (dst_sb.st_mode)) - { - char *tmp_backup = find_backup_file_name (dst_path); - if (tmp_backup == NULL) - error (1, 0, _("virtual memory exhausted")); + x->update = 0; + x->verbose = 0; +} - /* 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 -V simple a~ a - would leave two zero-length files: a and a~. */ - if (STREQ (tmp_backup, src_path)) - { - error (0, 0, - _("backing up `%s' would destroy source; `%s' not copied"), - dst_path, src_path); - return 1; +int +main (int argc, char **argv) +{ + int c; + int make_backups = 0; + char *version; + struct cp_options x; - } - 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'"), dst_path); - return 1; - } - else - dst_backup = NULL; - } - new_dst = 1; - } - else if (flag_force) - { - if (S_ISDIR (dst_sb.st_mode)) - { - /* Temporarily change mode to allow overwriting. */ - if (euidaccess (dst_path, W_OK | X_OK) != 0) - { - if (chmod (dst_path, 0700)) - { - error (0, errno, "%s", dst_path); - return 1; - } - else - fix_mode = 1; - } - } - else - { - if (unlink (dst_path) && errno != ENOENT) - { - error (0, errno, _("cannot remove old link to `%s'"), - dst_path); - return 1; - } - new_dst = 1; - } - } - } - } + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); - /* 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 (flag_verbose && !S_ISDIR (src_type)) - printf ("%s -> %s\n", src_path, dst_path); + cp_option_init (&x); - /* Did we copy this inode somewhere else (in this command line argument) - and therefore this is a second hard link to the inode? */ + version = getenv ("SIMPLE_BACKUP_SUFFIX"); + if (version) + simple_backup_suffix = version; + version = getenv ("VERSION_CONTROL"); - if (!flag_dereference && src_sb.st_nlink > 1 && earlier_file) + while ((c = getopt_long (argc, argv, "abdfilprsuvxPRS:V:", long_opts, NULL)) + != -1) { - if (link (earlier_file, dst_path)) + switch (c) { - error (0, errno, "%s", dst_path); - goto un_backup; - } - return 0; - } + case 0: + break; - if (S_ISDIR (src_type)) - { - struct dir_list *dir; + case 2: + { + int i; - /* If this directory has been copied before during the - recursion, there is a symbolic link to an ancestor - directory of the symbolic link. It is impossible to - continue to copy this, unless we've got an infinite disk. */ + /* --sparse={never,auto,always} */ + i = argmatch (optarg, sparse_type_string); + if (i < 0) + { + invalid_arg (_("sparse type"), optarg, i); + usage (1); + } + x.sparse_mode = sparse_type[i]; + } + break; - if (is_ancestor (&src_sb, ancestors)) - { - error (0, 0, _("%s: cannot copy cyclic symbolic link"), src_path); - goto un_backup; - } + case 'a': /* Like -dpR. */ + x.dereference = 0; + x.preserve = 1; + x.recursive = 1; + x.copy_as_regular = 0; + break; - /* Insert the current directory in the list of parents. */ + case 'b': + make_backups = 1; + break; - dir = (struct dir_list *) alloca (sizeof (struct dir_list)); - dir->parent = ancestors; - dir->ino = src_sb.st_ino; - dir->dev = src_sb.st_dev; + case 'd': + x.dereference = 0; + break; - if (new_dst || !S_ISDIR (dst_sb.st_mode)) - { - /* Create the new directory writable and searchable, so - we can create new entries in it. */ + case 'f': + x.force = 1; + x.interactive = 0; + break; - if (mkdir (dst_path, (src_mode & umask_kill) | 0700)) - { - error (0, errno, _("cannot create directory `%s'"), dst_path); - goto un_backup; - } + case 'i': + x.force = 0; + x.interactive = 1; + break; - /* Insert the created directory's inode and device - numbers into the search structure, so that we can - avoid copying it again. */ + case 'l': + x.hard_link = 1; + break; - if (remember_created (dst_path)) - goto un_backup; + case 'p': + x.preserve = 1; + break; - if (flag_verbose) - printf ("%s -> %s\n", src_path, dst_path); - } + case 'P': + flag_path = 1; + break; - /* Copy the contents of the directory. */ + case 'r': + x.recursive = 1; + x.copy_as_regular = 1; + break; - if (copy_dir (src_path, dst_path, new_dst, &src_sb, dir)) - return 1; - } -#ifdef S_ISLNK - else if (flag_symbolic_link) - { - if (*src_path == '/' - || (!strncmp (dst_path, "./", 2) && strchr (dst_path + 2, '/') == 0) - || strchr (dst_path, '/') == 0) - { - if (symlink (src_path, dst_path)) - { - error (0, errno, "%s", dst_path); - goto un_backup; - } + case 'R': + x.recursive = 1; + x.copy_as_regular = 0; + break; - return 0; - } - else - { - error (0, 0, - _("%s: can make relative symbolic links only in current directory"), - dst_path); - goto un_backup; - } - } -#endif - else if (flag_hard_link) - { - if (link (src_path, dst_path)) - { - error (0, errno, _("cannot create link `%s'"), dst_path); - goto un_backup; - } - return 0; - } - else if (S_ISREG (src_type) - || (flag_copy_as_regular && !S_ISDIR (src_type) -#ifdef S_ISLNK - && !S_ISLNK (src_type) -#endif - )) - { - if (copy_reg (src_path, dst_path)) - goto un_backup; - } - else -#ifdef S_ISFIFO - if (S_ISFIFO (src_type)) - { - if (mkfifo (dst_path, src_mode & umask_kill)) - { - error (0, errno, _("cannot create fifo `%s'"), dst_path); - goto un_backup; - } - } - else -#endif - if (S_ISBLK (src_type) || S_ISCHR (src_type) -#ifdef S_ISSOCK - || S_ISSOCK (src_type) -#endif - ) - { - if (mknod (dst_path, src_mode & umask_kill, src_sb.st_rdev)) - { - error (0, errno, _("cannot create special file `%s'"), dst_path); - goto un_backup; - } - } - else + case 's': #ifdef S_ISLNK - if (S_ISLNK (src_type)) - { - char *link_val; - int link_size; - - link_val = (char *) alloca (PATH_MAX + 2); - link_size = readlink (src_path, link_val, PATH_MAX + 1); - if (link_size < 0) - { - error (0, errno, _("cannot read symbolic link `%s'"), src_path); - goto un_backup; - } - link_val[link_size] = '\0'; - - if (symlink (link_val, dst_path)) - { - error (0, errno, _("cannot create symbolic link `%s'"), dst_path); - goto un_backup; - } - - if (flag_preserve) - { - /* Preserve the owner and group of the just-`copied' - symbolic link, if possible. */ -#ifdef HAVE_LCHOWN - if (DO_CHOWN (lchown, dst_path, src_sb.st_uid, src_sb.st_gid)) - { - error (0, errno, "%s", dst_path); - goto un_backup; - } + x.symbolic_link = 1; #else -# ifdef ROOT_CHOWN_AFFECTS_SYMLINKS - if (myeuid == 0) - { - if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid)) - { - error (0, errno, "%s", dst_path); - goto un_backup; - } - } - else - { - /* FIXME: maybe give a diagnostic: you must be root - to preserve ownership and group of symlinks. */ - } -# else - /* Can't preserve ownership of symlinks. - FIXME: maybe give a warning or even error for symlinks - in directories with the sticky bit set -- there, not - preserving owner/group is a potential security problem. */ -# endif + error (1, 0, _("symbolic links are not supported on this system")); #endif - } + break; - return 0; - } - else -#endif - { - error (0, 0, _("%s: unknown file type"), src_path); - goto un_backup; - } + case 'u': + x.update = 1; + break; - /* Adjust the times (and if possible, ownership) for the copy. - chown turns off set[ug]id bits for non-root, - so do the chmod last. */ + case 'v': + x.verbose = 1; + break; - if (flag_preserve) - { - struct utimbuf utb; + case 'x': + x.one_file_system = 1; + break; - utb.actime = src_sb.st_atime; - utb.modtime = src_sb.st_mtime; + case 'S': + simple_backup_suffix = optarg; + break; - if (utime (dst_path, &utb)) - { - error (0, errno, "%s", dst_path); - return 1; - } + case 'V': + version = optarg; + break; - if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid)) - { - error (0, errno, "%s", dst_path); - return 1; + default: + usage (1); } } - if ((flag_preserve || new_dst) - && (flag_copy_as_regular || S_ISREG (src_type) || S_ISDIR (src_type))) - { - if (chmod (dst_path, src_mode & umask_kill)) - { - error (0, errno, "%s", dst_path); - return 1; - } - } - else if (fix_mode) + if (show_version) { - /* Reset the temporarily changed mode. */ - if (chmod (dst_path, dst_sb.st_mode)) - { - error (0, errno, "%s", dst_path); - return 1; - } + printf ("cp (%s) %s\n", GNU_PACKAGE, VERSION); + exit (0); } - return 0; + if (show_help) + usage (0); -un_backup: - if (dst_backup) + if (x.hard_link && x.symbolic_link) { - if (rename (dst_backup, dst_path)) - error (0, errno, _("cannot un-backup `%s'"), dst_path); + error (0, 0, _("cannot make both hard and symbolic links")); + usage (1); } - return 1; -} - -/* Ensure that the parent directory of CONST_DIRPATH exists, for - the --parents option. - - SRC_OFFSET is the index in CONST_DIRPATH (which is a destination - path) of the beginning of the source directory name. - Create any leading directories that don't already exist, - giving them permissions MODE. - If VERBOSE_FMT_STRING is nonzero, use it as a printf format - string for printing a message after successfully making a directory. - The format should take two string arguments: the names of the - source and destination directories. - Creates a linked list of attributes of intermediate directories, - *ATTR_LIST, for re_protect to use after calling copy. - Sets *NEW_DST to 1 if this function creates parent of CONST_DIRPATH. - - Return 0 if parent of CONST_DIRPATH exists as a directory with the proper - permissions when done, otherwise 1. */ - -static int -make_path_private (const char *const_dirpath, int src_offset, int mode, - const char *verbose_fmt_string, struct dir_attr **attr_list, - int *new_dst) -{ - struct stat stats; - char *dirpath; /* A copy of CONST_DIRPATH we can change. */ - char *src; /* Source name in `dirpath'. */ - char *tmp_dst_dirname; /* Leading path of `dirpath', malloc. */ - char *dst_dirname; /* Leading path of `dirpath', alloca. */ - - dirpath = (char *) alloca (strlen (const_dirpath) + 1); - strcpy (dirpath, const_dirpath); - - src = dirpath + src_offset; - tmp_dst_dirname = dirname (dirpath); - dst_dirname = (char *) alloca (strlen (tmp_dst_dirname) + 1); - strcpy (dst_dirname, tmp_dst_dirname); - free (tmp_dst_dirname); - - *attr_list = NULL; - - if ((*xstat) (dst_dirname, &stats)) - { - /* Parent of CONST_DIRNAME does not exist. - Make all missing intermediate directories. */ - char *slash; - - slash = src; - while (*slash == '/') - slash++; - while ((slash = strchr (slash, '/'))) - { - /* Add this directory to the list of directories whose modes need - fixing later. */ - struct dir_attr *new = - (struct dir_attr *) xmalloc (sizeof (struct dir_attr)); - new->slash_offset = slash - dirpath; - new->next = *attr_list; - *attr_list = new; - - *slash = '\0'; - if ((*xstat) (dirpath, &stats)) - { - /* This element of the path does not exist. We must set - *new_dst and new->is_new_dir inside this loop because, - for example, in the command `cp --parents ../a/../b/c e_dir', - make_path_private creates only e_dir/../a if ./b already - exists. */ - *new_dst = 1; - new->is_new_dir = 1; - if (mkdir (dirpath, mode)) - { - error (0, errno, _("cannot make directory `%s'"), dirpath); - return 1; - } - else - { - if (verbose_fmt_string != NULL) - printf (verbose_fmt_string, src, dirpath); - } - } - else if (!S_ISDIR (stats.st_mode)) - { - error (0, 0, _("`%s' exists but is not a directory"), dirpath); - return 1; - } - else - { - new->is_new_dir = 0; - *new_dst = 0; - } - *slash++ = '/'; + if (make_backups) + backup_type = get_version (version); - /* Avoid unnecessary calls to `stat' when given - pathnames containing multiple adjacent slashes. */ - while (*slash == '/') - slash++; - } - } + if (x.preserve == 1) + x.umask_kill = 0777777; - /* We get here if the parent of `dirpath' already exists. */ + /* The key difference between -d (--no-dereference) and not is the version + of `stat' to call. */ - else if (!S_ISDIR (stats.st_mode)) - { - error (0, 0, _("`%s' exists but is not a directory"), dst_dirname); - return 1; - } + if (x.dereference) + x.xstat = stat; else - { - *new_dst = 0; - } - return 0; -} - -/* Ensure that the parent directories of CONST_DST_PATH have the - correct protections, for the --parents option. This is done - after all copying has been completed, to allow permissions - that don't include user write/execute. - - SRC_OFFSET is the index in CONST_DST_PATH of the beginning of the - source directory name. - - ATTR_LIST is a null-terminated linked list of structures that - indicates the end of the filename of each intermediate directory - in CONST_DST_PATH that may need to have its attributes changed. - The command `cp --parents --preserve a/b/c d/e_dir' changes the - attributes of the directories d/e_dir/a and d/e_dir/a/b to match - the corresponding source directories regardless of whether they - existed before the `cp' command was given. - - Return 0 if the parent of CONST_DST_PATH and any intermediate - directories specified by ATTR_LIST have the proper permissions - when done, otherwise 1. */ - -static int -re_protect (const char *const_dst_path, int src_offset, - struct dir_attr *attr_list) -{ - struct dir_attr *p; - char *dst_path; /* A copy of CONST_DST_PATH we can change. */ - char *src_path; /* The source name in `dst_path'. */ - - dst_path = (char *) alloca (strlen (const_dst_path) + 1); - strcpy (dst_path, const_dst_path); - src_path = dst_path + src_offset; - - for (p = attr_list; p; p = p->next) - { - struct stat src_sb; - - dst_path[p->slash_offset] = '\0'; - - if ((*xstat) (src_path, &src_sb)) - { - error (0, errno, "%s", src_path); - return 1; - } - - /* Adjust the times (and if possible, ownership) for the copy. - chown turns off set[ug]id bits for non-root, - so do the chmod last. */ - - if (flag_preserve) - { - struct utimbuf utb; - - utb.actime = src_sb.st_atime; - utb.modtime = src_sb.st_mtime; - - if (utime (dst_path, &utb)) - { - error (0, errno, "%s", dst_path); - return 1; - } - - /* 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. */ - if (chown (dst_path, src_sb.st_uid, src_sb.st_gid) - && (errno != EPERM || myeuid == 0)) - { - error (0, errno, "%s", dst_path); - return 1; - } - } + x.xstat = lstat; - if (flag_preserve || p->is_new_dir) - { - if (chmod (dst_path, src_sb.st_mode & umask_kill)) - { - error (0, errno, "%s", dst_path); - return 1; - } - } - - dst_path[p->slash_offset] = '/'; - } - 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. - Return 0 if successful, -1 if an error occurs. */ - -static int -copy_dir (const char *src_path_in, const char *dst_path_in, int new_dst, - const struct stat *src_sb, struct dir_list *ancestors) -{ - char *name_space; - char *namep; - char *src_path; - char *dst_path; - int ret = 0; - - errno = 0; - name_space = savedir (src_path_in, src_sb->st_size); - if (name_space == 0) - { - if (errno) - { - error (0, errno, "%s", src_path_in); - return -1; - } - else - error (1, 0, _("virtual memory exhausted")); - } - - namep = name_space; - while (*namep != '\0') - { - int fn_length = strlen (namep) + 1; - - dst_path = xmalloc (strlen (dst_path_in) + fn_length + 1); - src_path = xmalloc (strlen (src_path_in) + fn_length + 1); - - stpcpy (stpcpy (stpcpy (src_path, src_path_in), "/"), namep); - stpcpy (stpcpy (stpcpy (dst_path, dst_path_in), "/"), namep); - - ret |= copy (src_path, dst_path, new_dst, src_sb->st_dev, ancestors); - - /* Free the memory for `src_path'. The memory for `dst_path' - cannot be deallocated, since it is used to create multiple - hard links. */ - - free (src_path); - - namep += fn_length; - } - 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.) - Return 0 if successful, -1 if an error occurred. */ - -static int -copy_reg (const char *src_path, const char *dst_path) -{ - char *buf; - int buf_size; - int dest_desc; - int source_desc; - int n_read; - struct stat sb; - char *cp; - int *ip; - int return_val = 0; - long n_read_total = 0; - int last_write_made_hole = 0; - int make_holes = (flag_sparse == SPARSE_ALWAYS); - - source_desc = open (src_path, O_RDONLY); - if (source_desc < 0) - { - error (0, errno, "%s", src_path); - return -1; - } - - /* Create the new regular file with small permissions initially, - to not create a security hole. */ - - dest_desc = open (dst_path, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (dest_desc < 0) - { - error (0, errno, _("cannot create regular file `%s'"), dst_path); - return_val = -1; - goto ret2; - } - - /* Find out the optimal buffer size. */ - - if (fstat (dest_desc, &sb)) - { - error (0, errno, "%s", dst_path); - return_val = -1; - goto ret; - } - - buf_size = ST_BLKSIZE (sb); - -#ifdef HAVE_ST_BLOCKS - if (flag_sparse == 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, "%s", src_path); - return_val = -1; - goto ret; - } - - /* 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) - && (size_t) (sb.st_size / 512) > (size_t) 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 (;;) - { - n_read = read (source_desc, buf, buf_size); - if (n_read < 0) - { -#ifdef EINTR - if (errno == EINTR) - continue; -#endif - error (0, errno, "%s", src_path); - return_val = -1; - goto ret; - } - 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, "%s", dst_path); - return_val = -1; - goto ret; - } - last_write_made_hole = 1; - } - else - /* Clear to indicate that a normal write is needed. */ - ip = 0; - } - if (ip == 0) - { - if (full_write (dest_desc, buf, n_read) < 0) - { - error (0, errno, "%s", dst_path); - return_val = -1; - goto ret; - } - 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. */ + /* Allocate space for remembering copied and created files. */ - if (last_write_made_hole) - { -#ifdef HAVE_FTRUNCATE - /* Write a null character and truncate it again. */ - if (full_write (dest_desc, "", 1) < 0 - || 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) < 0) -#endif - { - error (0, errno, "%s", dst_path); - return_val = -1; - } - } + hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE); -ret: - if (close (dest_desc) < 0) - { - error (0, errno, "%s", dst_path); - return_val = -1; - } -ret2: - if (close (source_desc) < 0) - { - error (0, errno, "%s", src_path); - return_val = -1; - } + exit_status |= do_copy (argc, argv, &x); - return return_val; + exit (exit_status); } |