busybox/coreutils/install.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Copyright (C) 2003 by Glenn McGrath
   4 * SELinux support: by Yuichi Nakamura <ynakam@hitachisoft.jp>
   5 *
   6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   7 */
   8//config:config INSTALL
   9//config:       bool "install (12 kb)"
  10//config:       default y
  11//config:       help
  12//config:       Copy files and set attributes.
  13//config:
  14//config:config FEATURE_INSTALL_LONG_OPTIONS
  15//config:       bool "Enable long options"
  16//config:       default y
  17//config:       depends on INSTALL && LONG_OPTS
  18
  19//applet:IF_INSTALL(APPLET(install, BB_DIR_USR_BIN, BB_SUID_DROP))
  20
  21//kbuild:lib-$(CONFIG_INSTALL) += install.o
  22
  23/* -v, -b, -c are ignored */
  24//usage:#define install_trivial_usage
  25//usage:        "[-cdDsp] [-o USER] [-g GRP] [-m MODE] [-t DIR] [SOURCE]... DEST"
  26//usage:#define install_full_usage "\n\n"
  27//usage:       "Copy files and set attributes\n"
  28//usage:     "\n        -c      Just copy (default)"
  29//usage:     "\n        -d      Create directories"
  30//usage:     "\n        -D      Create leading target directories"
  31//usage:     "\n        -s      Strip symbol table"
  32//usage:     "\n        -p      Preserve date"
  33//usage:     "\n        -o USER Set ownership"
  34//usage:     "\n        -g GRP  Set group ownership"
  35//usage:     "\n        -m MODE Set permissions"
  36//usage:     "\n        -t DIR  Install to DIR"
  37//usage:        IF_SELINUX(
  38//usage:     "\n        -Z      Set security context"
  39//usage:        )
  40
  41#include "libbb.h"
  42#include "libcoreutils/coreutils.h"
  43
  44#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
  45static const char install_longopts[] ALIGN1 =
  46        IF_FEATURE_VERBOSE(
  47        "verbose\0"             No_argument       "v"
  48        )
  49        "directory\0"           No_argument       "d"
  50        "preserve-timestamps\0" No_argument       "p"
  51        "strip\0"               No_argument       "s"
  52        "group\0"               Required_argument "g"
  53        "mode\0"                Required_argument "m"
  54        "owner\0"               Required_argument "o"
  55        "target-directory\0"    Required_argument "t"
  56/* autofs build insists of using -b --suffix=.orig */
  57/* TODO? (short option for --suffix is -S) */
  58# if ENABLE_SELINUX
  59        "context\0"             Required_argument "Z"
  60        "preserve_context\0"    No_argument       "\xff"
  61        "preserve-context\0"    No_argument       "\xff"
  62# endif
  63        ;
  64# define GETOPT32 getopt32long
  65# define LONGOPTS install_longopts,
  66#else
  67# define GETOPT32 getopt32
  68# define LONGOPTS
  69#endif
  70
  71
  72#if ENABLE_SELINUX
  73static void setdefaultfilecon(const char *path)
  74{
  75        struct stat s;
  76        security_context_t scontext = NULL;
  77
  78        if (!is_selinux_enabled()) {
  79                return;
  80        }
  81        if (lstat(path, &s) != 0) {
  82                return;
  83        }
  84
  85        if (matchpathcon(path, s.st_mode, &scontext) < 0) {
  86                goto out;
  87        }
  88        if (strcmp(scontext, "<<none>>") == 0) {
  89                goto out;
  90        }
  91
  92        if (lsetfilecon(path, scontext) < 0) {
  93                if (errno != ENOTSUP) {
  94                        bb_perror_msg("warning: can't change context"
  95                                        " of %s to %s", path, scontext);
  96                }
  97        }
  98
  99 out:
 100        freecon(scontext);
 101}
 102
 103#endif
 104
 105int install_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 106int install_main(int argc, char **argv)
 107{
 108        struct stat statbuf;
 109        mode_t mode;
 110        uid_t uid;
 111        gid_t gid;
 112        char *arg, *last;
 113        const char *gid_str;
 114        const char *uid_str;
 115        const char *mode_str;
 116        int mkdir_flags = FILEUTILS_RECUR;
 117        int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE;
 118        int opts;
 119        int ret = EXIT_SUCCESS;
 120        int isdir;
 121#if ENABLE_SELINUX
 122        security_context_t scontext;
 123        bool use_default_selinux_context = 1;
 124#endif
 125        enum {
 126                OPT_c             = 1 << 0,
 127                OPT_v             = 1 << 1,
 128                OPT_b             = 1 << 2,
 129                OPT_MKDIR_LEADING = 1 << 3,
 130                OPT_DIRECTORY     = 1 << 4,
 131                OPT_PRESERVE_TIME = 1 << 5,
 132                OPT_STRIP         = 1 << 6,
 133                OPT_GROUP         = 1 << 7,
 134                OPT_MODE          = 1 << 8,
 135                OPT_OWNER         = 1 << 9,
 136                OPT_TARGET        = 1 << 10,
 137#if ENABLE_SELINUX
 138                OPT_SET_SECURITY_CONTEXT = 1 << 11,
 139                OPT_PRESERVE_SECURITY_CONTEXT = 1 << 12,
 140#endif
 141        };
 142
 143        /* -c exists for backwards compatibility, it's needed */
 144        /* -b is ignored ("make a backup of each existing destination file") */
 145        opts = GETOPT32(argv, "^"
 146                "cvb" "Ddpsg:m:o:t:" IF_SELINUX("Z:")
 147                "\0"
 148                "t--d:d--t:s--d:d--s"
 149                IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z")),
 150                LONGOPTS
 151                &gid_str, &mode_str, &uid_str, &last
 152                IF_SELINUX(, &scontext)
 153        );
 154        argc -= optind;
 155        argv += optind;
 156
 157#if ENABLE_SELINUX
 158        if (opts & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) {
 159                selinux_or_die();
 160                use_default_selinux_context = 0;
 161                if (opts & OPT_PRESERVE_SECURITY_CONTEXT) {
 162                        copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT;
 163                }
 164                if (opts & OPT_SET_SECURITY_CONTEXT) {
 165                        setfscreatecon_or_die(scontext);
 166                        copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT;
 167                }
 168        }
 169#endif
 170
 171        if ((opts & OPT_v) && FILEUTILS_VERBOSE) {
 172                mkdir_flags |= FILEUTILS_VERBOSE;
 173                copy_flags |= FILEUTILS_VERBOSE;
 174        }
 175
 176        /* preserve access and modification time, this is GNU behaviour,
 177         * BSD only preserves modification time */
 178        if (opts & OPT_PRESERVE_TIME) {
 179                copy_flags |= FILEUTILS_PRESERVE_STATUS;
 180        }
 181        mode = 0755; /* GNU coreutils 6.10 compat */
 182        if (opts & OPT_MODE)
 183                mode = bb_parse_mode(mode_str, mode);
 184        uid = (opts & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid();
 185        gid = (opts & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid();
 186
 187        /* If -t DIR is in use, then isdir=true, last="DIR" */
 188        isdir = (opts & OPT_TARGET);
 189        if (!(opts & (OPT_TARGET|OPT_DIRECTORY))) {
 190                /* Neither -t DIR nor -d is in use */
 191                argc--;
 192                last = argv[argc];
 193                argv[argc] = NULL;
 194                /* coreutils install resolves link in this case, don't use lstat */
 195                isdir = stat(last, &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode);
 196        }
 197
 198        if (argc < 1)
 199                bb_show_usage();
 200
 201        while ((arg = *argv++) != NULL) {
 202                char *dest;
 203
 204                if (opts & OPT_DIRECTORY) {
 205                        dest = arg;
 206                        /* GNU coreutils 6.9 does not set uid:gid
 207                         * on intermediate created directories
 208                         * (only on last one) */
 209                        if (bb_make_directory(dest, 0755, mkdir_flags)) {
 210                                ret = EXIT_FAILURE;
 211                                goto next;
 212                        }
 213                } else {
 214                        dest = last;
 215                        if (opts & OPT_MKDIR_LEADING) {
 216                                char *ddir = xstrdup(dest);
 217                                /*
 218                                 * -D -t DIR1/DIR2/F3 FILE: create DIR1/DIR2/F3, copy FILE there
 219                                 * -D FILE DIR1/DIR2/F3: create DIR1/DIR2, copy FILE there as F3
 220                                 */
 221                                bb_make_directory((opts & OPT_TARGET) ? ddir : dirname(ddir), 0755, mkdir_flags);
 222                                /* errors are not checked. copy_file
 223                                 * will fail if dir is not created.
 224                                 */
 225                                free(ddir);
 226                        }
 227                        if (isdir)
 228                                dest = concat_path_file(last, bb_basename(arg));
 229                        if (copy_file(arg, dest, copy_flags) != 0) {
 230                                /* copy is not made */
 231                                ret = EXIT_FAILURE;
 232                                goto next;
 233                        }
 234                        if (opts & OPT_STRIP) {
 235                                char *args[4];
 236                                args[0] = (char*)"strip";
 237                                args[1] = (char*)"-p"; /* -p --preserve-dates */
 238                                args[2] = dest;
 239                                args[3] = NULL;
 240                                if (spawn_and_wait(args)) {
 241                                        bb_simple_perror_msg("strip");
 242                                        ret = EXIT_FAILURE;
 243                                }
 244                        }
 245                }
 246
 247                /* Set the file mode (always, not only with -m).
 248                 * GNU coreutils 6.10 is not affected by umask. */
 249                if (chmod(dest, mode) == -1) {
 250                        bb_perror_msg("can't change %s of %s", "permissions", dest);
 251                        ret = EXIT_FAILURE;
 252                }
 253#if ENABLE_SELINUX
 254                if (use_default_selinux_context)
 255                        setdefaultfilecon(dest);
 256#endif
 257                /* Set the user and group id */
 258                if ((opts & (OPT_OWNER|OPT_GROUP))
 259                 && lchown(dest, uid, gid) == -1
 260                ) {
 261                        bb_perror_msg("can't change %s of %s", "ownership", dest);
 262                        ret = EXIT_FAILURE;
 263                }
 264 next:
 265                if (ENABLE_FEATURE_CLEAN_UP && isdir)
 266                        free(dest);
 267        }
 268
 269        return ret;
 270}
 271