1 // SPDX-License-Identifier: GPL-2.0
13 #include <linux/sched.h>
22 #include <sys/syscall.h>
23 #include <sys/types.h>
30 static struct list active_map;
32 static int parse_map(char *map)
34 char types[2] = {'u', 'g'};
36 __u32 id_host, id_ns, range;
42 ret = sscanf(map, "%c:%u:%u:%u", &which, &id_ns, &id_host, &range);
46 if (which != 'b' && which != 'u' && which != 'g')
49 for (i = 0; i < 2; i++) {
50 idmap_type_t map_type;
52 if (which != types[i] && which != 'b')
56 map_type = ID_TYPE_UID;
58 map_type = ID_TYPE_GID;
60 ret = add_map_entry(&active_map, id_host, id_ns, range, map_type);
68 static inline bool strnequal(const char *str, const char *eq, size_t len)
70 return strncmp(str, eq, len) == 0;
73 static void usage(void)
76 mount-idmapped --map-mount=<idmap> <source> <target>\n\
78 Create an idmapped mount of <source> at <target>\n\
80 --map-mount=<idmap>\n\
81 Specify an idmap for the <target> mount in the format\n\
82 <idmap-type>:<id-from>:<id-to>:<id-range>\n\
83 The <idmap-type> can be:\n\
84 \"b\" or \"both\" -> map both uids and gids\n\
85 \"u\" or \"uid\" -> map uids\n\
86 \"g\" or \"gid\" -> map gids\n\
87 For example, specifying:\n\
88 both:1000:1001:1 -> map uid and gid 1000 to uid and gid 1001 in <target> and no other ids\n\
89 uid:20000:100000:1000 -> map uid 20000 to uid 100000, uid 20001 to uid 100001 [...] in <target>\n\
90 Currently up to 340 separate idmappings may be specified.\n\n\
91 --map-mount=/proc/<pid>/ns/user\n\
92 Specify a path to a user namespace whose idmap is to be used.\n\n\
94 Copy the whole mount tree from <source> and apply the idmap to everyone at <target>.\n\n\
96 - Create an idmapped mount of /source on /target with both ('b') uids and gids mapped:\n\
97 mount-idmapped --map-mount b:0:10000:10000 /source /target\n\n\
98 - Create an idmapped mount of /source on /target with uids ('u') and gids ('g') mapped separately:\n\
99 mount-idmapped --map-mount u:0:10000:10000 g:0:20000:20000 /source /target\n\n\
101 fprintf(stderr, "%s", text);
105 #define exit_usage(format, ...) \
107 fprintf(stderr, format, ##__VA_ARGS__); \
111 #define exit_log(format, ...) \
113 fprintf(stderr, format, ##__VA_ARGS__); \
114 exit(EXIT_FAILURE); \
117 static const struct option longopts[] = {
118 {"map-mount", required_argument, 0, 'a'},
119 {"help", no_argument, 0, 'c'},
120 {"recursive", no_argument, 0, 'd'},
124 int main(int argc, char *argv[])
126 int fd_userns = -EBADF;
128 const char *source = NULL, *target = NULL;
129 bool recursive = false;
130 int fd_tree, new_argc, ret;
131 char *const *new_argv;
133 list_init(&active_map);
134 while ((ret = getopt_long_only(argc, argv, "", longopts, &index)) != -1) {
137 if (strnequal(optarg, "/proc/", STRLITERALLEN("/proc/"))) {
138 fd_userns = open(optarg, O_RDONLY | O_CLOEXEC);
140 exit_log("%m - Failed top open user namespace path %s\n", optarg);
144 ret = parse_map(optarg);
146 exit_log("Failed to parse idmaps for mount\n");
158 new_argv = &argv[optind];
159 new_argc = argc - optind;
161 exit_usage("Missing source or target mountpoint\n\n");
162 source = new_argv[0];
163 target = new_argv[1];
165 fd_tree = sys_open_tree(-EBADF, source,
169 (recursive ? AT_RECURSIVE : 0));
171 exit_log("%m - Failed to open %s\n", source);
175 if (!list_empty(&active_map) || fd_userns >= 0) {
176 struct mount_attr attr = {
177 .attr_set = MOUNT_ATTR_IDMAP,
181 attr.userns_fd = fd_userns;
183 attr.userns_fd = get_userns_fd_from_idmap(&active_map);
184 if (attr.userns_fd < 0)
185 exit_log("%m - Failed to create user namespace\n");
187 ret = sys_mount_setattr(fd_tree, "", AT_EMPTY_PATH | AT_RECURSIVE,
188 &attr, sizeof(attr));
190 exit_log("%m - Failed to change mount attributes\n");
191 close(attr.userns_fd);
194 ret = sys_move_mount(fd_tree, "", -EBADF, target,
195 MOVE_MOUNT_F_EMPTY_PATH);
197 exit_log("%m - Failed to attach mount to %s\n", target);