busybox/coreutils/cp.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini cp implementation for busybox
   4 *
   5 * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
   6 * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp>
   7 *
   8 * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
   9 */
  10
  11/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
  12
  13/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
  14 *
  15 * Size reduction.
  16 */
  17
  18#include "libbb.h"
  19#include "libcoreutils/coreutils.h"
  20
  21/* This is a NOEXEC applet. Be very careful! */
  22
  23int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  24int cp_main(int argc, char **argv)
  25{
  26        struct stat source_stat;
  27        struct stat dest_stat;
  28        const char *last;
  29        const char *dest;
  30        int s_flags;
  31        int d_flags;
  32        int flags;
  33        int status;
  34        enum {
  35                OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1),
  36                OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)),
  37                OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1),
  38                OPT_v = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2),
  39#if ENABLE_FEATURE_CP_LONG_OPTIONS
  40                OPT_parents = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3),
  41#endif
  42        };
  43
  44        // Need at least two arguments
  45        // Soft- and hardlinking doesn't mix
  46        // -P and -d are the same (-P is POSIX, -d is GNU)
  47        // -r and -R are the same
  48        // -R (and therefore -r) turns on -d (coreutils does this)
  49        // -a = -pdR
  50        opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR";
  51#if ENABLE_FEATURE_CP_LONG_OPTIONS
  52        applet_long_options =
  53                "archive\0"        No_argument "a"
  54                "force\0"          No_argument "f"
  55                "interactive\0"    No_argument "i"
  56                "link\0"           No_argument "l"
  57                "dereference\0"    No_argument "L"
  58                "no-dereference\0" No_argument "P"
  59                "recursive\0"      No_argument "R"
  60                "symbolic-link\0"  No_argument "s"
  61                "verbose\0"        No_argument "v"
  62                "parents\0"        No_argument "\xff"
  63                ;
  64#endif
  65        // -v (--verbose) is ignored
  66        flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv");
  67        /* Options of cp from GNU coreutils 6.10:
  68         * -a, --archive
  69         * -f, --force
  70         * -i, --interactive
  71         * -l, --link
  72         * -L, --dereference
  73         * -P, --no-dereference
  74         * -R, -r, --recursive
  75         * -s, --symbolic-link
  76         * -v, --verbose
  77         * -H   follow command-line symbolic links in SOURCE
  78         * -d   same as --no-dereference --preserve=links
  79         * -p   same as --preserve=mode,ownership,timestamps
  80         * -c   same as --preserve=context
  81         * --parents
  82         *      use full source file name under DIRECTORY
  83         * NOT SUPPORTED IN BBOX:
  84         * --backup[=CONTROL]
  85         *      make a backup of each existing destination file
  86         * -b   like --backup but does not accept an argument
  87         * --copy-contents
  88         *      copy contents of special files when recursive
  89         * --preserve[=ATTR_LIST]
  90         *      preserve attributes (default: mode,ownership,timestamps),
  91         *      if possible additional attributes: security context,links,all
  92         * --no-preserve=ATTR_LIST
  93         * --remove-destination
  94         *      remove  each existing destination file before attempting to open
  95         * --sparse=WHEN
  96         *      control creation of sparse files
  97         * --strip-trailing-slashes
  98         *      remove any trailing slashes from each SOURCE argument
  99         * -S, --suffix=SUFFIX
 100         *      override the usual backup suffix
 101         * -t, --target-directory=DIRECTORY
 102         *      copy all SOURCE arguments into DIRECTORY
 103         * -T, --no-target-directory
 104         *      treat DEST as a normal file
 105         * -u, --update
 106         *      copy only when the SOURCE file is newer than the destination
 107         *      file or when the destination file is missing
 108         * -x, --one-file-system
 109         *      stay on this file system
 110         * -Z, --context=CONTEXT
 111         *      (SELinux) set SELinux security context of copy to CONTEXT
 112         */
 113        argc -= optind;
 114        argv += optind;
 115        /* Reverse this bit. If there is -d, bit is not set: */
 116        flags ^= FILEUTILS_DEREFERENCE;
 117        /* coreutils 6.9 compat:
 118         * by default, "cp" derefs symlinks (creates regular dest files),
 119         * but "cp -R" does not. We switch off deref if -r or -R (see above).
 120         * However, "cp -RL" must still deref symlinks: */
 121        if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */
 122                flags |= FILEUTILS_DEREFERENCE;
 123
 124#if ENABLE_SELINUX
 125        if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) {
 126                selinux_or_die();
 127        }
 128#endif
 129
 130        status = EXIT_SUCCESS;
 131        last = argv[argc - 1];
 132        /* If there are only two arguments and...  */
 133        if (argc == 2) {
 134                s_flags = cp_mv_stat2(*argv, &source_stat,
 135                                (flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
 136                if (s_flags < 0)
 137                        return EXIT_FAILURE;
 138                d_flags = cp_mv_stat(last, &dest_stat);
 139                if (d_flags < 0)
 140                        return EXIT_FAILURE;
 141
 142#if ENABLE_FEATURE_CP_LONG_OPTIONS
 143                if (flags & OPT_parents) {
 144                        if (!(d_flags & 2)) {
 145                                bb_error_msg_and_die("with --parents, the destination must be a directory");
 146                        }
 147                }
 148#endif
 149
 150                /* ...if neither is a directory...  */
 151                if (!((s_flags | d_flags) & 2)
 152                    /* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */
 153                 || ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags)
 154                ) {
 155                        /* Do a simple copy */
 156                        dest = last;
 157                        goto DO_COPY; /* NB: argc==2 -> *++argv==last */
 158                }
 159        }
 160
 161        while (1) {
 162#if ENABLE_FEATURE_CP_LONG_OPTIONS
 163                if (flags & OPT_parents) {
 164                        char *dest_dup;
 165                        char *dest_dir;
 166                        dest = concat_path_file(last, *argv);
 167                        dest_dup = xstrdup(dest);
 168                        dest_dir = dirname(dest_dup);
 169                        if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) {
 170                                return EXIT_FAILURE;
 171                        }
 172                        free(dest_dup);
 173                        goto DO_COPY;
 174                }
 175#endif
 176                dest = concat_path_file(last, bb_get_last_path_component_strip(*argv));
 177 DO_COPY:
 178                if (copy_file(*argv, dest, flags) < 0) {
 179                        status = EXIT_FAILURE;
 180                }
 181                if (*++argv == last) {
 182                        /* possibly leaking dest... */
 183                        break;
 184                }
 185                /* don't move up: dest may be == last and not malloced! */
 186                free((void*)dest);
 187        }
 188
 189        /* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */
 190        return status;
 191}
 192