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 GPLv2 or later, see file LICENSE in this source tree.
   9 */
  10/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
  11 *
  12 * Size reduction.
  13 */
  14//config:config CP
  15//config:       bool "cp (9.7 kb)"
  16//config:       default y
  17//config:       help
  18//config:       cp is used to copy files and directories.
  19//config:
  20//config:config FEATURE_CP_LONG_OPTIONS
  21//config:       bool "Enable long options"
  22//config:       default y
  23//config:       depends on CP && LONG_OPTS
  24//config:       help
  25//config:       Enable long options.
  26//config:       Also add support for --parents option.
  27
  28//applet:IF_CP(APPLET_NOEXEC(cp, cp, BB_DIR_BIN, BB_SUID_DROP, cp))
  29/* NOEXEC despite cases when it can be a "runner" (cp -r LARGE_DIR NEW_DIR) */
  30
  31//kbuild:lib-$(CONFIG_CP) += cp.o
  32
  33/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
  34
  35//usage:#define cp_trivial_usage
  36//usage:       "[OPTIONS] SOURCE... DEST"
  37//usage:#define cp_full_usage "\n\n"
  38//usage:       "Copy SOURCE(s) to DEST\n"
  39//usage:     "\n        -a      Same as -dpR"
  40//usage:        IF_SELINUX(
  41//usage:     "\n        -c      Preserve security context"
  42//usage:        )
  43//usage:     "\n        -R,-r   Recurse"
  44//usage:     "\n        -d,-P   Preserve symlinks (default if -R)"
  45//usage:     "\n        -L      Follow all symlinks"
  46//usage:     "\n        -H      Follow symlinks on command line"
  47//usage:     "\n        -p      Preserve file attributes if possible"
  48//usage:     "\n        -f      Overwrite"
  49//usage:     "\n        -i      Prompt before overwrite"
  50//usage:     "\n        -l,-s   Create (sym)links"
  51//usage:     "\n        -T      Treat DEST as a normal file"
  52//usage:     "\n        -u      Copy only newer files"
  53
  54#include "libbb.h"
  55#include "libcoreutils/coreutils.h"
  56
  57/* This is a NOEXEC applet. Be very careful! */
  58
  59int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  60int cp_main(int argc, char **argv)
  61{
  62        struct stat source_stat;
  63        struct stat dest_stat;
  64        const char *last;
  65        const char *dest;
  66        int s_flags;
  67        int d_flags;
  68        int flags;
  69        int status;
  70        enum {
  71                FILEUTILS_CP_OPTNUM = sizeof(FILEUTILS_CP_OPTSTR)-1,
  72#if ENABLE_FEATURE_CP_LONG_OPTIONS
  73                /*OPT_rmdest  = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTNUM */
  74                OPT_parents = 1 << (FILEUTILS_CP_OPTNUM+1),
  75#endif
  76        };
  77
  78#if ENABLE_FEATURE_CP_LONG_OPTIONS
  79        flags = getopt32long(argv, "^"
  80                FILEUTILS_CP_OPTSTR
  81                "\0"
  82                // Need at least two arguments
  83                // Soft- and hardlinking doesn't mix
  84                // -P and -d are the same (-P is POSIX, -d is GNU)
  85                // -r and -R are the same
  86                // -R (and therefore -r) turns on -d (coreutils does this)
  87                // -a = -pdR
  88                "-2:l--s:s--l:Pd:rRd:Rd:apdR",
  89                "archive\0"        No_argument "a"
  90                "force\0"          No_argument "f"
  91                "interactive\0"    No_argument "i"
  92                "link\0"           No_argument "l"
  93                "dereference\0"    No_argument "L"
  94                "no-dereference\0" No_argument "P"
  95                "recursive\0"      No_argument "R"
  96                "symbolic-link\0"  No_argument "s"
  97                "no-target-directory\0" No_argument "T"
  98                "verbose\0"        No_argument "v"
  99                "update\0"         No_argument "u"
 100                "remove-destination\0" No_argument "\xff"
 101                "parents\0"        No_argument "\xfe"
 102        );
 103#else
 104        flags = getopt32(argv, "^"
 105                FILEUTILS_CP_OPTSTR
 106                "\0"
 107                "-2:l--s:s--l:Pd:rRd:Rd:apdR"
 108        );
 109#endif
 110        /* Options of cp from GNU coreutils 6.10:
 111         * -a, --archive
 112         * -f, --force
 113         * -i, --interactive
 114         * -l, --link
 115         * -L, --dereference
 116         * -P, --no-dereference
 117         * -R, -r, --recursive
 118         * -s, --symbolic-link
 119         * -v, --verbose
 120         * -H   follow command-line symbolic links in SOURCE
 121         * -d   same as --no-dereference --preserve=links
 122         * -p   same as --preserve=mode,ownership,timestamps
 123         * -c   same as --preserve=context
 124         * -u, --update
 125         *      copy only when the SOURCE file is newer than the destination
 126         *      file or when the destination file is missing
 127         * --remove-destination
 128         *      remove each existing destination file before attempting to open
 129         * --parents
 130         *      use full source file name under DIRECTORY
 131         * -T, --no-target-directory
 132         *      treat DEST as a normal file
 133         * NOT SUPPORTED IN BBOX:
 134         * --backup[=CONTROL]
 135         *      make a backup of each existing destination file
 136         * -b   like --backup but does not accept an argument
 137         * --copy-contents
 138         *      copy contents of special files when recursive
 139         * --preserve[=ATTR_LIST]
 140         *      preserve attributes (default: mode,ownership,timestamps),
 141         *      if possible additional attributes: security context,links,all
 142         * --no-preserve=ATTR_LIST
 143         * --sparse=WHEN
 144         *      control creation of sparse files
 145         * --strip-trailing-slashes
 146         *      remove any trailing slashes from each SOURCE argument
 147         * -S, --suffix=SUFFIX
 148         *      override the usual backup suffix
 149         * -t, --target-directory=DIRECTORY
 150         *      copy all SOURCE arguments into DIRECTORY
 151         * -x, --one-file-system
 152         *      stay on this file system
 153         * -Z, --context=CONTEXT
 154         *      (SELinux) set SELinux security context of copy to CONTEXT
 155         */
 156        argc -= optind;
 157        argv += optind;
 158        /* Reverse this bit. If there is -d, bit is not set: */
 159        flags ^= FILEUTILS_DEREFERENCE;
 160        /* coreutils 6.9 compat:
 161         * by default, "cp" derefs symlinks (creates regular dest files),
 162         * but "cp -R" does not. We switch off deref if -r or -R (see above).
 163         * However, "cp -RL" must still deref symlinks: */
 164        if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */
 165                flags |= FILEUTILS_DEREFERENCE;
 166
 167#if ENABLE_SELINUX
 168        if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) {
 169                selinux_or_die();
 170        }
 171#endif
 172
 173        status = EXIT_SUCCESS;
 174        last = argv[argc - 1];
 175        /* If there are only two arguments and...  */
 176        if (argc == 2) {
 177                s_flags = cp_mv_stat2(*argv, &source_stat,
 178                                (flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
 179                if (s_flags < 0)
 180                        return EXIT_FAILURE;
 181                d_flags = cp_mv_stat(last, &dest_stat);
 182                if (d_flags < 0)
 183                        return EXIT_FAILURE;
 184
 185                if (flags & FILEUTILS_NO_TARGET_DIR) { /* -T */
 186                        if (!(s_flags & 2) && (d_flags & 2))
 187                                /* cp -T NOTDIR DIR */
 188                                bb_error_msg_and_die("'%s' is a directory", last);
 189                }
 190
 191#if ENABLE_FEATURE_CP_LONG_OPTIONS
 192                //bb_error_msg("flags:%x FILEUTILS_RMDEST:%x OPT_parents:%x",
 193                //      flags, FILEUTILS_RMDEST, OPT_parents);
 194                if (flags & OPT_parents) {
 195                        if (!(d_flags & 2)) {
 196                                bb_error_msg_and_die("with --parents, the destination must be a directory");
 197                        }
 198                }
 199                if (flags & FILEUTILS_RMDEST) {
 200                        flags |= FILEUTILS_FORCE;
 201                }
 202#endif
 203
 204                /* ...if neither is a directory...  */
 205                if (!((s_flags | d_flags) & 2)
 206                    /* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */
 207                 || ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags)
 208                 || (flags & FILEUTILS_NO_TARGET_DIR)
 209                ) {
 210                        /* Do a simple copy */
 211                        dest = last;
 212                        goto DO_COPY; /* NB: argc==2 -> *++argv==last */
 213                }
 214        } else if (flags & FILEUTILS_NO_TARGET_DIR) {
 215                bb_error_msg_and_die("too many arguments");
 216        }
 217
 218        while (1) {
 219#if ENABLE_FEATURE_CP_LONG_OPTIONS
 220                if (flags & OPT_parents) {
 221                        char *dest_dup;
 222                        char *dest_dir;
 223                        dest = concat_path_file(last, *argv);
 224                        dest_dup = xstrdup(dest);
 225                        dest_dir = dirname(dest_dup);
 226                        if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) {
 227                                return EXIT_FAILURE;
 228                        }
 229                        free(dest_dup);
 230                        goto DO_COPY;
 231                }
 232#endif
 233                dest = concat_path_file(last, bb_get_last_path_component_strip(*argv));
 234 DO_COPY:
 235                if (copy_file(*argv, dest, flags) < 0) {
 236                        status = EXIT_FAILURE;
 237                }
 238                if (*++argv == last) {
 239                        /* possibly leaking dest... */
 240                        break;
 241                }
 242                /* don't move up: dest may be == last and not malloced! */
 243                free((void*)dest);
 244        }
 245
 246        /* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */
 247        return status;
 248}
 249