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