1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * A mount namespace file bind-mounted on top of the mount that is moved
4 * onto a shared mount isn't copied to the peers and slaves. The mount that
5 * already sits at the destination in a slave has to end up on top of the
6 * propagated copy, not below the root of the mount namespace file where no
7 * path walk ever finds it.
8 */
9 #define _GNU_SOURCE
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <sched.h>
13 #include <signal.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/mount.h>
19 #include <sys/stat.h>
20 #include <sys/syscall.h>
21 #include <sys/wait.h>
22
23 #include "../wrappers.h"
24 #include "../../kselftest_harness.h"
25
26 #define DIR_LEN 64
27 #define PATH_LEN 128
28
write_file(const char * path,const char * s)29 static int write_file(const char *path, const char *s)
30 {
31 ssize_t n = -1;
32 int fd;
33
34 fd = open(path, O_WRONLY | O_CLOEXEC);
35 if (fd >= 0) {
36 n = write(fd, s, strlen(s));
37 close(fd);
38 }
39 return n == (ssize_t)strlen(s) ? 0 : -1;
40 }
41
create_file(const char * path,const char * s)42 static int create_file(const char *path, const char *s)
43 {
44 ssize_t n = -1;
45 int fd;
46
47 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
48 if (fd >= 0) {
49 n = write(fd, s, strlen(s));
50 close(fd);
51 }
52 return n == (ssize_t)strlen(s) ? 0 : -1;
53 }
54
55 /* the first bytes of the file at @path, "" if it can't be read */
read_file(const char * path,char * buf,size_t len)56 static const char *read_file(const char *path, char *buf, size_t len)
57 {
58 ssize_t n = -1;
59 int fd;
60
61 fd = open(path, O_RDONLY | O_CLOEXEC);
62 if (fd >= 0) {
63 n = read(fd, buf, len - 1);
64 close(fd);
65 }
66 buf[n > 0 ? n : 0] = '\0';
67 return buf;
68 }
69
70 /* Become root in a new user namespace with a private mount namespace. */
enter_userns(void)71 static int enter_userns(void)
72 {
73 uid_t uid = getuid();
74 gid_t gid = getgid();
75 char map[32];
76
77 if (unshare(CLONE_NEWUSER | CLONE_NEWNS))
78 return -1;
79 if (write_file("/proc/self/setgroups", "deny") && errno != ENOENT)
80 return -1;
81 snprintf(map, sizeof(map), "0 %d 1", uid);
82 if (write_file("/proc/self/uid_map", map))
83 return -1;
84 snprintf(map, sizeof(map), "0 %d 1", gid);
85 if (write_file("/proc/self/gid_map", map))
86 return -1;
87 if (setgid(0) || setuid(0))
88 return -1;
89 return mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL);
90 }
91
FIXTURE(overmount_ns_file)92 FIXTURE(overmount_ns_file) {
93 char dir[DIR_LEN];
94 pid_t child;
95 };
96
FIXTURE_SETUP(overmount_ns_file)97 FIXTURE_SETUP(overmount_ns_file)
98 {
99 self->child = -1;
100 snprintf(self->dir, sizeof(self->dir), "/tmp/overmount_ns_file.XXXXXX");
101 ASSERT_NE(mkdtemp(self->dir), NULL);
102 if (enter_userns()) {
103 rmdir(self->dir);
104 SKIP(return, "test requires user namespaces");
105 }
106 ASSERT_EQ(mount("tmpfs", self->dir, "tmpfs", 0, NULL), 0);
107 }
108
FIXTURE_TEARDOWN(overmount_ns_file)109 FIXTURE_TEARDOWN(overmount_ns_file)
110 {
111 if (self->child > 0) {
112 kill(self->child, SIGKILL);
113 waitpid(self->child, NULL, 0);
114 }
115 umount2(self->dir, MNT_DETACH);
116 rmdir(self->dir);
117 }
118
119 /*
120 * A is a shared tmpfs and B its slave with Q, a bind mount of a file, on
121 * B/file. S is a bind mount of a file with N, a bind mount of a newer mount
122 * namespace's file, on top of it. S is moved onto A/file. Its copy S' lands
123 * on B/file below Q, without N. B/file keeps reading Q and once Q is
124 * unmounted it reads S'.
125 */
TEST_F(overmount_ns_file,existing_mount_stays_on_top)126 TEST_F(overmount_ns_file, existing_mount_stays_on_top)
127 {
128 char a[PATH_LEN], b[PATH_LEN], s[PATH_LEN], p[PATH_LEN], buf[16];
129 int fd, pfd[2];
130 char c;
131
132 snprintf(a, sizeof(a), "%s/A", self->dir);
133 snprintf(b, sizeof(b), "%s/B", self->dir);
134 snprintf(s, sizeof(s), "%s/s", self->dir);
135 ASSERT_EQ(mkdir(a, 0755), 0);
136 ASSERT_EQ(mkdir(b, 0755), 0);
137 ASSERT_EQ(mkdir(s, 0755), 0);
138
139 /* A shared, B its slave, Q on B/file */
140 ASSERT_EQ(mount("tmpfs", a, "tmpfs", 0, NULL), 0);
141 ASSERT_EQ(mount(NULL, a, NULL, MS_SHARED, NULL), 0);
142 snprintf(p, sizeof(p), "%s/A/file", self->dir);
143 ASSERT_EQ(create_file(p, "A"), 0);
144 ASSERT_EQ(mount(a, b, NULL, MS_BIND, NULL), 0);
145 ASSERT_EQ(mount(NULL, b, NULL, MS_SLAVE, NULL), 0);
146 snprintf(p, sizeof(p), "%s/Q", self->dir);
147 ASSERT_EQ(create_file(p, "Q"), 0);
148 snprintf(b, sizeof(b), "%s/B/file", self->dir);
149 ASSERT_EQ(mount(p, b, NULL, MS_BIND, NULL), 0);
150
151 /* S on s/f, pinned by a file descriptor before N goes on top */
152 ASSERT_EQ(mount("tmpfs", s, "tmpfs", 0, NULL), 0);
153 snprintf(p, sizeof(p), "%s/s/f", self->dir);
154 ASSERT_EQ(create_file(p, "f"), 0);
155 snprintf(s, sizeof(s), "%s/s/S", self->dir);
156 ASSERT_EQ(create_file(s, "S"), 0);
157 ASSERT_EQ(mount(s, p, NULL, MS_BIND, NULL), 0);
158 fd = open(p, O_PATH | O_CLOEXEC);
159 ASSERT_GE(fd, 0);
160
161 /* a newer mount namespace whose file can be bound */
162 ASSERT_EQ(pipe(pfd), 0);
163 self->child = fork();
164 ASSERT_GE(self->child, 0);
165 if (self->child == 0) {
166 close(pfd[0]);
167 if (unshare(CLONE_NEWNS) || write(pfd[1], "r", 1) != 1)
168 _exit(1);
169 pause();
170 _exit(0);
171 }
172 close(pfd[1]);
173 ASSERT_EQ(read(pfd[0], &c, 1), 1);
174 close(pfd[0]);
175 snprintf(s, sizeof(s), "/proc/%d/ns/mnt", self->child);
176 ASSERT_EQ(mount(s, p, NULL, MS_BIND, NULL), 0);
177
178 ASSERT_STREQ(read_file(b, buf, sizeof(buf)), "Q");
179
180 snprintf(a, sizeof(a), "%s/A/file", self->dir);
181 ASSERT_EQ(sys_move_mount(fd, "", AT_FDCWD, a, MOVE_MOUNT_F_EMPTY_PATH), 0);
182 close(fd);
183
184 /* Q is still on top of the copy in B and can be unmounted */
185 EXPECT_STREQ(read_file(b, buf, sizeof(buf)), "Q");
186 EXPECT_EQ(umount2(b, 0), 0);
187 EXPECT_STREQ(read_file(b, buf, sizeof(buf)), "S");
188 }
189
190 TEST_HARNESS_MAIN
191