- first pass to unify/cleanup uid handling (-236b)
[platform/upstream/busybox.git] / coreutils / install.c
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 tarball for details.
7  *
8  * TODO: -d option, need a way of recursively making directories and changing
9  *           owner/group, will probably modify bb_make_directory(...)
10  */
11
12 #include "libbb.h"
13 #include "libcoreutils/coreutils.h"
14
15 #if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
16 static const char install_longopts[] ALIGN1 =
17         "directory\0"           No_argument       "d"
18         "preserve-timestamps\0" No_argument       "p"
19         "strip\0"               No_argument       "s"
20         "group\0"               No_argument       "g"
21         "mode\0"                No_argument       "m"
22         "owner\0"               No_argument       "o"
23 /* autofs build insists of using -b --suffix=.orig */
24 /* TODO? (short option for --suffix is -S) */
25 #if ENABLE_SELINUX
26         "context\0"             Required_argument "Z"
27         "preserve_context\0"    No_argument       "\xff"
28         "preserve-context\0"    No_argument       "\xff"
29 #endif
30         ;
31 #endif
32
33
34 #if ENABLE_SELINUX
35 static void setdefaultfilecon(const char *path)
36 {
37         struct stat s;
38         security_context_t scontext = NULL;
39
40         if (!is_selinux_enabled()) {
41                 return;
42         }
43         if (lstat(path, &s) != 0) {
44                 return;
45         }
46
47         if (matchpathcon(path, s.st_mode, &scontext) < 0) {
48                 goto out;
49         }
50         if (strcmp(scontext, "<<none>>") == 0) {
51                 goto out;
52         }
53
54         if (lsetfilecon(path, scontext) < 0) {
55                 if (errno != ENOTSUP) {
56                         bb_perror_msg("warning: failed to change context of %s to %s", path, scontext);
57                 }
58         }
59
60  out:
61         freecon(scontext);
62 }
63
64 #endif
65
66 int install_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
67 int install_main(int argc, char **argv)
68 {
69         struct stat statbuf;
70         mode_t mode;
71         uid_t uid;
72         gid_t gid;
73         char *arg, *last;
74         const char *gid_str;
75         const char *uid_str;
76         const char *mode_str;
77         int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE;
78         int flags;
79         int min_args = 1;
80         int ret = EXIT_SUCCESS;
81         int isdir = 0;
82 #if ENABLE_SELINUX
83         security_context_t scontext;
84         bool use_default_selinux_context = 1;
85 #endif
86         enum {
87                 OPT_c             = 1 << 0,
88                 OPT_v             = 1 << 1,
89                 OPT_b             = 1 << 2,
90                 OPT_DIRECTORY     = 1 << 3,
91                 OPT_PRESERVE_TIME = 1 << 4,
92                 OPT_STRIP         = 1 << 5,
93                 OPT_GROUP         = 1 << 6,
94                 OPT_MODE          = 1 << 7,
95                 OPT_OWNER         = 1 << 8,
96 #if ENABLE_SELINUX
97                 OPT_SET_SECURITY_CONTEXT = 1 << 9,
98                 OPT_PRESERVE_SECURITY_CONTEXT = 1 << 10,
99 #endif
100         };
101
102 #if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
103         applet_long_options = install_longopts;
104 #endif
105         opt_complementary = "s--d:d--s" USE_SELINUX(":Z--\xff:\xff--Z");
106         /* -c exists for backwards compatibility, it's needed */
107         /* -v is ignored ("print name of each created directory") */
108         /* -b is ignored ("make a backup of each existing destination file") */
109         flags = getopt32(argv, "cvb" "dpsg:m:o:" USE_SELINUX("Z:"),
110                         &gid_str, &mode_str, &uid_str USE_SELINUX(, &scontext));
111         argc -= optind;
112         argv += optind;
113
114 #if ENABLE_SELINUX
115         if (flags & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) {
116                 selinux_or_die();
117                 use_default_selinux_context = 0;
118                 if (flags & OPT_PRESERVE_SECURITY_CONTEXT) {
119                         copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT;
120                 }
121                 if (flags & OPT_SET_SECURITY_CONTEXT) {
122                         setfscreatecon_or_die(scontext);
123                         copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT;
124                 }
125         }
126 #endif
127
128         /* preserve access and modification time, this is GNU behaviour, BSD only preserves modification time */
129         if (flags & OPT_PRESERVE_TIME) {
130                 copy_flags |= FILEUTILS_PRESERVE_STATUS;
131         }
132         mode = 0666;
133         if (flags & OPT_MODE)
134                 bb_parse_mode(mode_str, &mode);
135         uid = (flags & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid();
136         gid = (flags & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid();
137
138         last = argv[argc - 1];
139         if (!(flags & OPT_DIRECTORY)) {
140                 argv[argc - 1] = NULL;
141                 min_args++;
142
143                 /* coreutils install resolves link in this case, don't use lstat */
144                 isdir = stat(last, &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode);
145         }
146
147         if (argc < min_args)
148                 bb_show_usage();
149
150         while ((arg = *argv++) != NULL) {
151                 char *dest = last;
152                 if (flags & OPT_DIRECTORY) {
153                         dest = arg;
154                         /* GNU coreutils 6.9 does not set uid:gid
155                          * on intermediate created directories
156                          * (only on last one) */
157                         if (bb_make_directory(dest, 0755, FILEUTILS_RECUR)) {
158                                 ret = EXIT_FAILURE;
159                                 goto next;
160                         }
161                 } else {
162                         if (isdir)
163                                 dest = concat_path_file(last, basename(arg));
164                         if (copy_file(arg, dest, copy_flags)) {
165                                 /* copy is not made */
166                                 ret = EXIT_FAILURE;
167                                 goto next;
168                         }
169                 }
170
171                 /* Set the file mode */
172                 if ((flags & OPT_MODE) && chmod(dest, mode) == -1) {
173                         bb_perror_msg("can't change %s of %s", "permissions", dest);
174                         ret = EXIT_FAILURE;
175                 }
176 #if ENABLE_SELINUX
177                 if (use_default_selinux_context)
178                         setdefaultfilecon(dest);
179 #endif
180                 /* Set the user and group id */
181                 if ((flags & (OPT_OWNER|OPT_GROUP))
182                  && lchown(dest, uid, gid) == -1
183                 ) {
184                         bb_perror_msg("can't change %s of %s", "ownership", dest);
185                         ret = EXIT_FAILURE;
186                 }
187                 if (flags & OPT_STRIP) {
188                         char *args[3];
189                         args[0] = (char*)"strip";
190                         args[1] = dest;
191                         args[2] = NULL;
192                         if (spawn_and_wait(args)) {
193                                 bb_perror_msg("strip");
194                                 ret = EXIT_FAILURE;
195                         }
196                 }
197  next:
198                 if (ENABLE_FEATURE_CLEAN_UP && isdir)
199                         free(dest);
200         }
201
202         return ret;
203 }