1*dbd5678dSMartin Matuska /*
2*dbd5678dSMartin Matuska * CDDL HEADER START
3*dbd5678dSMartin Matuska *
4*dbd5678dSMartin Matuska * The contents of this file are subject to the terms of the
5*dbd5678dSMartin Matuska * Common Development and Distribution License (the "License").
6*dbd5678dSMartin Matuska * You may not use this file except in compliance with the License.
7*dbd5678dSMartin Matuska *
8*dbd5678dSMartin Matuska * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*dbd5678dSMartin Matuska * or https://opensource.org/licenses/CDDL-1.0.
10*dbd5678dSMartin Matuska * See the License for the specific language governing permissions
11*dbd5678dSMartin Matuska * and limitations under the License.
12*dbd5678dSMartin Matuska *
13*dbd5678dSMartin Matuska * When distributing Covered Code, include this CDDL HEADER in each
14*dbd5678dSMartin Matuska * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*dbd5678dSMartin Matuska * If applicable, add the following below this CDDL HEADER, with the
16*dbd5678dSMartin Matuska * fields enclosed by brackets "[]" replaced with your own identifying
17*dbd5678dSMartin Matuska * information: Portions Copyright [yyyy] [name of copyright owner]
18*dbd5678dSMartin Matuska *
19*dbd5678dSMartin Matuska * CDDL HEADER END
20*dbd5678dSMartin Matuska */
21*dbd5678dSMartin Matuska
22*dbd5678dSMartin Matuska #ifndef _GNU_SOURCE
23*dbd5678dSMartin Matuska #define _GNU_SOURCE
24*dbd5678dSMartin Matuska #endif
25*dbd5678dSMartin Matuska
26*dbd5678dSMartin Matuska #include <stdio.h>
27*dbd5678dSMartin Matuska #include <stdlib.h>
28*dbd5678dSMartin Matuska #include <stdbool.h>
29*dbd5678dSMartin Matuska #include <stddef.h>
30*dbd5678dSMartin Matuska #include <string.h>
31*dbd5678dSMartin Matuska #include <linux/types.h>
32*dbd5678dSMartin Matuska #include <sys/wait.h>
33*dbd5678dSMartin Matuska #include <sys/stat.h>
34*dbd5678dSMartin Matuska #include <sys/mount.h>
35*dbd5678dSMartin Matuska #include <fcntl.h>
36*dbd5678dSMartin Matuska #include <errno.h>
37*dbd5678dSMartin Matuska #include <sched.h>
38*dbd5678dSMartin Matuska #include <syscall.h>
39*dbd5678dSMartin Matuska #include <sys/socket.h>
40*dbd5678dSMartin Matuska
41*dbd5678dSMartin Matuska #include <sys/list.h>
42*dbd5678dSMartin Matuska
43*dbd5678dSMartin Matuska #ifndef UINT_MAX
44*dbd5678dSMartin Matuska #define UINT_MAX 4294967295U
45*dbd5678dSMartin Matuska #endif
46*dbd5678dSMartin Matuska
47*dbd5678dSMartin Matuska #ifndef __NR_Linux
48*dbd5678dSMartin Matuska #if defined __alpha__
49*dbd5678dSMartin Matuska #define __NR_Linux 110
50*dbd5678dSMartin Matuska #elif defined _MIPS_SIM
51*dbd5678dSMartin Matuska #if _MIPS_SIM == _MIPS_SIM_ABI32
52*dbd5678dSMartin Matuska #define __NR_Linux 4000
53*dbd5678dSMartin Matuska #endif
54*dbd5678dSMartin Matuska #if _MIPS_SIM == _MIPS_SIM_NABI32
55*dbd5678dSMartin Matuska #define __NR_Linux 6000
56*dbd5678dSMartin Matuska #endif
57*dbd5678dSMartin Matuska #if _MIPS_SIM == _MIPS_SIM_ABI64
58*dbd5678dSMartin Matuska #define __NR_Linux 5000
59*dbd5678dSMartin Matuska #endif
60*dbd5678dSMartin Matuska #elif defined __ia64__
61*dbd5678dSMartin Matuska #define __NR_Linux 1024
62*dbd5678dSMartin Matuska #else
63*dbd5678dSMartin Matuska #define __NR_Linux 0
64*dbd5678dSMartin Matuska #endif
65*dbd5678dSMartin Matuska #endif
66*dbd5678dSMartin Matuska
67*dbd5678dSMartin Matuska #ifndef __NR_mount_setattr
68*dbd5678dSMartin Matuska #define __NR_mount_setattr (442 + __NR_Linux)
69*dbd5678dSMartin Matuska #endif
70*dbd5678dSMartin Matuska
71*dbd5678dSMartin Matuska #ifndef __NR_open_tree
72*dbd5678dSMartin Matuska #define __NR_open_tree (428 + __NR_Linux)
73*dbd5678dSMartin Matuska #endif
74*dbd5678dSMartin Matuska
75*dbd5678dSMartin Matuska #ifndef __NR_move_mount
76*dbd5678dSMartin Matuska #define __NR_move_mount (429 + __NR_Linux)
77*dbd5678dSMartin Matuska #endif
78*dbd5678dSMartin Matuska
79*dbd5678dSMartin Matuska #ifndef MNT_DETACH
80*dbd5678dSMartin Matuska #define MNT_DETACH 2
81*dbd5678dSMartin Matuska #endif
82*dbd5678dSMartin Matuska
83*dbd5678dSMartin Matuska #ifndef MOVE_MOUNT_F_EMPTY_PATH
84*dbd5678dSMartin Matuska #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004
85*dbd5678dSMartin Matuska #endif
86*dbd5678dSMartin Matuska
87*dbd5678dSMartin Matuska #ifndef MOUNT_ATTR_IDMAP
88*dbd5678dSMartin Matuska #define MOUNT_ATTR_IDMAP 0x00100000
89*dbd5678dSMartin Matuska #endif
90*dbd5678dSMartin Matuska
91*dbd5678dSMartin Matuska #ifndef OPEN_TREE_CLONE
92*dbd5678dSMartin Matuska #define OPEN_TREE_CLONE 1
93*dbd5678dSMartin Matuska #endif
94*dbd5678dSMartin Matuska
95*dbd5678dSMartin Matuska #ifndef OPEN_TREE_CLOEXEC
96*dbd5678dSMartin Matuska #define OPEN_TREE_CLOEXEC O_CLOEXEC
97*dbd5678dSMartin Matuska #endif
98*dbd5678dSMartin Matuska
99*dbd5678dSMartin Matuska #ifndef AT_RECURSIVE
100*dbd5678dSMartin Matuska #define AT_RECURSIVE 0x8000
101*dbd5678dSMartin Matuska #endif
102*dbd5678dSMartin Matuska
103*dbd5678dSMartin Matuska typedef struct {
104*dbd5678dSMartin Matuska __u64 attr_set;
105*dbd5678dSMartin Matuska __u64 attr_clr;
106*dbd5678dSMartin Matuska __u64 propagation;
107*dbd5678dSMartin Matuska __u64 userns_fd;
108*dbd5678dSMartin Matuska } mount_attr_t;
109*dbd5678dSMartin Matuska
110*dbd5678dSMartin Matuska static inline int
sys_mount_setattr(int dfd,const char * path,unsigned int flags,mount_attr_t * attr,size_t size)111*dbd5678dSMartin Matuska sys_mount_setattr(int dfd, const char *path, unsigned int flags,
112*dbd5678dSMartin Matuska mount_attr_t *attr, size_t size)
113*dbd5678dSMartin Matuska {
114*dbd5678dSMartin Matuska return (syscall(__NR_mount_setattr, dfd, path, flags, attr, size));
115*dbd5678dSMartin Matuska }
116*dbd5678dSMartin Matuska
117*dbd5678dSMartin Matuska static inline int
sys_open_tree(int dfd,const char * filename,unsigned int flags)118*dbd5678dSMartin Matuska sys_open_tree(int dfd, const char *filename, unsigned int flags)
119*dbd5678dSMartin Matuska {
120*dbd5678dSMartin Matuska return (syscall(__NR_open_tree, dfd, filename, flags));
121*dbd5678dSMartin Matuska }
122*dbd5678dSMartin Matuska
sys_move_mount(int from_dfd,const char * from_pathname,int to_dfd,const char * to_pathname,unsigned int flags)123*dbd5678dSMartin Matuska static inline int sys_move_mount(int from_dfd, const char *from_pathname,
124*dbd5678dSMartin Matuska int to_dfd, const char *to_pathname, unsigned int flags)
125*dbd5678dSMartin Matuska {
126*dbd5678dSMartin Matuska return (syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,
127*dbd5678dSMartin Matuska to_pathname, flags));
128*dbd5678dSMartin Matuska }
129*dbd5678dSMartin Matuska
130*dbd5678dSMartin Matuska typedef enum idmap_type_t {
131*dbd5678dSMartin Matuska TYPE_UID,
132*dbd5678dSMartin Matuska TYPE_GID,
133*dbd5678dSMartin Matuska TYPE_BOTH
134*dbd5678dSMartin Matuska } idmap_type_t;
135*dbd5678dSMartin Matuska
136*dbd5678dSMartin Matuska struct idmap_entry {
137*dbd5678dSMartin Matuska __u32 first;
138*dbd5678dSMartin Matuska __u32 lower_first;
139*dbd5678dSMartin Matuska __u32 count;
140*dbd5678dSMartin Matuska idmap_type_t type;
141*dbd5678dSMartin Matuska list_node_t node;
142*dbd5678dSMartin Matuska };
143*dbd5678dSMartin Matuska
144*dbd5678dSMartin Matuska static void
log_msg(const char * msg,...)145*dbd5678dSMartin Matuska log_msg(const char *msg, ...)
146*dbd5678dSMartin Matuska {
147*dbd5678dSMartin Matuska va_list ap;
148*dbd5678dSMartin Matuska
149*dbd5678dSMartin Matuska va_start(ap, msg);
150*dbd5678dSMartin Matuska vfprintf(stderr, msg, ap);
151*dbd5678dSMartin Matuska fputc('\n', stderr);
152*dbd5678dSMartin Matuska va_end(ap);
153*dbd5678dSMartin Matuska }
154*dbd5678dSMartin Matuska
155*dbd5678dSMartin Matuska #define log_errno(msg, args...) \
156*dbd5678dSMartin Matuska do { \
157*dbd5678dSMartin Matuska log_msg("%s:%d:%s: [%m] " msg, __FILE__, __LINE__,\
158*dbd5678dSMartin Matuska __FUNCTION__, ##args); \
159*dbd5678dSMartin Matuska } while (0)
160*dbd5678dSMartin Matuska
161*dbd5678dSMartin Matuska /*
162*dbd5678dSMartin Matuska * Parse the idmapping in the following format
163*dbd5678dSMartin Matuska * and add to the list:
164*dbd5678dSMartin Matuska *
165*dbd5678dSMartin Matuska * u:nsid_first:hostid_first:count
166*dbd5678dSMartin Matuska * g:nsid_first:hostid_first:count
167*dbd5678dSMartin Matuska * b:nsid_first:hostid_first:count
168*dbd5678dSMartin Matuska *
169*dbd5678dSMartin Matuska * The delimiter can be : or space character.
170*dbd5678dSMartin Matuska *
171*dbd5678dSMartin Matuska * Return:
172*dbd5678dSMartin Matuska * 0 if success
173*dbd5678dSMartin Matuska * ENOMEM if out of memory
174*dbd5678dSMartin Matuska * EINVAL if wrong arg or input
175*dbd5678dSMartin Matuska */
176*dbd5678dSMartin Matuska static int
parse_idmap_entry(list_t * head,char * input)177*dbd5678dSMartin Matuska parse_idmap_entry(list_t *head, char *input)
178*dbd5678dSMartin Matuska {
179*dbd5678dSMartin Matuska char *token, *savedptr = NULL;
180*dbd5678dSMartin Matuska struct idmap_entry *entry;
181*dbd5678dSMartin Matuska unsigned long ul;
182*dbd5678dSMartin Matuska char *delimiter = (char *)": ";
183*dbd5678dSMartin Matuska char c;
184*dbd5678dSMartin Matuska
185*dbd5678dSMartin Matuska if (!input || !head)
186*dbd5678dSMartin Matuska return (EINVAL);
187*dbd5678dSMartin Matuska entry = malloc(sizeof (*entry));
188*dbd5678dSMartin Matuska if (!entry)
189*dbd5678dSMartin Matuska return (ENOMEM);
190*dbd5678dSMartin Matuska
191*dbd5678dSMartin Matuska token = strtok_r(input, delimiter, &savedptr);
192*dbd5678dSMartin Matuska if (token)
193*dbd5678dSMartin Matuska c = token[0];
194*dbd5678dSMartin Matuska if (!token || (c != 'b' && c != 'u' && c != 'g'))
195*dbd5678dSMartin Matuska goto errout;
196*dbd5678dSMartin Matuska entry->type = (c == 'b') ? TYPE_BOTH :
197*dbd5678dSMartin Matuska ((c == 'u') ? TYPE_UID : TYPE_GID);
198*dbd5678dSMartin Matuska
199*dbd5678dSMartin Matuska token = strtok_r(NULL, delimiter, &savedptr);
200*dbd5678dSMartin Matuska if (!token)
201*dbd5678dSMartin Matuska goto errout;
202*dbd5678dSMartin Matuska ul = strtoul(token, NULL, 10);
203*dbd5678dSMartin Matuska if (ul > UINT_MAX || errno != 0)
204*dbd5678dSMartin Matuska goto errout;
205*dbd5678dSMartin Matuska entry->first = (__u32)ul;
206*dbd5678dSMartin Matuska
207*dbd5678dSMartin Matuska token = strtok_r(NULL, delimiter, &savedptr);
208*dbd5678dSMartin Matuska if (!token)
209*dbd5678dSMartin Matuska goto errout;
210*dbd5678dSMartin Matuska ul = strtoul(token, NULL, 10);
211*dbd5678dSMartin Matuska if (ul > UINT_MAX || errno != 0)
212*dbd5678dSMartin Matuska goto errout;
213*dbd5678dSMartin Matuska entry->lower_first = (__u32)ul;
214*dbd5678dSMartin Matuska
215*dbd5678dSMartin Matuska token = strtok_r(NULL, delimiter, &savedptr);
216*dbd5678dSMartin Matuska if (!token)
217*dbd5678dSMartin Matuska goto errout;
218*dbd5678dSMartin Matuska ul = strtoul(token, NULL, 10);
219*dbd5678dSMartin Matuska if (ul > UINT_MAX || errno != 0)
220*dbd5678dSMartin Matuska goto errout;
221*dbd5678dSMartin Matuska entry->count = (__u32)ul;
222*dbd5678dSMartin Matuska
223*dbd5678dSMartin Matuska list_insert_tail(head, entry);
224*dbd5678dSMartin Matuska
225*dbd5678dSMartin Matuska return (0);
226*dbd5678dSMartin Matuska
227*dbd5678dSMartin Matuska errout:
228*dbd5678dSMartin Matuska free(entry);
229*dbd5678dSMartin Matuska return (EINVAL);
230*dbd5678dSMartin Matuska }
231*dbd5678dSMartin Matuska
232*dbd5678dSMartin Matuska /*
233*dbd5678dSMartin Matuska * Release all the entries in the list
234*dbd5678dSMartin Matuska */
235*dbd5678dSMartin Matuska static void
free_idmap(list_t * head)236*dbd5678dSMartin Matuska free_idmap(list_t *head)
237*dbd5678dSMartin Matuska {
238*dbd5678dSMartin Matuska struct idmap_entry *entry;
239*dbd5678dSMartin Matuska
240*dbd5678dSMartin Matuska while ((entry = list_remove_head(head)) != NULL)
241*dbd5678dSMartin Matuska free(entry);
242*dbd5678dSMartin Matuska /* list_destroy() to be done by the caller */
243*dbd5678dSMartin Matuska }
244*dbd5678dSMartin Matuska
245*dbd5678dSMartin Matuska /*
246*dbd5678dSMartin Matuska * Write all bytes in the buffer to fd
247*dbd5678dSMartin Matuska */
248*dbd5678dSMartin Matuska static ssize_t
write_buf(int fd,const char * buf,size_t buf_size)249*dbd5678dSMartin Matuska write_buf(int fd, const char *buf, size_t buf_size)
250*dbd5678dSMartin Matuska {
251*dbd5678dSMartin Matuska ssize_t written, total_written = 0;
252*dbd5678dSMartin Matuska size_t remaining = buf_size;
253*dbd5678dSMartin Matuska char *position = (char *)buf;
254*dbd5678dSMartin Matuska
255*dbd5678dSMartin Matuska for (;;) {
256*dbd5678dSMartin Matuska written = write(fd, position, remaining);
257*dbd5678dSMartin Matuska if (written < 0 && errno == EINTR)
258*dbd5678dSMartin Matuska continue;
259*dbd5678dSMartin Matuska if (written < 0) {
260*dbd5678dSMartin Matuska log_errno("write");
261*dbd5678dSMartin Matuska return (written);
262*dbd5678dSMartin Matuska }
263*dbd5678dSMartin Matuska total_written += written;
264*dbd5678dSMartin Matuska if (total_written == buf_size)
265*dbd5678dSMartin Matuska break;
266*dbd5678dSMartin Matuska remaining -= written;
267*dbd5678dSMartin Matuska position += written;
268*dbd5678dSMartin Matuska }
269*dbd5678dSMartin Matuska
270*dbd5678dSMartin Matuska return (total_written);
271*dbd5678dSMartin Matuska }
272*dbd5678dSMartin Matuska
273*dbd5678dSMartin Matuska /*
274*dbd5678dSMartin Matuska * Read data from file into buffer
275*dbd5678dSMartin Matuska */
276*dbd5678dSMartin Matuska static ssize_t
read_buf(int fd,char * buf,size_t buf_size)277*dbd5678dSMartin Matuska read_buf(int fd, char *buf, size_t buf_size)
278*dbd5678dSMartin Matuska {
279*dbd5678dSMartin Matuska int ret;
280*dbd5678dSMartin Matuska for (;;) {
281*dbd5678dSMartin Matuska ret = read(fd, buf, buf_size);
282*dbd5678dSMartin Matuska if (ret < 0 && errno == EINTR)
283*dbd5678dSMartin Matuska continue;
284*dbd5678dSMartin Matuska break;
285*dbd5678dSMartin Matuska }
286*dbd5678dSMartin Matuska if (ret < 0)
287*dbd5678dSMartin Matuska log_errno("read");
288*dbd5678dSMartin Matuska return (ret);
289*dbd5678dSMartin Matuska }
290*dbd5678dSMartin Matuska
291*dbd5678dSMartin Matuska /*
292*dbd5678dSMartin Matuska * Write idmap of the given type in the buffer to the
293*dbd5678dSMartin Matuska * process' uid_map or gid_map proc file.
294*dbd5678dSMartin Matuska *
295*dbd5678dSMartin Matuska * Return:
296*dbd5678dSMartin Matuska * 0 if success
297*dbd5678dSMartin Matuska * errno if there's any error
298*dbd5678dSMartin Matuska */
299*dbd5678dSMartin Matuska static int
write_idmap(pid_t pid,char * buf,size_t buf_size,idmap_type_t type)300*dbd5678dSMartin Matuska write_idmap(pid_t pid, char *buf, size_t buf_size, idmap_type_t type)
301*dbd5678dSMartin Matuska {
302*dbd5678dSMartin Matuska char path[PATH_MAX];
303*dbd5678dSMartin Matuska int fd = -EBADF;
304*dbd5678dSMartin Matuska int ret;
305*dbd5678dSMartin Matuska
306*dbd5678dSMartin Matuska (void) snprintf(path, sizeof (path), "/proc/%d/%cid_map",
307*dbd5678dSMartin Matuska pid, type == TYPE_UID ? 'u' : 'g');
308*dbd5678dSMartin Matuska fd = open(path, O_WRONLY | O_CLOEXEC);
309*dbd5678dSMartin Matuska if (fd < 0) {
310*dbd5678dSMartin Matuska ret = errno;
311*dbd5678dSMartin Matuska log_errno("open(%s)", path);
312*dbd5678dSMartin Matuska goto out;
313*dbd5678dSMartin Matuska }
314*dbd5678dSMartin Matuska ret = write_buf(fd, buf, buf_size);
315*dbd5678dSMartin Matuska if (ret < 0)
316*dbd5678dSMartin Matuska ret = errno;
317*dbd5678dSMartin Matuska else
318*dbd5678dSMartin Matuska ret = 0;
319*dbd5678dSMartin Matuska out:
320*dbd5678dSMartin Matuska if (fd >= 0)
321*dbd5678dSMartin Matuska close(fd);
322*dbd5678dSMartin Matuska return (ret);
323*dbd5678dSMartin Matuska }
324*dbd5678dSMartin Matuska
325*dbd5678dSMartin Matuska /*
326*dbd5678dSMartin Matuska * Write idmap info in the list to the process
327*dbd5678dSMartin Matuska * user namespace, i.e. its /proc/<pid>/uid_map
328*dbd5678dSMartin Matuska * and /proc/<pid>/gid_map file.
329*dbd5678dSMartin Matuska *
330*dbd5678dSMartin Matuska * Return:
331*dbd5678dSMartin Matuska * 0 if success
332*dbd5678dSMartin Matuska * errno if it fails
333*dbd5678dSMartin Matuska */
334*dbd5678dSMartin Matuska static int
write_pid_idmaps(pid_t pid,list_t * head)335*dbd5678dSMartin Matuska write_pid_idmaps(pid_t pid, list_t *head)
336*dbd5678dSMartin Matuska {
337*dbd5678dSMartin Matuska char *buf_uids, *buf_gids;
338*dbd5678dSMartin Matuska char *curr_bufu, *curr_bufg;
339*dbd5678dSMartin Matuska /* max 4k to be allowed for each map */
340*dbd5678dSMartin Matuska int size_buf_uids = 4096, size_buf_gids = 4096;
341*dbd5678dSMartin Matuska struct idmap_entry *entry;
342*dbd5678dSMartin Matuska int uid_filled, gid_filled;
343*dbd5678dSMartin Matuska int ret = 0;
344*dbd5678dSMartin Matuska int has_uids = 0, has_gids = 0;
345*dbd5678dSMartin Matuska size_t buf_size;
346*dbd5678dSMartin Matuska
347*dbd5678dSMartin Matuska buf_uids = malloc(size_buf_uids);
348*dbd5678dSMartin Matuska if (!buf_uids)
349*dbd5678dSMartin Matuska return (ENOMEM);
350*dbd5678dSMartin Matuska buf_gids = malloc(size_buf_gids);
351*dbd5678dSMartin Matuska if (!buf_gids) {
352*dbd5678dSMartin Matuska free(buf_uids);
353*dbd5678dSMartin Matuska return (ENOMEM);
354*dbd5678dSMartin Matuska }
355*dbd5678dSMartin Matuska curr_bufu = buf_uids;
356*dbd5678dSMartin Matuska curr_bufg = buf_gids;
357*dbd5678dSMartin Matuska
358*dbd5678dSMartin Matuska for (entry = list_head(head); entry; entry = list_next(head, entry)) {
359*dbd5678dSMartin Matuska if (entry->type == TYPE_UID || entry->type == TYPE_BOTH) {
360*dbd5678dSMartin Matuska uid_filled = snprintf(curr_bufu, size_buf_uids,
361*dbd5678dSMartin Matuska "%u %u %u\n", entry->first, entry->lower_first,
362*dbd5678dSMartin Matuska entry->count);
363*dbd5678dSMartin Matuska if (uid_filled <= 0 || uid_filled >= size_buf_uids) {
364*dbd5678dSMartin Matuska ret = E2BIG;
365*dbd5678dSMartin Matuska goto out;
366*dbd5678dSMartin Matuska }
367*dbd5678dSMartin Matuska curr_bufu += uid_filled;
368*dbd5678dSMartin Matuska size_buf_uids -= uid_filled;
369*dbd5678dSMartin Matuska has_uids = 1;
370*dbd5678dSMartin Matuska }
371*dbd5678dSMartin Matuska if (entry->type == TYPE_GID || entry->type == TYPE_BOTH) {
372*dbd5678dSMartin Matuska gid_filled = snprintf(curr_bufg, size_buf_gids,
373*dbd5678dSMartin Matuska "%u %u %u\n", entry->first, entry->lower_first,
374*dbd5678dSMartin Matuska entry->count);
375*dbd5678dSMartin Matuska if (gid_filled <= 0 || gid_filled >= size_buf_gids) {
376*dbd5678dSMartin Matuska ret = E2BIG;
377*dbd5678dSMartin Matuska goto out;
378*dbd5678dSMartin Matuska }
379*dbd5678dSMartin Matuska curr_bufg += gid_filled;
380*dbd5678dSMartin Matuska size_buf_gids -= gid_filled;
381*dbd5678dSMartin Matuska has_gids = 1;
382*dbd5678dSMartin Matuska }
383*dbd5678dSMartin Matuska }
384*dbd5678dSMartin Matuska if (has_uids) {
385*dbd5678dSMartin Matuska buf_size = curr_bufu - buf_uids;
386*dbd5678dSMartin Matuska ret = write_idmap(pid, buf_uids, buf_size, TYPE_UID);
387*dbd5678dSMartin Matuska if (ret)
388*dbd5678dSMartin Matuska goto out;
389*dbd5678dSMartin Matuska }
390*dbd5678dSMartin Matuska if (has_gids) {
391*dbd5678dSMartin Matuska buf_size = curr_bufg - buf_gids;
392*dbd5678dSMartin Matuska ret = write_idmap(pid, buf_gids, buf_size, TYPE_GID);
393*dbd5678dSMartin Matuska }
394*dbd5678dSMartin Matuska
395*dbd5678dSMartin Matuska out:
396*dbd5678dSMartin Matuska free(buf_uids);
397*dbd5678dSMartin Matuska free(buf_gids);
398*dbd5678dSMartin Matuska return (ret);
399*dbd5678dSMartin Matuska }
400*dbd5678dSMartin Matuska
401*dbd5678dSMartin Matuska /*
402*dbd5678dSMartin Matuska * Wait for the child process to exit
403*dbd5678dSMartin Matuska * and reap it.
404*dbd5678dSMartin Matuska *
405*dbd5678dSMartin Matuska * Return:
406*dbd5678dSMartin Matuska * process exit code if available
407*dbd5678dSMartin Matuska */
408*dbd5678dSMartin Matuska static int
wait_for_pid(pid_t pid)409*dbd5678dSMartin Matuska wait_for_pid(pid_t pid)
410*dbd5678dSMartin Matuska {
411*dbd5678dSMartin Matuska int status;
412*dbd5678dSMartin Matuska int ret;
413*dbd5678dSMartin Matuska
414*dbd5678dSMartin Matuska for (;;) {
415*dbd5678dSMartin Matuska ret = waitpid(pid, &status, 0);
416*dbd5678dSMartin Matuska if (ret < 0) {
417*dbd5678dSMartin Matuska if (errno == EINTR)
418*dbd5678dSMartin Matuska continue;
419*dbd5678dSMartin Matuska return (EXIT_FAILURE);
420*dbd5678dSMartin Matuska }
421*dbd5678dSMartin Matuska break;
422*dbd5678dSMartin Matuska }
423*dbd5678dSMartin Matuska if (!WIFEXITED(status))
424*dbd5678dSMartin Matuska return (EXIT_FAILURE);
425*dbd5678dSMartin Matuska return (WEXITSTATUS(status));
426*dbd5678dSMartin Matuska }
427*dbd5678dSMartin Matuska
428*dbd5678dSMartin Matuska /*
429*dbd5678dSMartin Matuska * Get the file descriptor of the process user namespace
430*dbd5678dSMartin Matuska * given its pid.
431*dbd5678dSMartin Matuska *
432*dbd5678dSMartin Matuska * Return:
433*dbd5678dSMartin Matuska * fd if success
434*dbd5678dSMartin Matuska * -1 if it fails
435*dbd5678dSMartin Matuska */
436*dbd5678dSMartin Matuska static int
userns_fd_from_pid(pid_t pid)437*dbd5678dSMartin Matuska userns_fd_from_pid(pid_t pid)
438*dbd5678dSMartin Matuska {
439*dbd5678dSMartin Matuska int fd;
440*dbd5678dSMartin Matuska char path[PATH_MAX];
441*dbd5678dSMartin Matuska
442*dbd5678dSMartin Matuska (void) snprintf(path, sizeof (path), "/proc/%d/ns/user", pid);
443*dbd5678dSMartin Matuska fd = open(path, O_RDONLY | O_CLOEXEC);
444*dbd5678dSMartin Matuska if (fd < 0)
445*dbd5678dSMartin Matuska log_errno("open(%s)", path);
446*dbd5678dSMartin Matuska return (fd);
447*dbd5678dSMartin Matuska }
448*dbd5678dSMartin Matuska
449*dbd5678dSMartin Matuska /*
450*dbd5678dSMartin Matuska * Get the user namespace file descriptor given a list
451*dbd5678dSMartin Matuska * of idmap info.
452*dbd5678dSMartin Matuska *
453*dbd5678dSMartin Matuska * Return:
454*dbd5678dSMartin Matuska * fd if success
455*dbd5678dSMartin Matuska * -errno if it fails
456*dbd5678dSMartin Matuska */
457*dbd5678dSMartin Matuska static int
userns_fd_from_idmap(list_t * head)458*dbd5678dSMartin Matuska userns_fd_from_idmap(list_t *head)
459*dbd5678dSMartin Matuska {
460*dbd5678dSMartin Matuska pid_t pid;
461*dbd5678dSMartin Matuska int ret, fd;
462*dbd5678dSMartin Matuska int fds[2];
463*dbd5678dSMartin Matuska char c;
464*dbd5678dSMartin Matuska int saved_errno = 0;
465*dbd5678dSMartin Matuska
466*dbd5678dSMartin Matuska /* socketpair for bidirectional communication */
467*dbd5678dSMartin Matuska ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
468*dbd5678dSMartin Matuska if (ret) {
469*dbd5678dSMartin Matuska log_errno("socketpair");
470*dbd5678dSMartin Matuska return (-errno);
471*dbd5678dSMartin Matuska }
472*dbd5678dSMartin Matuska
473*dbd5678dSMartin Matuska pid = fork();
474*dbd5678dSMartin Matuska if (pid < 0) {
475*dbd5678dSMartin Matuska log_errno("fork");
476*dbd5678dSMartin Matuska fd = -errno;
477*dbd5678dSMartin Matuska goto out;
478*dbd5678dSMartin Matuska }
479*dbd5678dSMartin Matuska
480*dbd5678dSMartin Matuska if (pid == 0) {
481*dbd5678dSMartin Matuska /* child process */
482*dbd5678dSMartin Matuska ret = unshare(CLONE_NEWUSER);
483*dbd5678dSMartin Matuska if (ret == 0) {
484*dbd5678dSMartin Matuska /* notify the parent of success */
485*dbd5678dSMartin Matuska ret = write_buf(fds[1], "1", 1);
486*dbd5678dSMartin Matuska if (ret < 0)
487*dbd5678dSMartin Matuska saved_errno = errno;
488*dbd5678dSMartin Matuska else {
489*dbd5678dSMartin Matuska /*
490*dbd5678dSMartin Matuska * Until the parent has written to idmap,
491*dbd5678dSMartin Matuska * we cannot exit, otherwise the defunct
492*dbd5678dSMartin Matuska * process is owned by the real root, writing
493*dbd5678dSMartin Matuska * to its idmap ends up with EPERM in the
494*dbd5678dSMartin Matuska * context of a user ns
495*dbd5678dSMartin Matuska */
496*dbd5678dSMartin Matuska ret = read_buf(fds[1], &c, 1);
497*dbd5678dSMartin Matuska if (ret < 0)
498*dbd5678dSMartin Matuska saved_errno = errno;
499*dbd5678dSMartin Matuska }
500*dbd5678dSMartin Matuska } else {
501*dbd5678dSMartin Matuska saved_errno = errno;
502*dbd5678dSMartin Matuska log_errno("unshare");
503*dbd5678dSMartin Matuska ret = write_buf(fds[1], "0", 1);
504*dbd5678dSMartin Matuska if (ret < 0)
505*dbd5678dSMartin Matuska saved_errno = errno;
506*dbd5678dSMartin Matuska }
507*dbd5678dSMartin Matuska exit(saved_errno);
508*dbd5678dSMartin Matuska }
509*dbd5678dSMartin Matuska
510*dbd5678dSMartin Matuska /* parent process */
511*dbd5678dSMartin Matuska ret = read_buf(fds[0], &c, 1);
512*dbd5678dSMartin Matuska if (ret == 1 && c == '1') {
513*dbd5678dSMartin Matuska ret = write_pid_idmaps(pid, head);
514*dbd5678dSMartin Matuska if (!ret) {
515*dbd5678dSMartin Matuska fd = userns_fd_from_pid(pid);
516*dbd5678dSMartin Matuska if (fd < 0)
517*dbd5678dSMartin Matuska fd = -errno;
518*dbd5678dSMartin Matuska } else {
519*dbd5678dSMartin Matuska fd = -ret;
520*dbd5678dSMartin Matuska }
521*dbd5678dSMartin Matuska /* Let child know it can exit */
522*dbd5678dSMartin Matuska (void) write_buf(fds[0], "1", 1);
523*dbd5678dSMartin Matuska } else {
524*dbd5678dSMartin Matuska fd = -EBADF;
525*dbd5678dSMartin Matuska }
526*dbd5678dSMartin Matuska (void) wait_for_pid(pid);
527*dbd5678dSMartin Matuska out:
528*dbd5678dSMartin Matuska close(fds[0]);
529*dbd5678dSMartin Matuska close(fds[1]);
530*dbd5678dSMartin Matuska return (fd);
531*dbd5678dSMartin Matuska }
532*dbd5678dSMartin Matuska
533*dbd5678dSMartin Matuska /*
534*dbd5678dSMartin Matuska * Check if the operating system supports idmapped mount on the
535*dbd5678dSMartin Matuska * given path or not.
536*dbd5678dSMartin Matuska *
537*dbd5678dSMartin Matuska * Return:
538*dbd5678dSMartin Matuska * true if supported
539*dbd5678dSMartin Matuska * false if not supported
540*dbd5678dSMartin Matuska */
541*dbd5678dSMartin Matuska static bool
is_idmap_supported(char * path)542*dbd5678dSMartin Matuska is_idmap_supported(char *path)
543*dbd5678dSMartin Matuska {
544*dbd5678dSMartin Matuska list_t head;
545*dbd5678dSMartin Matuska int ret;
546*dbd5678dSMartin Matuska int tree_fd = -EBADF, path_fd = -EBADF;
547*dbd5678dSMartin Matuska mount_attr_t attr = {
548*dbd5678dSMartin Matuska .attr_set = MOUNT_ATTR_IDMAP,
549*dbd5678dSMartin Matuska .userns_fd = -EBADF,
550*dbd5678dSMartin Matuska };
551*dbd5678dSMartin Matuska
552*dbd5678dSMartin Matuska /* strtok_r() won't be happy with a const string */
553*dbd5678dSMartin Matuska /* To check if idmapped mount can be done in a user ns, map 0 to 0 */
554*dbd5678dSMartin Matuska char *input = strdup("b:0:0:1");
555*dbd5678dSMartin Matuska
556*dbd5678dSMartin Matuska if (!input) {
557*dbd5678dSMartin Matuska errno = ENOMEM;
558*dbd5678dSMartin Matuska log_errno("strdup");
559*dbd5678dSMartin Matuska return (false);
560*dbd5678dSMartin Matuska }
561*dbd5678dSMartin Matuska
562*dbd5678dSMartin Matuska list_create(&head, sizeof (struct idmap_entry),
563*dbd5678dSMartin Matuska offsetof(struct idmap_entry, node));
564*dbd5678dSMartin Matuska ret = parse_idmap_entry(&head, input);
565*dbd5678dSMartin Matuska if (ret) {
566*dbd5678dSMartin Matuska errno = ret;
567*dbd5678dSMartin Matuska log_errno("parse_idmap_entry(%s)", input);
568*dbd5678dSMartin Matuska goto out1;
569*dbd5678dSMartin Matuska }
570*dbd5678dSMartin Matuska ret = userns_fd_from_idmap(&head);
571*dbd5678dSMartin Matuska if (ret < 0)
572*dbd5678dSMartin Matuska goto out1;
573*dbd5678dSMartin Matuska attr.userns_fd = ret;
574*dbd5678dSMartin Matuska ret = openat(-EBADF, path, O_DIRECTORY | O_CLOEXEC);
575*dbd5678dSMartin Matuska if (ret < 0) {
576*dbd5678dSMartin Matuska log_errno("openat(%s)", path);
577*dbd5678dSMartin Matuska goto out;
578*dbd5678dSMartin Matuska }
579*dbd5678dSMartin Matuska path_fd = ret;
580*dbd5678dSMartin Matuska ret = sys_open_tree(path_fd, "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT |
581*dbd5678dSMartin Matuska AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE);
582*dbd5678dSMartin Matuska if (ret < 0) {
583*dbd5678dSMartin Matuska log_errno("sys_open_tree");
584*dbd5678dSMartin Matuska goto out;
585*dbd5678dSMartin Matuska }
586*dbd5678dSMartin Matuska tree_fd = ret;
587*dbd5678dSMartin Matuska ret = sys_mount_setattr(tree_fd, "", AT_EMPTY_PATH, &attr,
588*dbd5678dSMartin Matuska sizeof (attr));
589*dbd5678dSMartin Matuska if (ret < 0) {
590*dbd5678dSMartin Matuska log_errno("sys_mount_setattr");
591*dbd5678dSMartin Matuska }
592*dbd5678dSMartin Matuska out:
593*dbd5678dSMartin Matuska close(attr.userns_fd);
594*dbd5678dSMartin Matuska out1:
595*dbd5678dSMartin Matuska free_idmap(&head);
596*dbd5678dSMartin Matuska list_destroy(&head);
597*dbd5678dSMartin Matuska if (tree_fd >= 0)
598*dbd5678dSMartin Matuska close(tree_fd);
599*dbd5678dSMartin Matuska if (path_fd >= 0)
600*dbd5678dSMartin Matuska close(path_fd);
601*dbd5678dSMartin Matuska free(input);
602*dbd5678dSMartin Matuska return (ret == 0);
603*dbd5678dSMartin Matuska }
604*dbd5678dSMartin Matuska
605*dbd5678dSMartin Matuska /*
606*dbd5678dSMartin Matuska * Check if the given path is a mount point or not.
607*dbd5678dSMartin Matuska *
608*dbd5678dSMartin Matuska * Return:
609*dbd5678dSMartin Matuska * true if it is
610*dbd5678dSMartin Matuska * false otherwise
611*dbd5678dSMartin Matuska */
612*dbd5678dSMartin Matuska static bool
is_mountpoint(char * path)613*dbd5678dSMartin Matuska is_mountpoint(char *path)
614*dbd5678dSMartin Matuska {
615*dbd5678dSMartin Matuska char *parent;
616*dbd5678dSMartin Matuska struct stat st_me, st_parent;
617*dbd5678dSMartin Matuska bool ret;
618*dbd5678dSMartin Matuska
619*dbd5678dSMartin Matuska parent = malloc(strlen(path)+4);
620*dbd5678dSMartin Matuska if (!parent) {
621*dbd5678dSMartin Matuska errno = ENOMEM;
622*dbd5678dSMartin Matuska log_errno("malloc");
623*dbd5678dSMartin Matuska return (false);
624*dbd5678dSMartin Matuska }
625*dbd5678dSMartin Matuska strcat(strcpy(parent, path), "/..");
626*dbd5678dSMartin Matuska if (lstat(path, &st_me) != 0 ||
627*dbd5678dSMartin Matuska lstat(parent, &st_parent) != 0)
628*dbd5678dSMartin Matuska ret = false;
629*dbd5678dSMartin Matuska else
630*dbd5678dSMartin Matuska if (st_me.st_dev != st_parent.st_dev ||
631*dbd5678dSMartin Matuska st_me.st_ino == st_parent.st_ino)
632*dbd5678dSMartin Matuska ret = true;
633*dbd5678dSMartin Matuska else
634*dbd5678dSMartin Matuska ret = false;
635*dbd5678dSMartin Matuska free(parent);
636*dbd5678dSMartin Matuska return (ret);
637*dbd5678dSMartin Matuska }
638*dbd5678dSMartin Matuska
639*dbd5678dSMartin Matuska /*
640*dbd5678dSMartin Matuska * Remount the source on the new target folder with the given
641*dbd5678dSMartin Matuska * list of idmap info. If target is NULL, the source will be
642*dbd5678dSMartin Matuska * unmounted and then remounted if it is a mountpoint, otherwise
643*dbd5678dSMartin Matuska * no unmount is done, the source is simply idmap remounted.
644*dbd5678dSMartin Matuska *
645*dbd5678dSMartin Matuska * Return:
646*dbd5678dSMartin Matuska * 0 if success
647*dbd5678dSMartin Matuska * -errno otherwise
648*dbd5678dSMartin Matuska */
649*dbd5678dSMartin Matuska static int
do_idmap_mount(list_t * idmap,char * source,char * target,int flags)650*dbd5678dSMartin Matuska do_idmap_mount(list_t *idmap, char *source, char *target, int flags)
651*dbd5678dSMartin Matuska {
652*dbd5678dSMartin Matuska int ret;
653*dbd5678dSMartin Matuska int tree_fd = -EBADF, source_fd = -EBADF;
654*dbd5678dSMartin Matuska mount_attr_t attr = {
655*dbd5678dSMartin Matuska .attr_set = MOUNT_ATTR_IDMAP,
656*dbd5678dSMartin Matuska .userns_fd = -EBADF,
657*dbd5678dSMartin Matuska };
658*dbd5678dSMartin Matuska
659*dbd5678dSMartin Matuska ret = userns_fd_from_idmap(idmap);
660*dbd5678dSMartin Matuska if (ret < 0)
661*dbd5678dSMartin Matuska goto out1;
662*dbd5678dSMartin Matuska attr.userns_fd = ret;
663*dbd5678dSMartin Matuska ret = openat(-EBADF, source, O_DIRECTORY | O_CLOEXEC);
664*dbd5678dSMartin Matuska if (ret < 0) {
665*dbd5678dSMartin Matuska ret = -errno;
666*dbd5678dSMartin Matuska log_errno("openat(%s)", source);
667*dbd5678dSMartin Matuska goto out;
668*dbd5678dSMartin Matuska }
669*dbd5678dSMartin Matuska source_fd = ret;
670*dbd5678dSMartin Matuska ret = sys_open_tree(source_fd, "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT |
671*dbd5678dSMartin Matuska AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE | flags);
672*dbd5678dSMartin Matuska if (ret < 0) {
673*dbd5678dSMartin Matuska ret = -errno;
674*dbd5678dSMartin Matuska log_errno("sys_open_tree");
675*dbd5678dSMartin Matuska goto out;
676*dbd5678dSMartin Matuska }
677*dbd5678dSMartin Matuska tree_fd = ret;
678*dbd5678dSMartin Matuska ret = sys_mount_setattr(tree_fd, "", AT_EMPTY_PATH | flags, &attr,
679*dbd5678dSMartin Matuska sizeof (attr));
680*dbd5678dSMartin Matuska if (ret < 0) {
681*dbd5678dSMartin Matuska ret = -errno;
682*dbd5678dSMartin Matuska log_errno("sys_mount_setattr");
683*dbd5678dSMartin Matuska goto out;
684*dbd5678dSMartin Matuska }
685*dbd5678dSMartin Matuska if (target == NULL && is_mountpoint(source)) {
686*dbd5678dSMartin Matuska ret = umount2(source, MNT_DETACH);
687*dbd5678dSMartin Matuska if (ret < 0) {
688*dbd5678dSMartin Matuska ret = -errno;
689*dbd5678dSMartin Matuska log_errno("umount2(%s)", source);
690*dbd5678dSMartin Matuska goto out;
691*dbd5678dSMartin Matuska }
692*dbd5678dSMartin Matuska }
693*dbd5678dSMartin Matuska ret = sys_move_mount(tree_fd, "", -EBADF, target == NULL ?
694*dbd5678dSMartin Matuska source : target, MOVE_MOUNT_F_EMPTY_PATH);
695*dbd5678dSMartin Matuska if (ret < 0) {
696*dbd5678dSMartin Matuska ret = -errno;
697*dbd5678dSMartin Matuska log_errno("sys_move_mount(%s)", target == NULL ?
698*dbd5678dSMartin Matuska source : target);
699*dbd5678dSMartin Matuska }
700*dbd5678dSMartin Matuska out:
701*dbd5678dSMartin Matuska close(attr.userns_fd);
702*dbd5678dSMartin Matuska out1:
703*dbd5678dSMartin Matuska if (tree_fd >= 0)
704*dbd5678dSMartin Matuska close(tree_fd);
705*dbd5678dSMartin Matuska if (source_fd >= 0)
706*dbd5678dSMartin Matuska close(source_fd);
707*dbd5678dSMartin Matuska return (ret);
708*dbd5678dSMartin Matuska }
709*dbd5678dSMartin Matuska
710*dbd5678dSMartin Matuska static void
print_usage(char * argv[])711*dbd5678dSMartin Matuska print_usage(char *argv[])
712*dbd5678dSMartin Matuska {
713*dbd5678dSMartin Matuska fprintf(stderr, "Usage: %s [-r] [-c] [-m <idmap1>] [-m <idmap2>]" \
714*dbd5678dSMartin Matuska " ... [<source>] [<target>]\n", argv[0]);
715*dbd5678dSMartin Matuska fprintf(stderr, "\n");
716*dbd5678dSMartin Matuska fprintf(stderr, " -r Recursively do idmapped mount.\n");
717*dbd5678dSMartin Matuska fprintf(stderr, "\n");
718*dbd5678dSMartin Matuska fprintf(stderr, " -c Checks if idmapped mount is supported " \
719*dbd5678dSMartin Matuska "on the <source> by the operating system or not.\n");
720*dbd5678dSMartin Matuska fprintf(stderr, "\n");
721*dbd5678dSMartin Matuska fprintf(stderr, " -m <idmap> to specify the idmap info, " \
722*dbd5678dSMartin Matuska "in the following format:\n");
723*dbd5678dSMartin Matuska fprintf(stderr, " <id_type>:<nsid_first>:<hostid_first>:<count>\n");
724*dbd5678dSMartin Matuska fprintf(stderr, "\n");
725*dbd5678dSMartin Matuska fprintf(stderr, " <id_type> can be either of 'b', 'u', and 'g'.\n");
726*dbd5678dSMartin Matuska fprintf(stderr, "\n");
727*dbd5678dSMartin Matuska fprintf(stderr, "The <source> folder will be mounted at <target> " \
728*dbd5678dSMartin Matuska "with the provided idmap information.\nIf no <target> is " \
729*dbd5678dSMartin Matuska "specified, and <source> is a mount point, " \
730*dbd5678dSMartin Matuska "then <source> will be unmounted and then remounted.\n");
731*dbd5678dSMartin Matuska }
732*dbd5678dSMartin Matuska
733*dbd5678dSMartin Matuska int
main(int argc,char * argv[])734*dbd5678dSMartin Matuska main(int argc, char *argv[])
735*dbd5678dSMartin Matuska {
736*dbd5678dSMartin Matuska int opt;
737*dbd5678dSMartin Matuska list_t idmap_head;
738*dbd5678dSMartin Matuska int check_supported = 0;
739*dbd5678dSMartin Matuska int ret = EXIT_SUCCESS;
740*dbd5678dSMartin Matuska char *source = NULL, *target = NULL;
741*dbd5678dSMartin Matuska int flags = 0;
742*dbd5678dSMartin Matuska
743*dbd5678dSMartin Matuska list_create(&idmap_head, sizeof (struct idmap_entry),
744*dbd5678dSMartin Matuska offsetof(struct idmap_entry, node));
745*dbd5678dSMartin Matuska
746*dbd5678dSMartin Matuska while ((opt = getopt(argc, argv, "rcm:")) != -1) {
747*dbd5678dSMartin Matuska switch (opt) {
748*dbd5678dSMartin Matuska case 'r':
749*dbd5678dSMartin Matuska flags |= AT_RECURSIVE;
750*dbd5678dSMartin Matuska break;
751*dbd5678dSMartin Matuska case 'c':
752*dbd5678dSMartin Matuska check_supported = 1;
753*dbd5678dSMartin Matuska break;
754*dbd5678dSMartin Matuska case 'm':
755*dbd5678dSMartin Matuska ret = parse_idmap_entry(&idmap_head, optarg);
756*dbd5678dSMartin Matuska if (ret) {
757*dbd5678dSMartin Matuska errno = ret;
758*dbd5678dSMartin Matuska log_errno("parse_idmap_entry(%s)", optarg);
759*dbd5678dSMartin Matuska ret = EXIT_FAILURE;
760*dbd5678dSMartin Matuska goto out;
761*dbd5678dSMartin Matuska }
762*dbd5678dSMartin Matuska break;
763*dbd5678dSMartin Matuska default:
764*dbd5678dSMartin Matuska print_usage(argv);
765*dbd5678dSMartin Matuska exit(EXIT_FAILURE);
766*dbd5678dSMartin Matuska }
767*dbd5678dSMartin Matuska }
768*dbd5678dSMartin Matuska
769*dbd5678dSMartin Matuska if (check_supported == 0 && list_is_empty(&idmap_head)) {
770*dbd5678dSMartin Matuska print_usage(argv);
771*dbd5678dSMartin Matuska ret = EXIT_FAILURE;
772*dbd5678dSMartin Matuska goto out;
773*dbd5678dSMartin Matuska }
774*dbd5678dSMartin Matuska
775*dbd5678dSMartin Matuska if (optind >= argc) {
776*dbd5678dSMartin Matuska fprintf(stderr, "Expected to have <source>, <target>.\n");
777*dbd5678dSMartin Matuska print_usage(argv);
778*dbd5678dSMartin Matuska ret = EXIT_FAILURE;
779*dbd5678dSMartin Matuska goto out;
780*dbd5678dSMartin Matuska }
781*dbd5678dSMartin Matuska
782*dbd5678dSMartin Matuska source = argv[optind];
783*dbd5678dSMartin Matuska if (optind < (argc - 1)) {
784*dbd5678dSMartin Matuska target = argv[optind + 1];
785*dbd5678dSMartin Matuska }
786*dbd5678dSMartin Matuska
787*dbd5678dSMartin Matuska if (check_supported) {
788*dbd5678dSMartin Matuska free_idmap(&idmap_head);
789*dbd5678dSMartin Matuska list_destroy(&idmap_head);
790*dbd5678dSMartin Matuska if (is_idmap_supported(source)) {
791*dbd5678dSMartin Matuska printf("idmapped mount is supported on [%s].\n",
792*dbd5678dSMartin Matuska source);
793*dbd5678dSMartin Matuska return (EXIT_SUCCESS);
794*dbd5678dSMartin Matuska } else {
795*dbd5678dSMartin Matuska printf("idmapped mount is NOT supported.\n");
796*dbd5678dSMartin Matuska return (EXIT_FAILURE);
797*dbd5678dSMartin Matuska }
798*dbd5678dSMartin Matuska }
799*dbd5678dSMartin Matuska
800*dbd5678dSMartin Matuska ret = do_idmap_mount(&idmap_head, source, target, flags);
801*dbd5678dSMartin Matuska if (ret)
802*dbd5678dSMartin Matuska ret = EXIT_FAILURE;
803*dbd5678dSMartin Matuska out:
804*dbd5678dSMartin Matuska free_idmap(&idmap_head);
805*dbd5678dSMartin Matuska list_destroy(&idmap_head);
806*dbd5678dSMartin Matuska
807*dbd5678dSMartin Matuska exit(ret);
808*dbd5678dSMartin Matuska }
809