*: make GNU licensing statement forms more regular
[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 source tree.
7  */
8
9 #include "libbb.h"
10 #include "libcoreutils/coreutils.h"
11
12 #if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
13 static const char install_longopts[] ALIGN1 =
14         "directory\0"           No_argument       "d"
15         "preserve-timestamps\0" No_argument       "p"
16         "strip\0"               No_argument       "s"
17         "group\0"               Required_argument "g"
18         "mode\0"                Required_argument "m"
19         "owner\0"               Required_argument "o"
20 /* autofs build insists of using -b --suffix=.orig */
21 /* TODO? (short option for --suffix is -S) */
22 #if ENABLE_SELINUX
23         "context\0"             Required_argument "Z"
24         "preserve_context\0"    No_argument       "\xff"
25         "preserve-context\0"    No_argument       "\xff"
26 #endif
27         ;
28 #endif
29
30
31 #if ENABLE_SELINUX
32 static void setdefaultfilecon(const char *path)
33 {
34         struct stat s;
35         security_context_t scontext = NULL;
36
37         if (!is_selinux_enabled()) {
38                 return;
39         }
40         if (lstat(path, &s) != 0) {
41                 return;
42         }
43
44         if (matchpathcon(path, s.st_mode, &scontext) < 0) {
45                 goto out;
46         }
47         if (strcmp(scontext, "<<none>>") == 0) {
48                 goto out;
49         }
50
51         if (lsetfilecon(path, scontext) < 0) {
52                 if (errno != ENOTSUP) {
53                         bb_perror_msg("warning: can't change context"
54                                         " of %s to %s", path, scontext);
55                 }
56         }
57
58  out:
59         freecon(scontext);
60 }
61
62 #endif
63
64 int install_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
65 int install_main(int argc, char **argv)
66 {
67         struct stat statbuf;
68         mode_t mode;
69         uid_t uid;
70         gid_t gid;
71         char *arg, *last;
72         const char *gid_str;
73         const char *uid_str;
74         const char *mode_str;
75         int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE;
76         int opts;
77         int min_args = 1;
78         int ret = EXIT_SUCCESS;
79         int isdir = 0;
80 #if ENABLE_SELINUX
81         security_context_t scontext;
82         bool use_default_selinux_context = 1;
83 #endif
84         enum {
85                 OPT_c             = 1 << 0,
86                 OPT_v             = 1 << 1,
87                 OPT_b             = 1 << 2,
88                 OPT_MKDIR_LEADING = 1 << 3,
89                 OPT_DIRECTORY     = 1 << 4,
90                 OPT_PRESERVE_TIME = 1 << 5,
91                 OPT_STRIP         = 1 << 6,
92                 OPT_GROUP         = 1 << 7,
93                 OPT_MODE          = 1 << 8,
94                 OPT_OWNER         = 1 << 9,
95 #if ENABLE_SELINUX
96                 OPT_SET_SECURITY_CONTEXT = 1 << 10,
97                 OPT_PRESERVE_SECURITY_CONTEXT = 1 << 11,
98 #endif
99         };
100
101 #if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
102         applet_long_options = install_longopts;
103 #endif
104         opt_complementary = "s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z"));
105         /* -c exists for backwards compatibility, it's needed */
106         /* -v is ignored ("print name of each created directory") */
107         /* -b is ignored ("make a backup of each existing destination file") */
108         opts = getopt32(argv, "cvb" "Ddpsg:m:o:" IF_SELINUX("Z:"),
109                         &gid_str, &mode_str, &uid_str IF_SELINUX(, &scontext));
110         argc -= optind;
111         argv += optind;
112
113 #if ENABLE_SELINUX
114         if (opts & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) {
115                 selinux_or_die();
116                 use_default_selinux_context = 0;
117                 if (opts & OPT_PRESERVE_SECURITY_CONTEXT) {
118                         copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT;
119                 }
120                 if (opts & OPT_SET_SECURITY_CONTEXT) {
121                         setfscreatecon_or_die(scontext);
122                         copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT;
123                 }
124         }
125 #endif
126
127         /* preserve access and modification time, this is GNU behaviour,
128          * BSD only preserves modification time */
129         if (opts & OPT_PRESERVE_TIME) {
130                 copy_flags |= FILEUTILS_PRESERVE_STATUS;
131         }
132         mode = 0755; /* GNU coreutils 6.10 compat */
133         if (opts & OPT_MODE)
134                 bb_parse_mode(mode_str, &mode);
135         uid = (opts & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid();
136         gid = (opts & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid();
137
138         last = argv[argc - 1];
139         if (!(opts & 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 (opts & 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 (opts & OPT_MKDIR_LEADING) {
163                                 char *ddir = xstrdup(dest);
164                                 bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR);
165                                 /* errors are not checked. copy_file
166                                  * will fail if dir is not created. */
167                                 free(ddir);
168                         }
169                         if (isdir)
170                                 dest = concat_path_file(last, bb_basename(arg));
171                         if (copy_file(arg, dest, copy_flags) != 0) {
172                                 /* copy is not made */
173                                 ret = EXIT_FAILURE;
174                                 goto next;
175                         }
176                         if (opts & OPT_STRIP) {
177                                 char *args[4];
178                                 args[0] = (char*)"strip";
179                                 args[1] = (char*)"-p"; /* -p --preserve-dates */
180                                 args[2] = dest;
181                                 args[3] = NULL;
182                                 if (spawn_and_wait(args)) {
183                                         bb_perror_msg("strip");
184                                         ret = EXIT_FAILURE;
185                                 }
186                         }
187                 }
188
189                 /* Set the file mode (always, not only with -m).
190                  * GNU coreutils 6.10 is not affected by umask. */
191                 if (chmod(dest, mode) == -1) {
192                         bb_perror_msg("can't change %s of %s", "permissions", dest);
193                         ret = EXIT_FAILURE;
194                 }
195 #if ENABLE_SELINUX
196                 if (use_default_selinux_context)
197                         setdefaultfilecon(dest);
198 #endif
199                 /* Set the user and group id */
200                 if ((opts & (OPT_OWNER|OPT_GROUP))
201                  && lchown(dest, uid, gid) == -1
202                 ) {
203                         bb_perror_msg("can't change %s of %s", "ownership", dest);
204                         ret = EXIT_FAILURE;
205                 }
206  next:
207                 if (ENABLE_FEATURE_CLEAN_UP && isdir)
208                         free(dest);
209         }
210
211         return ret;
212 }