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