generic: test MADV_POPULATE_READ with IO errors
[xfstests-dev.git] / src / idmapped-mounts / mount-idmapped.c
1 // SPDX-License-Identifier: GPL-2.0
2 #ifndef _GNU_SOURCE
3 #define _GNU_SOURCE
4 #endif
5
6 #include "../global.h"
7
8 #include <dirent.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <getopt.h>
12 #include <limits.h>
13 #include <linux/sched.h>
14 #include <sched.h>
15 #include <signal.h>
16 #include <stdbool.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/stat.h>
22 #include <sys/syscall.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26
27 #include "missing.h"
28 #include "utils.h"
29
30 static struct list active_map;
31
32 static int parse_map(char *map)
33 {
34         char types[2] = {'u', 'g'};
35         int ret, i;
36         __u32 id_host, id_ns, range;
37         char which;
38
39         if (!map)
40                 return -1;
41
42         ret = sscanf(map, "%c:%u:%u:%u", &which, &id_ns, &id_host, &range);
43         if (ret != 4)
44                 return -1;
45
46         if (which != 'b' && which != 'u' && which != 'g')
47                 return -1;
48
49         for (i = 0; i < 2; i++) {
50                 idmap_type_t map_type;
51
52                 if (which != types[i] && which != 'b')
53                         continue;
54
55                 if (types[i] == 'u')
56                         map_type = ID_TYPE_UID;
57                 else
58                         map_type = ID_TYPE_GID;
59
60                 ret = add_map_entry(&active_map, id_host, id_ns, range, map_type);
61                 if (ret < 0)
62                         return ret;
63         }
64
65         return 0;
66 }
67
68 static inline bool strnequal(const char *str, const char *eq, size_t len)
69 {
70         return strncmp(str, eq, len) == 0;
71 }
72
73 static void usage(void)
74 {
75         const char *text = "\
76 mount-idmapped --map-mount=<idmap> <source> <target>\n\
77 \n\
78 Create an idmapped mount of <source> at <target>\n\
79 Options:\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\
93   --recursive\n\
94         Copy the whole mount tree from <source> and apply the idmap to everyone at <target>.\n\n\
95 Examples:\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\
100 ";
101         fprintf(stderr, "%s", text);
102         _exit(EXIT_SUCCESS);
103 }
104
105 #define exit_usage(format, ...)                         \
106         ({                                              \
107                 fprintf(stderr, format, ##__VA_ARGS__); \
108                 usage();                                \
109         })
110
111 #define exit_log(format, ...)                           \
112         ({                                              \
113                 fprintf(stderr, format, ##__VA_ARGS__); \
114                 exit(EXIT_FAILURE);                     \
115         })
116
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'},
121         { NULL,         0,                      0,      0  },
122 };
123
124 int main(int argc, char *argv[])
125 {
126         int fd_userns = -EBADF;
127         int index = 0;
128         const char *source = NULL, *target = NULL;
129         bool recursive = false;
130         int fd_tree, new_argc, ret;
131         char *const *new_argv;
132
133         list_init(&active_map);
134         while ((ret = getopt_long_only(argc, argv, "", longopts, &index)) != -1) {
135                 switch (ret) {
136                 case 'a':
137                         if (strnequal(optarg, "/proc/", STRLITERALLEN("/proc/"))) {
138                                 fd_userns = open(optarg, O_RDONLY | O_CLOEXEC);
139                                 if (fd_userns < 0)
140                                         exit_log("%m - Failed top open user namespace path %s\n", optarg);
141                                 break;
142                         }
143
144                         ret = parse_map(optarg);
145                         if (ret < 0)
146                                 exit_log("Failed to parse idmaps for mount\n");
147                         break;
148                 case 'd':
149                         recursive = true;
150                         break;
151                 case 'c':
152                         /* fallthrough */
153                 default:
154                         usage();
155                 }
156         }
157
158         new_argv = &argv[optind];
159         new_argc = argc - optind;
160         if (new_argc < 2)
161                 exit_usage("Missing source or target mountpoint\n\n");
162         source = new_argv[0];
163         target = new_argv[1];
164
165         fd_tree = sys_open_tree(-EBADF, source,
166                                 OPEN_TREE_CLONE |
167                                 OPEN_TREE_CLOEXEC |
168                                 AT_EMPTY_PATH |
169                                 (recursive ? AT_RECURSIVE : 0));
170         if (fd_tree < 0) {
171                 exit_log("%m - Failed to open %s\n", source);
172                 exit(EXIT_FAILURE);
173         }
174
175         if (!list_empty(&active_map) || fd_userns >= 0) {
176                 struct mount_attr attr = {
177                         .attr_set = MOUNT_ATTR_IDMAP,
178                 };
179
180                 if (fd_userns >= 0)
181                         attr.userns_fd = fd_userns;
182                 else
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");
186
187                 ret = sys_mount_setattr(fd_tree, "", AT_EMPTY_PATH | AT_RECURSIVE,
188                                         &attr, sizeof(attr));
189                 if (ret < 0)
190                         exit_log("%m - Failed to change mount attributes\n");
191                 close(attr.userns_fd);
192         }
193
194         ret = sys_move_mount(fd_tree, "", -EBADF, target,
195                              MOVE_MOUNT_F_EMPTY_PATH);
196         if (ret < 0)
197                 exit_log("%m - Failed to attach mount to %s\n", target);
198         close(fd_tree);
199
200         exit(EXIT_SUCCESS);
201 }