Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / tools / testing / selftests / mount / unprivileged-remount-test.c
1 #define _GNU_SOURCE
2 #include <sched.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <sys/types.h>
7 #include <sys/mount.h>
8 #include <sys/wait.h>
9 #include <sys/vfs.h>
10 #include <sys/statvfs.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <grp.h>
15 #include <stdbool.h>
16 #include <stdarg.h>
17
18 #ifndef CLONE_NEWNS
19 # define CLONE_NEWNS 0x00020000
20 #endif
21 #ifndef CLONE_NEWUTS
22 # define CLONE_NEWUTS 0x04000000
23 #endif
24 #ifndef CLONE_NEWIPC
25 # define CLONE_NEWIPC 0x08000000
26 #endif
27 #ifndef CLONE_NEWNET
28 # define CLONE_NEWNET 0x40000000
29 #endif
30 #ifndef CLONE_NEWUSER
31 # define CLONE_NEWUSER 0x10000000
32 #endif
33 #ifndef CLONE_NEWPID
34 # define CLONE_NEWPID 0x20000000
35 #endif
36
37 #ifndef MS_REC
38 # define MS_REC 16384
39 #endif
40 #ifndef MS_RELATIME
41 # define MS_RELATIME (1 << 21)
42 #endif
43 #ifndef MS_STRICTATIME
44 # define MS_STRICTATIME (1 << 24)
45 #endif
46
47 static void die(char *fmt, ...)
48 {
49         va_list ap;
50         va_start(ap, fmt);
51         vfprintf(stderr, fmt, ap);
52         va_end(ap);
53         exit(EXIT_FAILURE);
54 }
55
56 static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
57 {
58         char buf[4096];
59         int fd;
60         ssize_t written;
61         int buf_len;
62
63         buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
64         if (buf_len < 0) {
65                 die("vsnprintf failed: %s\n",
66                     strerror(errno));
67         }
68         if (buf_len >= sizeof(buf)) {
69                 die("vsnprintf output truncated\n");
70         }
71
72         fd = open(filename, O_WRONLY);
73         if (fd < 0) {
74                 if ((errno == ENOENT) && enoent_ok)
75                         return;
76                 die("open of %s failed: %s\n",
77                     filename, strerror(errno));
78         }
79         written = write(fd, buf, buf_len);
80         if (written != buf_len) {
81                 if (written >= 0) {
82                         die("short write to %s\n", filename);
83                 } else {
84                         die("write to %s failed: %s\n",
85                                 filename, strerror(errno));
86                 }
87         }
88         if (close(fd) != 0) {
89                 die("close of %s failed: %s\n",
90                         filename, strerror(errno));
91         }
92 }
93
94 static void maybe_write_file(char *filename, char *fmt, ...)
95 {
96         va_list ap;
97
98         va_start(ap, fmt);
99         vmaybe_write_file(true, filename, fmt, ap);
100         va_end(ap);
101
102 }
103
104 static void write_file(char *filename, char *fmt, ...)
105 {
106         va_list ap;
107
108         va_start(ap, fmt);
109         vmaybe_write_file(false, filename, fmt, ap);
110         va_end(ap);
111
112 }
113
114 static int read_mnt_flags(const char *path)
115 {
116         int ret;
117         struct statvfs stat;
118         int mnt_flags;
119
120         ret = statvfs(path, &stat);
121         if (ret != 0) {
122                 die("statvfs of %s failed: %s\n",
123                         path, strerror(errno));
124         }
125         if (stat.f_flag & ~(ST_RDONLY | ST_NOSUID | ST_NODEV | \
126                         ST_NOEXEC | ST_NOATIME | ST_NODIRATIME | ST_RELATIME | \
127                         ST_SYNCHRONOUS | ST_MANDLOCK)) {
128                 die("Unrecognized mount flags\n");
129         }
130         mnt_flags = 0;
131         if (stat.f_flag & ST_RDONLY)
132                 mnt_flags |= MS_RDONLY;
133         if (stat.f_flag & ST_NOSUID)
134                 mnt_flags |= MS_NOSUID;
135         if (stat.f_flag & ST_NODEV)
136                 mnt_flags |= MS_NODEV;
137         if (stat.f_flag & ST_NOEXEC)
138                 mnt_flags |= MS_NOEXEC;
139         if (stat.f_flag & ST_NOATIME)
140                 mnt_flags |= MS_NOATIME;
141         if (stat.f_flag & ST_NODIRATIME)
142                 mnt_flags |= MS_NODIRATIME;
143         if (stat.f_flag & ST_RELATIME)
144                 mnt_flags |= MS_RELATIME;
145         if (stat.f_flag & ST_SYNCHRONOUS)
146                 mnt_flags |= MS_SYNCHRONOUS;
147         if (stat.f_flag & ST_MANDLOCK)
148                 mnt_flags |= ST_MANDLOCK;
149
150         return mnt_flags;
151 }
152
153 static void create_and_enter_userns(void)
154 {
155         uid_t uid;
156         gid_t gid;
157
158         uid = getuid();
159         gid = getgid();
160
161         if (unshare(CLONE_NEWUSER) !=0) {
162                 die("unshare(CLONE_NEWUSER) failed: %s\n",
163                         strerror(errno));
164         }
165
166         maybe_write_file("/proc/self/setgroups", "deny");
167         write_file("/proc/self/uid_map", "0 %d 1", uid);
168         write_file("/proc/self/gid_map", "0 %d 1", gid);
169
170         if (setgid(0) != 0) {
171                 die ("setgid(0) failed %s\n",
172                         strerror(errno));
173         }
174         if (setuid(0) != 0) {
175                 die("setuid(0) failed %s\n",
176                         strerror(errno));
177         }
178 }
179
180 static
181 bool test_unpriv_remount(const char *fstype, const char *mount_options,
182                          int mount_flags, int remount_flags, int invalid_flags)
183 {
184         pid_t child;
185
186         child = fork();
187         if (child == -1) {
188                 die("fork failed: %s\n",
189                         strerror(errno));
190         }
191         if (child != 0) { /* parent */
192                 pid_t pid;
193                 int status;
194                 pid = waitpid(child, &status, 0);
195                 if (pid == -1) {
196                         die("waitpid failed: %s\n",
197                                 strerror(errno));
198                 }
199                 if (pid != child) {
200                         die("waited for %d got %d\n",
201                                 child, pid);
202                 }
203                 if (!WIFEXITED(status)) {
204                         die("child did not terminate cleanly\n");
205                 }
206                 return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
207         }
208
209         create_and_enter_userns();
210         if (unshare(CLONE_NEWNS) != 0) {
211                 die("unshare(CLONE_NEWNS) failed: %s\n",
212                         strerror(errno));
213         }
214
215         if (mount("testing", "/tmp", fstype, mount_flags, mount_options) != 0) {
216                 die("mount of %s with options '%s' on /tmp failed: %s\n",
217                     fstype,
218                     mount_options? mount_options : "",
219                     strerror(errno));
220         }
221
222         create_and_enter_userns();
223
224         if (unshare(CLONE_NEWNS) != 0) {
225                 die("unshare(CLONE_NEWNS) failed: %s\n",
226                         strerror(errno));
227         }
228
229         if (mount("/tmp", "/tmp", "none",
230                   MS_REMOUNT | MS_BIND | remount_flags, NULL) != 0) {
231                 /* system("cat /proc/self/mounts"); */
232                 die("remount of /tmp failed: %s\n",
233                     strerror(errno));
234         }
235
236         if (mount("/tmp", "/tmp", "none",
237                   MS_REMOUNT | MS_BIND | invalid_flags, NULL) == 0) {
238                 /* system("cat /proc/self/mounts"); */
239                 die("remount of /tmp with invalid flags "
240                     "succeeded unexpectedly\n");
241         }
242         exit(EXIT_SUCCESS);
243 }
244
245 static bool test_unpriv_remount_simple(int mount_flags)
246 {
247         return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 0);
248 }
249
250 static bool test_unpriv_remount_atime(int mount_flags, int invalid_flags)
251 {
252         return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags,
253                                    invalid_flags);
254 }
255
256 static bool test_priv_mount_unpriv_remount(void)
257 {
258         pid_t child;
259         int ret;
260         const char *orig_path = "/dev";
261         const char *dest_path = "/tmp";
262         int orig_mnt_flags, remount_mnt_flags;
263
264         child = fork();
265         if (child == -1) {
266                 die("fork failed: %s\n",
267                         strerror(errno));
268         }
269         if (child != 0) { /* parent */
270                 pid_t pid;
271                 int status;
272                 pid = waitpid(child, &status, 0);
273                 if (pid == -1) {
274                         die("waitpid failed: %s\n",
275                                 strerror(errno));
276                 }
277                 if (pid != child) {
278                         die("waited for %d got %d\n",
279                                 child, pid);
280                 }
281                 if (!WIFEXITED(status)) {
282                         die("child did not terminate cleanly\n");
283                 }
284                 return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
285         }
286
287         orig_mnt_flags = read_mnt_flags(orig_path);
288
289         create_and_enter_userns();
290         ret = unshare(CLONE_NEWNS);
291         if (ret != 0) {
292                 die("unshare(CLONE_NEWNS) failed: %s\n",
293                         strerror(errno));
294         }
295
296         ret = mount(orig_path, dest_path, "bind", MS_BIND | MS_REC, NULL);
297         if (ret != 0) {
298                 die("recursive bind mount of %s onto %s failed: %s\n",
299                         orig_path, dest_path, strerror(errno));
300         }
301
302         ret = mount(dest_path, dest_path, "none",
303                     MS_REMOUNT | MS_BIND | orig_mnt_flags , NULL);
304         if (ret != 0) {
305                 /* system("cat /proc/self/mounts"); */
306                 die("remount of /tmp failed: %s\n",
307                     strerror(errno));
308         }
309
310         remount_mnt_flags = read_mnt_flags(dest_path);
311         if (orig_mnt_flags != remount_mnt_flags) {
312                 die("Mount flags unexpectedly changed during remount of %s originally mounted on %s\n",
313                         dest_path, orig_path);
314         }
315         exit(EXIT_SUCCESS);
316 }
317
318 int main(int argc, char **argv)
319 {
320         if (!test_unpriv_remount_simple(MS_RDONLY)) {
321                 die("MS_RDONLY malfunctions\n");
322         }
323         if (!test_unpriv_remount("devpts", "newinstance", MS_NODEV, MS_NODEV, 0)) {
324                 die("MS_NODEV malfunctions\n");
325         }
326         if (!test_unpriv_remount_simple(MS_NOSUID)) {
327                 die("MS_NOSUID malfunctions\n");
328         }
329         if (!test_unpriv_remount_simple(MS_NOEXEC)) {
330                 die("MS_NOEXEC malfunctions\n");
331         }
332         if (!test_unpriv_remount_atime(MS_RELATIME,
333                                        MS_NOATIME))
334         {
335                 die("MS_RELATIME malfunctions\n");
336         }
337         if (!test_unpriv_remount_atime(MS_STRICTATIME,
338                                        MS_NOATIME))
339         {
340                 die("MS_STRICTATIME malfunctions\n");
341         }
342         if (!test_unpriv_remount_atime(MS_NOATIME,
343                                        MS_STRICTATIME))
344         {
345                 die("MS_NOATIME malfunctions\n");
346         }
347         if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME,
348                                        MS_NOATIME))
349         {
350                 die("MS_RELATIME|MS_NODIRATIME malfunctions\n");
351         }
352         if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME,
353                                        MS_NOATIME))
354         {
355                 die("MS_STRICTATIME|MS_NODIRATIME malfunctions\n");
356         }
357         if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME,
358                                        MS_STRICTATIME))
359         {
360                 die("MS_NOATIME|MS_DIRATIME malfunctions\n");
361         }
362         if (!test_unpriv_remount("ramfs", NULL, MS_STRICTATIME, 0, MS_NOATIME))
363         {
364                 die("Default atime malfunctions\n");
365         }
366         if (!test_priv_mount_unpriv_remount()) {
367                 die("Mount flags unexpectedly changed after remount\n");
368         }
369         return EXIT_SUCCESS;
370 }