aboutsummaryrefslogtreecommitdiff
path: root/util-linux
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2016-04-04 16:22:54 +0100
committerRon Yorston <rmy@pobox.com>2016-04-04 16:22:54 +0100
commit253dbd612b2d2f041f4263e15a3b94df70f41e36 (patch)
treef6c6e12a0541233058a7f7ccb1251afeb457da06 /util-linux
parent3cf56a021d7a62512b477640e930e1a78288075c (diff)
parentd7d4750e1e213e7448147186dddfe3bfbb47eea0 (diff)
downloadbusybox-w32-253dbd612b2d2f041f4263e15a3b94df70f41e36.tar.gz
busybox-w32-253dbd612b2d2f041f4263e15a3b94df70f41e36.tar.bz2
busybox-w32-253dbd612b2d2f041f4263e15a3b94df70f41e36.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'util-linux')
-rw-r--r--util-linux/fbset.c18
-rw-r--r--util-linux/minix.h5
-rw-r--r--util-linux/mkfs_ext2.c10
-rw-r--r--util-linux/mkfs_vfat.c4
-rw-r--r--util-linux/nsenter.c291
-rw-r--r--util-linux/umount.c3
-rw-r--r--util-linux/unshare.c380
7 files changed, 695 insertions, 16 deletions
diff --git a/util-linux/fbset.c b/util-linux/fbset.c
index ac0082f70..09e96b763 100644
--- a/util-linux/fbset.c
+++ b/util-linux/fbset.c
@@ -248,12 +248,12 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
248 if (!p) 248 if (!p)
249 continue; 249 continue;
250 s = p + strlen(mode); 250 s = p + strlen(mode);
251 //bb_info_msg("CHECK[%s][%s][%d]", mode, p-1, *s); 251 //bb_error_msg("CHECK[%s][%s][%d]", mode, p-1, *s);
252 /* exact match? */ 252 /* exact match? */
253 if (((!*s || isspace(*s)) && '"' != s[-1]) /* end-of-token */ 253 if (((!*s || isspace(*s)) && '"' != s[-1]) /* end-of-token */
254 || ('"' == *s && '"' == p[-1]) /* ends with " but starts with " too! */ 254 || ('"' == *s && '"' == p[-1]) /* ends with " but starts with " too! */
255 ) { 255 ) {
256 //bb_info_msg("FOUND[%s][%s][%s][%d]", token[1], p, mode, isspace(*s)); 256 //bb_error_msg("FOUND[%s][%s][%s][%d]", token[1], p, mode, isspace(*s));
257 break; 257 break;
258 } 258 }
259 } 259 }
@@ -264,9 +264,9 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
264 while (config_read(parser, token, 2, 1, "# \t", PARSE_NORMAL)) { 264 while (config_read(parser, token, 2, 1, "# \t", PARSE_NORMAL)) {
265 int i; 265 int i;
266 266
267//bb_info_msg("???[%s][%s]", token[0], token[1]); 267//bb_error_msg("???[%s][%s]", token[0], token[1]);
268 if (strcmp(token[0], "endmode") == 0) { 268 if (strcmp(token[0], "endmode") == 0) {
269//bb_info_msg("OK[%s]", mode); 269//bb_error_msg("OK[%s]", mode);
270 return 1; 270 return 1;
271 } 271 }
272 p = token[1]; 272 p = token[1];
@@ -294,7 +294,7 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
294 base->yres_virtual = base_yres_virtual; 294 base->yres_virtual = base_yres_virtual;
295 base->bits_per_pixel = base_bits_per_pixel; 295 base->bits_per_pixel = base_bits_per_pixel;
296 } 296 }
297//bb_info_msg("GEO[%s]", p); 297//bb_error_msg("GEO[%s]", p);
298 break; 298 break;
299 case 1: 299 case 1:
300 if (sizeof(int) == sizeof(base->xres)) { 300 if (sizeof(int) == sizeof(base->xres)) {
@@ -321,13 +321,13 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
321 base->hsync_len = base_hsync_len; 321 base->hsync_len = base_hsync_len;
322 base->vsync_len = base_vsync_len; 322 base->vsync_len = base_vsync_len;
323 } 323 }
324//bb_info_msg("TIM[%s]", p); 324//bb_error_msg("TIM[%s]", p);
325 break; 325 break;
326 case 2: 326 case 2:
327 case 3: { 327 case 3: {
328 static const uint32_t syncs[] = {FB_VMODE_INTERLACED, FB_VMODE_DOUBLE}; 328 static const uint32_t syncs[] = {FB_VMODE_INTERLACED, FB_VMODE_DOUBLE};
329 ss(&base->vmode, syncs[i-2], p, "false"); 329 ss(&base->vmode, syncs[i-2], p, "false");
330//bb_info_msg("VMODE[%s]", p); 330//bb_error_msg("VMODE[%s]", p);
331 break; 331 break;
332 } 332 }
333 case 4: 333 case 4:
@@ -335,12 +335,12 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
335 case 6: { 335 case 6: {
336 static const uint32_t syncs[] = {FB_SYNC_VERT_HIGH_ACT, FB_SYNC_HOR_HIGH_ACT, FB_SYNC_COMP_HIGH_ACT}; 336 static const uint32_t syncs[] = {FB_SYNC_VERT_HIGH_ACT, FB_SYNC_HOR_HIGH_ACT, FB_SYNC_COMP_HIGH_ACT};
337 ss(&base->sync, syncs[i-4], p, "low"); 337 ss(&base->sync, syncs[i-4], p, "low");
338//bb_info_msg("SYNC[%s]", p); 338//bb_error_msg("SYNC[%s]", p);
339 break; 339 break;
340 } 340 }
341 case 7: 341 case 7:
342 ss(&base->sync, FB_SYNC_EXT, p, "false"); 342 ss(&base->sync, FB_SYNC_EXT, p, "false");
343//bb_info_msg("EXTSYNC[%s]", p); 343//bb_error_msg("EXTSYNC[%s]", p);
344 break; 344 break;
345 case 8: { 345 case 8: {
346 int red_offset, red_length; 346 int red_offset, red_length;
diff --git a/util-linux/minix.h b/util-linux/minix.h
index e0fbcf761..83ffe6da5 100644
--- a/util-linux/minix.h
+++ b/util-linux/minix.h
@@ -61,9 +61,14 @@ enum {
61 MINIX_ROOT_INO = 1, 61 MINIX_ROOT_INO = 1,
62 MINIX_BAD_INO = 2, 62 MINIX_BAD_INO = 2,
63 63
64#undef MINIX1_SUPER_MAGIC
64 MINIX1_SUPER_MAGIC = 0x137F, /* original minix fs */ 65 MINIX1_SUPER_MAGIC = 0x137F, /* original minix fs */
66#undef MINIX1_SUPER_MAGIC2
65 MINIX1_SUPER_MAGIC2 = 0x138F, /* minix fs, 30 char names */ 67 MINIX1_SUPER_MAGIC2 = 0x138F, /* minix fs, 30 char names */
68/* bionic has this define */
69#undef MINIX2_SUPER_MAGIC
66 MINIX2_SUPER_MAGIC = 0x2468, /* minix V2 fs */ 70 MINIX2_SUPER_MAGIC = 0x2468, /* minix V2 fs */
71#undef MINIX2_SUPER_MAGIC2
67 MINIX2_SUPER_MAGIC2 = 0x2478, /* minix V2 fs, 30 char names */ 72 MINIX2_SUPER_MAGIC2 = 0x2478, /* minix V2 fs, 30 char names */
68 MINIX_VALID_FS = 0x0001, /* clean fs */ 73 MINIX_VALID_FS = 0x0001, /* clean fs */
69 MINIX_ERROR_FS = 0x0002, /* fs has errors */ 74 MINIX_ERROR_FS = 0x0002, /* fs has errors */
diff --git a/util-linux/mkfs_ext2.c b/util-linux/mkfs_ext2.c
index 3258d7eee..f91a0b4bf 100644
--- a/util-linux/mkfs_ext2.c
+++ b/util-linux/mkfs_ext2.c
@@ -116,7 +116,7 @@ static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32
116{ 116{
117 uint32_t i; 117 uint32_t i;
118 118
119//bb_info_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, (1 << (start & 7)) - 1, (uint8_t)(0xFF00 >> (end & 7))); 119//bb_error_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, (1 << (start & 7)) - 1, (uint8_t)(0xFF00 >> (end & 7)));
120 memset(bitmap, 0, blocksize); 120 memset(bitmap, 0, blocksize);
121 i = start / 8; 121 i = start / 8;
122 memset(bitmap, 0xFF, i); 122 memset(bitmap, 0xFF, i);
@@ -151,7 +151,7 @@ static uint32_t has_super(uint32_t x)
151 151
152static void PUT(uint64_t off, void *buf, uint32_t size) 152static void PUT(uint64_t off, void *buf, uint32_t size)
153{ 153{
154// bb_info_msg("PUT[%llu]:[%u]", off, size); 154 //bb_error_msg("PUT[%llu]:[%u]", off, size);
155 xlseek(fd, off, SEEK_SET); 155 xlseek(fd, off, SEEK_SET);
156 xwrite(fd, buf, size); 156 xwrite(fd, buf, size);
157} 157}
@@ -412,7 +412,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
412 // (a bit after 8M image size), but it works for two->three groups 412 // (a bit after 8M image size), but it works for two->three groups
413 // transition (at 16M). 413 // transition (at 16M).
414 if (remainder && (remainder < overhead + 50)) { 414 if (remainder && (remainder < overhead + 50)) {
415//bb_info_msg("CHOP[%u]", remainder); 415//bb_error_msg("CHOP[%u]", remainder);
416 nblocks -= remainder; 416 nblocks -= remainder;
417 goto retry; 417 goto retry;
418 } 418 }
@@ -568,7 +568,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
568 free_blocks = (n < blocks_per_group ? n : blocks_per_group) - overhead; 568 free_blocks = (n < blocks_per_group ? n : blocks_per_group) - overhead;
569 569
570 // mark preallocated blocks as allocated 570 // mark preallocated blocks as allocated
571//bb_info_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (free_blocks + overhead)); 571//bb_error_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (free_blocks + overhead));
572 allocate(buf, blocksize, 572 allocate(buf, blocksize,
573 // reserve "overhead" blocks 573 // reserve "overhead" blocks
574 overhead, 574 overhead,
@@ -647,7 +647,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
647 n = FETCH_LE32(inode->i_block[0]) + 1; 647 n = FETCH_LE32(inode->i_block[0]) + 1;
648 for (i = 0; i < lost_and_found_blocks; ++i) 648 for (i = 0; i < lost_and_found_blocks; ++i)
649 STORE_LE(inode->i_block[i], i + n); // use next block 649 STORE_LE(inode->i_block[i], i + n); // use next block
650//bb_info_msg("LAST BLOCK USED[%u]", i + n); 650//bb_error_msg("LAST BLOCK USED[%u]", i + n);
651 PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_GOOD_OLD_FIRST_INO-1) * inodesize, 651 PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_GOOD_OLD_FIRST_INO-1) * inodesize,
652 buf, inodesize); 652 buf, inodesize);
653 653
diff --git a/util-linux/mkfs_vfat.c b/util-linux/mkfs_vfat.c
index 7d81ed06d..d53c751eb 100644
--- a/util-linux/mkfs_vfat.c
+++ b/util-linux/mkfs_vfat.c
@@ -578,7 +578,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
578 start_data_sector = (reserved_sect + NUM_FATS * sect_per_fat) * (bytes_per_sect / SECTOR_SIZE); 578 start_data_sector = (reserved_sect + NUM_FATS * sect_per_fat) * (bytes_per_sect / SECTOR_SIZE);
579 start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK; 579 start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK;
580 580
581 bb_info_msg("searching for bad blocks "); 581 bb_error_msg("searching for bad blocks");
582 currently_testing = 0; 582 currently_testing = 0;
583 try = TEST_BUFFER_BLOCKS; 583 try = TEST_BUFFER_BLOCKS;
584 while (currently_testing < volume_size_blocks) { 584 while (currently_testing < volume_size_blocks) {
@@ -616,7 +616,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
616 } 616 }
617 free(blkbuf); 617 free(blkbuf);
618 if (badblocks) 618 if (badblocks)
619 bb_info_msg("%d bad block(s)", badblocks); 619 bb_error_msg("%d bad block(s)", badblocks);
620 } 620 }
621#endif 621#endif
622 622
diff --git a/util-linux/nsenter.c b/util-linux/nsenter.c
new file mode 100644
index 000000000..b08b3dae7
--- /dev/null
+++ b/util-linux/nsenter.c
@@ -0,0 +1,291 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini nsenter implementation for busybox.
4 *
5 * Copyright (C) 2016 by Bartosz Golaszewski <bartekgola@gmail.com>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10//config:config NSENTER
11//config: bool "nsenter"
12//config: default y
13//config: select PLATFORM_LINUX
14//config: help
15//config: Run program with namespaces of other processes.
16//config:
17//config:config FEATURE_NSENTER_LONG_OPTS
18//config: bool "Enable long options"
19//config: default y
20//config: depends on NSENTER && LONG_OPTS
21//config: help
22//config: Support long options for the nsenter applet. This makes
23//config: the busybox implementation more compatible with upstream.
24
25//applet:IF_NSENTER(APPLET(nsenter, BB_DIR_USR_BIN, BB_SUID_DROP))
26
27//kbuild:lib-$(CONFIG_NSENTER) += nsenter.o
28
29//usage:#define nsenter_trivial_usage
30//usage: "[OPTIONS] [PROG [ARGS]]"
31//usage:#if ENABLE_FEATURE_NSENTER_LONG_OPTS
32//usage:#define nsenter_full_usage "\n"
33//usage: "\n -t, --target=PID Target process to get namespaces from"
34//usage: "\n -m, --mount[=FILE] Enter mount namespace"
35//usage: "\n -u, --uts[=FILE] Enter UTS namespace (hostname etc)"
36//usage: "\n -i, --ipc[=FILE] Enter System V IPC namespace"
37//usage: "\n -n, --net[=FILE] Enter network namespace"
38//usage: "\n -p, --pid[=FILE] Enter pid namespace"
39//usage: "\n -U, --user[=FILE] Enter user namespace"
40//usage: "\n -S, --setuid=UID Set uid in entered namespace"
41//usage: "\n -G, --setgid=GID Set gid in entered namespace"
42//usage: "\n --preserve-credentials Don't touch uids or gids"
43//usage: "\n -r, --root[=DIR] Set root directory"
44//usage: "\n -w, --wd[=DIR] Set working directory"
45//usage: "\n -F, --no-fork Don't fork before exec'ing PROG"
46//usage:#else
47//usage:#define nsenter_full_usage "\n"
48//usage: "\n -t PID Target process to get namespaces from"
49//usage: "\n -m[FILE] Enter mount namespace"
50//usage: "\n -u[FILE] Enter UTS namespace (hostname etc)"
51//usage: "\n -i[FILE] Enter System V IPC namespace"
52//usage: "\n -n[FILE] Enter network namespace"
53//usage: "\n -p[FILE] Enter pid namespace"
54//usage: "\n -U[FILE] Enter user namespace"
55//usage: "\n -S UID Set uid in entered namespace"
56//usage: "\n -G GID Set gid in entered namespace"
57//usage: "\n -r[DIR] Set root directory"
58//usage: "\n -w[DIR] Set working directory"
59//usage: "\n -F Don't fork before exec'ing PROG"
60//usage:#endif
61
62#include <sched.h>
63#ifndef CLONE_NEWUTS
64# define CLONE_NEWUTS 0x04000000
65#endif
66#ifndef CLONE_NEWIPC
67# define CLONE_NEWIPC 0x08000000
68#endif
69#ifndef CLONE_NEWUSER
70# define CLONE_NEWUSER 0x10000000
71#endif
72#ifndef CLONE_NEWPID
73# define CLONE_NEWPID 0x20000000
74#endif
75#ifndef CLONE_NEWNET
76# define CLONE_NEWNET 0x40000000
77#endif
78
79#include "libbb.h"
80
81struct namespace_descr {
82 int flag; /* value passed to setns() */
83 char ns_nsfile8[8]; /* "ns/" + namespace file in process' procfs entry */
84};
85
86struct namespace_ctx {
87 char *path; /* optional path to a custom ns file */
88 int fd; /* opened namespace file descriptor */
89};
90
91enum {
92 OPT_user = 1 << 0,
93 OPT_ipc = 1 << 1,
94 OPT_uts = 1 << 2,
95 OPT_network = 1 << 3,
96 OPT_pid = 1 << 4,
97 OPT_mount = 1 << 5,
98 OPT_target = 1 << 6,
99 OPT_setuid = 1 << 7,
100 OPT_setgid = 1 << 8,
101 OPT_root = 1 << 9,
102 OPT_wd = 1 << 10,
103 OPT_nofork = 1 << 11,
104 OPT_prescred = (1 << 12) * ENABLE_FEATURE_NSENTER_LONG_OPTS,
105};
106enum {
107 NS_USR_POS = 0,
108 NS_IPC_POS,
109 NS_UTS_POS,
110 NS_NET_POS,
111 NS_PID_POS,
112 NS_MNT_POS,
113 NS_COUNT,
114};
115/*
116 * The order is significant in nsenter.
117 * The user namespace comes first, so that it is entered first.
118 * This gives an unprivileged user the potential to enter other namespaces.
119 */
120static const struct namespace_descr ns_list[] = {
121 { CLONE_NEWUSER, "ns/user", },
122 { CLONE_NEWIPC, "ns/ipc", },
123 { CLONE_NEWUTS, "ns/uts", },
124 { CLONE_NEWNET, "ns/net", },
125 { CLONE_NEWPID, "ns/pid", },
126 { CLONE_NEWNS, "ns/mnt", },
127};
128/*
129 * Upstream nsenter doesn't support the short option for --preserve-credentials
130 */
131static const char opt_str[] = "U::i::u::n::p::m::""t+S+G+r::w::F";
132
133#if ENABLE_FEATURE_NSENTER_LONG_OPTS
134static const char nsenter_longopts[] ALIGN1 =
135 "user\0" Optional_argument "U"
136 "ipc\0" Optional_argument "i"
137 "uts\0" Optional_argument "u"
138 "network\0" Optional_argument "n"
139 "pid\0" Optional_argument "p"
140 "mount\0" Optional_argument "m"
141 "target\0" Required_argument "t"
142 "setuid\0" Required_argument "S"
143 "setgid\0" Required_argument "G"
144 "root\0" Optional_argument "r"
145 "wd\0" Optional_argument "w"
146 "no-fork\0" No_argument "F"
147 "preserve-credentials\0" No_argument "\xff"
148 ;
149#endif
150
151/*
152 * Open a file and return the new descriptor. If a full path is provided in
153 * fs_path, then the file to which it points is opened. Otherwise (fd_path is
154 * NULL) the routine builds a path to a procfs file using the following
155 * template: '/proc/<target_pid>/<target_file>'.
156 */
157static int open_by_path_or_target(const char *path,
158 pid_t target_pid, const char *target_file)
159{
160 char proc_path_buf[sizeof("/proc/%u/1234567890") + sizeof(int)*3];
161
162 if (!path) {
163 if (target_pid == 0) {
164 /* Example:
165 * "nsenter -p PROG" - neither -pFILE nor -tPID given.
166 */
167 bb_show_usage();
168 }
169 snprintf(proc_path_buf, sizeof(proc_path_buf),
170 "/proc/%u/%s", (unsigned)target_pid, target_file);
171 path = proc_path_buf;
172 }
173
174 return xopen(path, O_RDONLY);
175}
176
177int nsenter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
178int nsenter_main(int argc UNUSED_PARAM, char **argv)
179{
180 int i;
181 unsigned int opts;
182 const char *root_dir_str = NULL;
183 const char *wd_str = NULL;
184 struct namespace_ctx ns_ctx_list[NS_COUNT];
185 int setgroups_failed;
186 int root_fd, wd_fd;
187 int target_pid = 0;
188 int uid = 0;
189 int gid = 0;
190
191 memset(ns_ctx_list, 0, sizeof(ns_ctx_list));
192
193 IF_FEATURE_NSENTER_LONG_OPTS(applet_long_options = nsenter_longopts);
194 opts = getopt32(argv, opt_str,
195 &ns_ctx_list[NS_USR_POS].path,
196 &ns_ctx_list[NS_IPC_POS].path,
197 &ns_ctx_list[NS_UTS_POS].path,
198 &ns_ctx_list[NS_NET_POS].path,
199 &ns_ctx_list[NS_PID_POS].path,
200 &ns_ctx_list[NS_MNT_POS].path,
201 &target_pid, &uid, &gid,
202 &root_dir_str, &wd_str
203 );
204 argv += optind;
205
206 root_fd = wd_fd = -1;
207 if (opts & OPT_root)
208 root_fd = open_by_path_or_target(root_dir_str,
209 target_pid, "root");
210 if (opts & OPT_wd)
211 wd_fd = open_by_path_or_target(wd_str, target_pid, "cwd");
212
213 for (i = 0; i < NS_COUNT; i++) {
214 const struct namespace_descr *ns = &ns_list[i];
215 struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
216
217 ns_ctx->fd = -1;
218 if (opts & (1 << i))
219 ns_ctx->fd = open_by_path_or_target(ns_ctx->path,
220 target_pid, ns->ns_nsfile8);
221 }
222
223 /*
224 * Entering the user namespace without --preserve-credentials implies
225 * --setuid & --setgid and clearing root's groups.
226 */
227 setgroups_failed = 0;
228 if ((opts & OPT_user) && !(opts & OPT_prescred)) {
229 opts |= (OPT_setuid | OPT_setgid);
230 /*
231 * We call setgroups() before and after setns() and only
232 * bail-out if it fails twice.
233 */
234 setgroups_failed = (setgroups(0, NULL) < 0);
235 }
236
237 for (i = 0; i < NS_COUNT; i++) {
238 const struct namespace_descr *ns = &ns_list[i];
239 struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
240
241 if (ns_ctx->fd < 0)
242 continue;
243 if (setns(ns_ctx->fd, ns->flag)) {
244 bb_perror_msg_and_die(
245 "setns(): can't reassociate to namespace '%s'",
246 ns->ns_nsfile8 + 3 /* skip over "ns/" */
247 );
248 }
249 close(ns_ctx->fd); /* should close fds, to not confuse exec'ed PROG */
250 /*ns_ctx->fd = -1;*/
251 }
252
253 if (root_fd >= 0) {
254 if (wd_fd < 0) {
255 /*
256 * Save the current working directory if we're not
257 * changing it.
258 */
259 wd_fd = xopen(".", O_RDONLY);
260 }
261 xfchdir(root_fd);
262 xchroot(".");
263 close(root_fd);
264 /*root_fd = -1;*/
265 }
266
267 if (wd_fd >= 0) {
268 xfchdir(wd_fd);
269 close(wd_fd);
270 /*wd_fd = -1;*/
271 }
272
273 /*
274 * Entering the pid namespace implies forking unless it's been
275 * explicitly requested by the user not to.
276 */
277 if (!(opts & OPT_nofork) && (opts & OPT_pid)) {
278 xvfork_parent_waits_and_exits();
279 /* Child continues */
280 }
281
282 if (opts & OPT_setgid) {
283 if (setgroups(0, NULL) < 0 && setgroups_failed)
284 bb_perror_msg_and_die("setgroups");
285 xsetgid(gid);
286 }
287 if (opts & OPT_setuid)
288 xsetuid(uid);
289
290 exec_prog_or_SHELL(argv);
291}
diff --git a/util-linux/umount.c b/util-linux/umount.c
index 00910977d..30bef1686 100644
--- a/util-linux/umount.c
+++ b/util-linux/umount.c
@@ -30,6 +30,9 @@
30 30
31#include <mntent.h> 31#include <mntent.h>
32#include <sys/mount.h> 32#include <sys/mount.h>
33#ifndef MNT_DETACH
34# define MNT_DETACH 0x00000002
35#endif
33#include "libbb.h" 36#include "libbb.h"
34 37
35#if defined(__dietlibc__) 38#if defined(__dietlibc__)
diff --git a/util-linux/unshare.c b/util-linux/unshare.c
new file mode 100644
index 000000000..d05cfdb6c
--- /dev/null
+++ b/util-linux/unshare.c
@@ -0,0 +1,380 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini unshare implementation for busybox.
4 *
5 * Copyright (C) 2016 by Bartosz Golaszewski <bartekgola@gmail.com>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10//config:config UNSHARE
11//config: bool "unshare"
12//config: default y
13//config: depends on LONG_OPTS && !NOMMU
14//config: select PLATFORM_LINUX
15//config: help
16//config: Run program with some namespaces unshared from parent.
17
18// depends on LONG_OPTS: it is awkward to exclude code which handles --propagation
19// and --setgroups based on LONG_OPTS, so instead applet requires LONG_OPTS.
20// depends on !NOMMU: we need fork()
21
22//applet:IF_UNSHARE(APPLET(unshare, BB_DIR_USR_BIN, BB_SUID_DROP))
23
24//kbuild:lib-$(CONFIG_UNSHARE) += unshare.o
25
26//usage:#define unshare_trivial_usage
27//usage: "[OPTIONS] [PROG [ARGS]]"
28//usage:#define unshare_full_usage "\n"
29//usage: "\n -m, --mount[=FILE] Unshare mount namespace"
30//usage: "\n -u, --uts[=FILE] Unshare UTS namespace (hostname etc.)"
31//usage: "\n -i, --ipc[=FILE] Unshare System V IPC namespace"
32//usage: "\n -n, --net[=FILE] Unshare network namespace"
33//usage: "\n -p, --pid[=FILE] Unshare PID namespace"
34//usage: "\n -U, --user[=FILE} Unshare user namespace"
35//usage: "\n -f, --fork Fork before execing PROG"
36//usage: "\n -r, --map-root-user Map current user to root (implies -u)"
37//usage: "\n --mount-proc[=DIR] Mount /proc filesystem first (implies -m)"
38//usage: "\n --propagation slave|shared|private|unchanged"
39//usage: "\n Modify mount propagation in mount namespace"
40//usage: "\n --setgroups allow|deny Control the setgroups syscall in user namespaces"
41
42#include <sched.h>
43#ifndef CLONE_NEWUTS
44# define CLONE_NEWUTS 0x04000000
45#endif
46#ifndef CLONE_NEWIPC
47# define CLONE_NEWIPC 0x08000000
48#endif
49#ifndef CLONE_NEWUSER
50# define CLONE_NEWUSER 0x10000000
51#endif
52#ifndef CLONE_NEWPID
53# define CLONE_NEWPID 0x20000000
54#endif
55#ifndef CLONE_NEWNET
56# define CLONE_NEWNET 0x40000000
57#endif
58
59#include <sys/mount.h>
60#ifndef MS_REC
61# define MS_REC (1 << 14)
62#endif
63#ifndef MS_PRIVATE
64# define MS_PRIVATE (1 << 18)
65#endif
66#ifndef MS_SLAVE
67# define MS_SLAVE (1 << 19)
68#endif
69#ifndef MS_SHARED
70# define MS_SHARED (1 << 20)
71#endif
72
73#include "libbb.h"
74
75static void mount_or_die(const char *source, const char *target,
76 const char *fstype, unsigned long mountflags)
77{
78 if (mount(source, target, fstype, mountflags, NULL)) {
79 bb_perror_msg_and_die("can't mount %s on %s (flags:0x%lx)",
80 source, target, mountflags);
81 /* fstype is always either NULL or "proc".
82 * "proc" is only used to mount /proc.
83 * No need to clutter up error message with fstype,
84 * it is easily deductible.
85 */
86 }
87}
88
89#define PATH_PROC_SETGROUPS "/proc/self/setgroups"
90#define PATH_PROC_UIDMAP "/proc/self/uid_map"
91#define PATH_PROC_GIDMAP "/proc/self/gid_map"
92
93struct namespace_descr {
94 int flag;
95 const char nsfile4[4];
96};
97
98struct namespace_ctx {
99 char *path;
100};
101
102enum {
103 OPT_mount = 1 << 0,
104 OPT_uts = 1 << 1,
105 OPT_ipc = 1 << 2,
106 OPT_network = 1 << 3,
107 OPT_pid = 1 << 4,
108 OPT_user = 1 << 5, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */
109 OPT_fork = 1 << 6,
110 OPT_map_root = 1 << 7,
111 OPT_mount_proc = 1 << 8,
112 OPT_propagation = 1 << 9,
113 OPT_setgroups = 1 << 10,
114};
115enum {
116 NS_MNT_POS = 0,
117 NS_UTS_POS,
118 NS_IPC_POS,
119 NS_NET_POS,
120 NS_PID_POS,
121 NS_USR_POS, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */
122 NS_COUNT,
123};
124static const struct namespace_descr ns_list[] = {
125 { CLONE_NEWNS, "mnt" },
126 { CLONE_NEWUTS, "uts" },
127 { CLONE_NEWIPC, "ipc" },
128 { CLONE_NEWNET, "net" },
129 { CLONE_NEWPID, "pid" },
130 { CLONE_NEWUSER, "user" }, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */
131};
132
133/*
134 * Upstream unshare doesn't support short options for --mount-proc,
135 * --propagation, --setgroups.
136 * Optional arguments (namespace mountpoints) exist only for long opts,
137 * we are forced to use "fake" letters for them.
138 * '+': stop at first non-option.
139 */
140static const char opt_str[] = "+muinpU""fr""\xfd::""\xfe:""\xff:";
141static const char unshare_longopts[] ALIGN1 =
142 "mount\0" Optional_argument "\xf0"
143 "uts\0" Optional_argument "\xf1"
144 "ipc\0" Optional_argument "\xf2"
145 "network\0" Optional_argument "\xf3"
146 "pid\0" Optional_argument "\xf4"
147 "user\0" Optional_argument "\xf5"
148 "fork\0" No_argument "f"
149 "map-root-user\0" No_argument "r"
150 "mount-proc\0" Optional_argument "\xfd"
151 "propagation\0" Required_argument "\xfe"
152 "setgroups\0" Required_argument "\xff"
153;
154
155/* Ugly-looking string reuse trick */
156#define PRIVATE_STR "private\0""unchanged\0""shared\0""slave\0"
157#define PRIVATE_UNCHANGED_SHARED_SLAVE PRIVATE_STR
158
159static unsigned long parse_propagation(const char *prop_str)
160{
161 int i = index_in_strings(PRIVATE_UNCHANGED_SHARED_SLAVE, prop_str);
162 if (i < 0)
163 bb_error_msg_and_die("unrecognized: --%s=%s", "propagation", prop_str);
164 if (i == 0)
165 return MS_REC | MS_PRIVATE;
166 if (i == 1)
167 return 0;
168 if (i == 2)
169 return MS_REC | MS_SHARED;
170 return MS_REC | MS_SLAVE;
171}
172
173static void mount_namespaces(pid_t pid, struct namespace_ctx *ns_ctx_list)
174{
175 const struct namespace_descr *ns;
176 struct namespace_ctx *ns_ctx;
177 int i;
178
179 for (i = 0; i < NS_COUNT; i++) {
180 char nsf[sizeof("/proc/%u/ns/AAAA") + sizeof(int)*3];
181
182 ns = &ns_list[i];
183 ns_ctx = &ns_ctx_list[i];
184 if (!ns_ctx->path)
185 continue;
186 sprintf(nsf, "/proc/%u/ns/%.4s", (unsigned)pid, ns->nsfile4);
187 mount_or_die(nsf, ns_ctx->path, NULL, MS_BIND);
188 }
189}
190
191int unshare_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
192int unshare_main(int argc UNUSED_PARAM, char **argv)
193{
194 int i;
195 unsigned int opts;
196 int unsflags;
197 uintptr_t need_mount;
198 const char *proc_mnt_target;
199 const char *prop_str;
200 const char *setgrp_str;
201 unsigned long prop_flags;
202 uid_t reuid = geteuid();
203 gid_t regid = getegid();
204 struct fd_pair fdp;
205 pid_t child = child; /* for compiler */
206 struct namespace_ctx ns_ctx_list[NS_COUNT];
207
208 memset(ns_ctx_list, 0, sizeof(ns_ctx_list));
209 proc_mnt_target = "/proc";
210 prop_str = PRIVATE_STR;
211 setgrp_str = NULL;
212
213 opt_complementary =
214 "\xf0""m" /* long opts (via their "fake chars") imply short opts */
215 ":\xf1""u"
216 ":\xf2""i"
217 ":\xf3""n"
218 ":\xf4""p"
219 ":\xf5""U"
220 ":ru" /* --map-root-user or -r implies -u */
221 ":\xfd""m" /* --mount-proc implies -m */
222 ;
223 applet_long_options = unshare_longopts;
224 opts = getopt32(argv, opt_str,
225 &proc_mnt_target, &prop_str, &setgrp_str,
226 &ns_ctx_list[NS_MNT_POS].path,
227 &ns_ctx_list[NS_UTS_POS].path,
228 &ns_ctx_list[NS_IPC_POS].path,
229 &ns_ctx_list[NS_NET_POS].path,
230 &ns_ctx_list[NS_PID_POS].path,
231 &ns_ctx_list[NS_USR_POS].path
232 );
233 argv += optind;
234 //bb_error_msg("opts:0x%x", opts);
235 //bb_error_msg("mount:%s", ns_ctx_list[NS_MNT_POS].path);
236 //bb_error_msg("proc_mnt_target:%s", proc_mnt_target);
237 //bb_error_msg("prop_str:%s", prop_str);
238 //bb_error_msg("setgrp_str:%s", setgrp_str);
239 //exit(1);
240
241 if (setgrp_str) {
242 if (strcmp(setgrp_str, "allow") == 0) {
243 if (opts & OPT_map_root) {
244 bb_error_msg_and_die(
245 "--setgroups=allow and --map-root-user "
246 "are mutually exclusive"
247 );
248 }
249 } else {
250 /* It's not "allow", must be "deny" */
251 if (strcmp(setgrp_str, "deny") != 0)
252 bb_error_msg_and_die("unrecognized: --%s=%s",
253 "setgroups", setgrp_str);
254 }
255 }
256
257 unsflags = 0;
258 need_mount = 0;
259 for (i = 0; i < NS_COUNT; i++) {
260 const struct namespace_descr *ns = &ns_list[i];
261 struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
262
263 if (opts & (1 << i))
264 unsflags |= ns->flag;
265
266 need_mount |= (uintptr_t)(ns_ctx->path);
267 }
268 /* need_mount != 0 if at least one FILE was given */
269
270 prop_flags = MS_REC | MS_PRIVATE;
271 /* Silently ignore --propagation if --mount is not requested. */
272 if (opts & OPT_mount)
273 prop_flags = parse_propagation(prop_str);
274
275 /*
276 * Special case: if we were requested to unshare the mount namespace
277 * AND to make any namespace persistent (by bind mounting it) we need
278 * to spawn a child process which will wait for the parent to call
279 * unshare(), then mount parent's namespaces while still in the
280 * previous namespace.
281 */
282 fdp.wr = -1;
283 if (need_mount && (opts & OPT_mount)) {
284 /*
285 * Can't use getppid() in child, as we can be unsharing the
286 * pid namespace.
287 */
288 pid_t ppid = getpid();
289
290 xpiped_pair(fdp);
291
292 child = xfork();
293 if (child == 0) {
294 /* Child */
295 close(fdp.wr);
296
297 /* Wait until parent calls unshare() */
298 read(fdp.rd, ns_ctx_list, 1); /* ...using bogus buffer */
299 /*close(fdp.rd);*/
300
301 /* Mount parent's unshared namespaces. */
302 mount_namespaces(ppid, ns_ctx_list);
303 return EXIT_SUCCESS;
304 }
305 /* Parent continues */
306 }
307
308 if (unshare(unsflags) != 0)
309 bb_perror_msg_and_die("unshare(0x%x)", unsflags);
310
311 if (fdp.wr >= 0) {
312 close(fdp.wr); /* Release child */
313 close(fdp.rd); /* should close fd, to not confuse exec'ed PROG */
314 }
315
316 if (need_mount) {
317 /* Wait for the child to finish mounting the namespaces. */
318 if (opts & OPT_mount) {
319 int exit_status = wait_for_exitstatus(child);
320 if (WIFEXITED(exit_status) &&
321 WEXITSTATUS(exit_status) != EXIT_SUCCESS)
322 return WEXITSTATUS(exit_status);
323 } else {
324 /*
325 * Regular way - we were requested to mount some other
326 * namespaces: mount them after the call to unshare().
327 */
328 mount_namespaces(getpid(), ns_ctx_list);
329 }
330 }
331
332 /*
333 * When we're unsharing the pid namespace, it's not the process that
334 * calls unshare() that is put into the new namespace, but its first
335 * child. The user may want to use this option to spawn a new process
336 * that'll become PID 1 in this new namespace.
337 */
338 if (opts & OPT_fork) {
339 xvfork_parent_waits_and_exits();
340 /* Child continues */
341 }
342
343 if (opts & OPT_map_root) {
344 char uidmap_buf[sizeof("%u 0 1") + sizeof(int)*3];
345
346 /*
347 * Since Linux 3.19 unprivileged writing of /proc/self/gid_map
348 * has been disabled unless /proc/self/setgroups is written
349 * first to permanently disable the ability to call setgroups
350 * in that user namespace.
351 */
352 xopen_xwrite_close(PATH_PROC_SETGROUPS, "deny");
353 sprintf(uidmap_buf, "%u 0 1", (unsigned)reuid);
354 xopen_xwrite_close(PATH_PROC_UIDMAP, uidmap_buf);
355 sprintf(uidmap_buf, "%u 0 1", (unsigned)regid);
356 xopen_xwrite_close(PATH_PROC_GIDMAP, uidmap_buf);
357 } else
358 if (setgrp_str) {
359 /* Write "allow" or "deny" */
360 xopen_xwrite_close(PATH_PROC_SETGROUPS, setgrp_str);
361 }
362
363 if (opts & OPT_mount) {
364 mount_or_die("none", "/", NULL, prop_flags);
365 }
366
367 if (opts & OPT_mount_proc) {
368 /*
369 * When creating a new pid namespace, we might want the pid
370 * subdirectories in /proc to remain consistent with the new
371 * process IDs. Without --mount-proc the pids in /proc would
372 * still reflect the old pid namespace. This is why we make
373 * /proc private here and then do a fresh mount.
374 */
375 mount_or_die("none", proc_mnt_target, NULL, MS_PRIVATE | MS_REC);
376 mount_or_die("proc", proc_mnt_target, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV);
377 }
378
379 exec_prog_or_SHELL(argv);
380}