xref: /linux/tools/testing/selftests/filesystems/mount_cycle/mount_cover_test.c (revision 8e8cd9a4c6dc006e9bd51b8ad0079fb4576b48d3)
1*3e78bf84SChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*3e78bf84SChristian Brauner /*
3*3e78bf84SChristian Brauner  * An unmounted mount that would have stayed attached to its unmounted parent
4*3e78bf84SChristian Brauner  * leaves a cover behind instead. A lookup on the parent at the mountpoint
5*3e78bf84SChristian Brauner  * finds knullfs: an empty read-only directory that is shared by every cover
6*3e78bf84SChristian Brauner  * and every kernel thread, so it can't be watched or locked and nothing can
7*3e78bf84SChristian Brauner  * be mounted on it. The mount itself is a root from then on. Where a file
8*3e78bf84SChristian Brauner  * was mounted, the stand-in is an empty regular file of the same instance.
9*3e78bf84SChristian Brauner  */
10*3e78bf84SChristian Brauner #define _GNU_SOURCE
11*3e78bf84SChristian Brauner #include <errno.h>
12*3e78bf84SChristian Brauner #include <fcntl.h>
13*3e78bf84SChristian Brauner #include <sched.h>
14*3e78bf84SChristian Brauner #include <stdint.h>
15*3e78bf84SChristian Brauner #include <stdio.h>
16*3e78bf84SChristian Brauner #include <stdlib.h>
17*3e78bf84SChristian Brauner #include <string.h>
18*3e78bf84SChristian Brauner #include <unistd.h>
19*3e78bf84SChristian Brauner #include <sys/fanotify.h>
20*3e78bf84SChristian Brauner #include <sys/file.h>
21*3e78bf84SChristian Brauner #include <sys/inotify.h>
22*3e78bf84SChristian Brauner #include <sys/mount.h>
23*3e78bf84SChristian Brauner #include <sys/prctl.h>
24*3e78bf84SChristian Brauner #include <sys/stat.h>
25*3e78bf84SChristian Brauner #include <sys/statvfs.h>
26*3e78bf84SChristian Brauner #include <sys/syscall.h>
27*3e78bf84SChristian Brauner #include <sys/vfs.h>
28*3e78bf84SChristian Brauner #include <sys/wait.h>
29*3e78bf84SChristian Brauner 
30*3e78bf84SChristian Brauner #include "../../kselftest_harness.h"
31*3e78bf84SChristian Brauner #include "../readdir_hold.h"
32*3e78bf84SChristian Brauner 
33*3e78bf84SChristian Brauner #ifndef NULL_FS_MAGIC
34*3e78bf84SChristian Brauner #define NULL_FS_MAGIC 0x4E554C4C
35*3e78bf84SChristian Brauner #endif
36*3e78bf84SChristian Brauner 
37*3e78bf84SChristian Brauner #ifndef TMPFS_MAGIC
38*3e78bf84SChristian Brauner #define TMPFS_MAGIC 0x01021994
39*3e78bf84SChristian Brauner #endif
40*3e78bf84SChristian Brauner 
41*3e78bf84SChristian Brauner #ifndef F_SETDELEG
42*3e78bf84SChristian Brauner #define F_SETDELEG	(F_SETLEASE + 16)
43*3e78bf84SChristian Brauner #endif
44*3e78bf84SChristian Brauner 
45*3e78bf84SChristian Brauner /* struct delegation of <linux/fcntl.h>, which doesn't mix with <fcntl.h> */
46*3e78bf84SChristian Brauner struct delegation_req {
47*3e78bf84SChristian Brauner 	uint32_t d_flags;
48*3e78bf84SChristian Brauner 	uint16_t d_type;
49*3e78bf84SChristian Brauner 	uint16_t __pad;
50*3e78bf84SChristian Brauner };
51*3e78bf84SChristian Brauner 
52*3e78bf84SChristian Brauner #define DIR_LEN 64
53*3e78bf84SChristian Brauner #define PATH_LEN 128
54*3e78bf84SChristian Brauner 
55*3e78bf84SChristian Brauner /* what the parent asks the child to do */
56*3e78bf84SChristian Brauner #define CMD_RMDIR	'r'
57*3e78bf84SChristian Brauner #define CMD_CLOSE_T	't'
58*3e78bf84SChristian Brauner #define CMD_QUIT	'q'
59*3e78bf84SChristian Brauner 
write_file(const char * path,const char * s)60*3e78bf84SChristian Brauner static int write_file(const char *path, const char *s)
61*3e78bf84SChristian Brauner {
62*3e78bf84SChristian Brauner 	ssize_t n = -1;
63*3e78bf84SChristian Brauner 	int fd;
64*3e78bf84SChristian Brauner 
65*3e78bf84SChristian Brauner 	fd = open(path, O_WRONLY | O_CLOEXEC);
66*3e78bf84SChristian Brauner 	if (fd >= 0) {
67*3e78bf84SChristian Brauner 		n = write(fd, s, strlen(s));
68*3e78bf84SChristian Brauner 		close(fd);
69*3e78bf84SChristian Brauner 	}
70*3e78bf84SChristian Brauner 	return n == (ssize_t)strlen(s) ? 0 : -1;
71*3e78bf84SChristian Brauner }
72*3e78bf84SChristian Brauner 
touch(const char * path)73*3e78bf84SChristian Brauner static int touch(const char *path)
74*3e78bf84SChristian Brauner {
75*3e78bf84SChristian Brauner 	int fd;
76*3e78bf84SChristian Brauner 
77*3e78bf84SChristian Brauner 	fd = open(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
78*3e78bf84SChristian Brauner 	if (fd < 0)
79*3e78bf84SChristian Brauner 		return -1;
80*3e78bf84SChristian Brauner 	close(fd);
81*3e78bf84SChristian Brauner 	return 0;
82*3e78bf84SChristian Brauner }
83*3e78bf84SChristian Brauner 
84*3e78bf84SChristian Brauner /* Become root in a new user namespace with a private mount namespace. */
enter_userns(void)85*3e78bf84SChristian Brauner static int enter_userns(void)
86*3e78bf84SChristian Brauner {
87*3e78bf84SChristian Brauner 	uid_t uid = getuid();
88*3e78bf84SChristian Brauner 	gid_t gid = getgid();
89*3e78bf84SChristian Brauner 	char map[32];
90*3e78bf84SChristian Brauner 
91*3e78bf84SChristian Brauner 	prctl(PR_SET_DUMPABLE, 1);
92*3e78bf84SChristian Brauner 	if (unshare(CLONE_NEWUSER | CLONE_NEWNS))
93*3e78bf84SChristian Brauner 		return -1;
94*3e78bf84SChristian Brauner 	if (write_file("/proc/self/setgroups", "deny") && errno != ENOENT)
95*3e78bf84SChristian Brauner 		return -1;
96*3e78bf84SChristian Brauner 	snprintf(map, sizeof(map), "0 %d 1", uid);
97*3e78bf84SChristian Brauner 	if (write_file("/proc/self/uid_map", map))
98*3e78bf84SChristian Brauner 		return -1;
99*3e78bf84SChristian Brauner 	snprintf(map, sizeof(map), "0 %d 1", gid);
100*3e78bf84SChristian Brauner 	if (write_file("/proc/self/gid_map", map))
101*3e78bf84SChristian Brauner 		return -1;
102*3e78bf84SChristian Brauner 	if (setgid(0) || setuid(0))
103*3e78bf84SChristian Brauner 		return -1;
104*3e78bf84SChristian Brauner 	return mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL);
105*3e78bf84SChristian Brauner }
106*3e78bf84SChristian Brauner 
107*3e78bf84SChristian Brauner /*
108*3e78bf84SChristian Brauner  * The child mounts P on @base/p, C on P/covered and the file P/src on P/file
109*3e78bf84SChristian Brauner  * in a mount namespace of its own, binds P a second time at @base/q and hands
110*3e78bf84SChristian Brauner  * out descriptors on P and on C. rmdir() of @base/p from here unmounts P
111*3e78bf84SChristian Brauner  * together with C and the file bind. P and C are held by the descriptors and
112*3e78bf84SChristian Brauner  * the unmounted children leave their covers behind. On request the child
113*3e78bf84SChristian Brauner  * removes C's mountpoint through the bind.
114*3e78bf84SChristian Brauner  *
115*3e78bf84SChristian Brauner  * It also mounts T on @base/t with a child on T/covered, binds T at @base/u
116*3e78bf84SChristian Brauner  * with a second child on the same dentry and hands out descriptors on T and
117*3e78bf84SChristian Brauner  * U. rmdir() of both from here leaves two covers on one mountpoint. On request
118*3e78bf84SChristian Brauner  * the child lets go of T.
119*3e78bf84SChristian Brauner  */
cover_child(const char * base,int to_parent,int from_parent)120*3e78bf84SChristian Brauner static int cover_child(const char *base, int to_parent, int from_parent)
121*3e78bf84SChristian Brauner {
122*3e78bf84SChristian Brauner 	char p[PATH_LEN], c[PATH_LEN], q[PATH_LEN], qc[PATH_LEN];
123*3e78bf84SChristian Brauner 	char f[PATH_LEN], src[PATH_LEN], t[PATH_LEN], u[PATH_LEN], tc[PATH_LEN];
124*3e78bf84SChristian Brauner 	int fds[4], ret;
125*3e78bf84SChristian Brauner 	char cmd;
126*3e78bf84SChristian Brauner 
127*3e78bf84SChristian Brauner 	if (unshare(CLONE_NEWNS) || mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL))
128*3e78bf84SChristian Brauner 		return 1;
129*3e78bf84SChristian Brauner 	snprintf(p, sizeof(p), "%s/p", base);
130*3e78bf84SChristian Brauner 	if (mkdir(p, 0755) || mount("tmpfs", p, "tmpfs", 0, NULL))
131*3e78bf84SChristian Brauner 		return 2;
132*3e78bf84SChristian Brauner 	snprintf(c, sizeof(c), "%s/p/covered", base);
133*3e78bf84SChristian Brauner 	if (mkdir(c, 0755) || mount("tmpfs", c, "tmpfs", 0, NULL))
134*3e78bf84SChristian Brauner 		return 3;
135*3e78bf84SChristian Brauner 	snprintf(f, sizeof(f), "%s/p/file", base);
136*3e78bf84SChristian Brauner 	snprintf(src, sizeof(src), "%s/p/src", base);
137*3e78bf84SChristian Brauner 	if (touch(src) || touch(f) || mount(src, f, NULL, MS_BIND, NULL))
138*3e78bf84SChristian Brauner 		return 4;
139*3e78bf84SChristian Brauner 	snprintf(q, sizeof(q), "%s/q", base);
140*3e78bf84SChristian Brauner 	if (mkdir(q, 0755) || mount(p, q, NULL, MS_BIND, NULL))
141*3e78bf84SChristian Brauner 		return 5;
142*3e78bf84SChristian Brauner 	snprintf(t, sizeof(t), "%s/t", base);
143*3e78bf84SChristian Brauner 	if (mkdir(t, 0755) || mount("tmpfs", t, "tmpfs", 0, NULL))
144*3e78bf84SChristian Brauner 		return 6;
145*3e78bf84SChristian Brauner 	snprintf(tc, sizeof(tc), "%s/t/covered", base);
146*3e78bf84SChristian Brauner 	if (mkdir(tc, 0755) || mount("tmpfs", tc, "tmpfs", 0, NULL))
147*3e78bf84SChristian Brauner 		return 7;
148*3e78bf84SChristian Brauner 	snprintf(u, sizeof(u), "%s/u", base);
149*3e78bf84SChristian Brauner 	if (mkdir(u, 0755) || mount(t, u, NULL, MS_BIND, NULL))
150*3e78bf84SChristian Brauner 		return 8;
151*3e78bf84SChristian Brauner 	snprintf(tc, sizeof(tc), "%s/u/covered", base);
152*3e78bf84SChristian Brauner 	if (mount("tmpfs", tc, "tmpfs", 0, NULL))
153*3e78bf84SChristian Brauner 		return 9;
154*3e78bf84SChristian Brauner 	fds[0] = open(p, O_PATH | O_DIRECTORY | O_CLOEXEC);
155*3e78bf84SChristian Brauner 	fds[1] = open(c, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
156*3e78bf84SChristian Brauner 	fds[2] = open(t, O_PATH | O_DIRECTORY | O_CLOEXEC);
157*3e78bf84SChristian Brauner 	fds[3] = open(u, O_PATH | O_DIRECTORY | O_CLOEXEC);
158*3e78bf84SChristian Brauner 	if (fds[0] < 0 || fds[1] < 0 || fds[2] < 0 || fds[3] < 0)
159*3e78bf84SChristian Brauner 		return 10;
160*3e78bf84SChristian Brauner 	if (write(to_parent, fds, sizeof(fds)) != sizeof(fds))
161*3e78bf84SChristian Brauner 		return 11;
162*3e78bf84SChristian Brauner 
163*3e78bf84SChristian Brauner 	snprintf(qc, sizeof(qc), "%s/q/covered", base);
164*3e78bf84SChristian Brauner 	for (;;) {
165*3e78bf84SChristian Brauner 		if (read(from_parent, &cmd, 1) != 1)
166*3e78bf84SChristian Brauner 			return 12;
167*3e78bf84SChristian Brauner 		switch (cmd) {
168*3e78bf84SChristian Brauner 		case CMD_RMDIR:
169*3e78bf84SChristian Brauner 			ret = rmdir(qc) ? errno : 0;
170*3e78bf84SChristian Brauner 			if (write(to_parent, &ret, sizeof(ret)) != sizeof(ret))
171*3e78bf84SChristian Brauner 				return 13;
172*3e78bf84SChristian Brauner 			break;
173*3e78bf84SChristian Brauner 		case CMD_CLOSE_T:
174*3e78bf84SChristian Brauner 			ret = close(fds[2]) ? errno : 0;
175*3e78bf84SChristian Brauner 			if (write(to_parent, &ret, sizeof(ret)) != sizeof(ret))
176*3e78bf84SChristian Brauner 				return 13;
177*3e78bf84SChristian Brauner 			break;
178*3e78bf84SChristian Brauner 		case CMD_QUIT:
179*3e78bf84SChristian Brauner 			return 0;
180*3e78bf84SChristian Brauner 		default:
181*3e78bf84SChristian Brauner 			return 14;
182*3e78bf84SChristian Brauner 		}
183*3e78bf84SChristian Brauner 	}
184*3e78bf84SChristian Brauner }
185*3e78bf84SChristian Brauner 
FIXTURE(mount_cover)186*3e78bf84SChristian Brauner FIXTURE(mount_cover) {
187*3e78bf84SChristian Brauner 	char base[DIR_LEN];
188*3e78bf84SChristian Brauner 	pid_t child;
189*3e78bf84SChristian Brauner 	int to_child;
190*3e78bf84SChristian Brauner 	int from_child;
191*3e78bf84SChristian Brauner 	int dfd;	/* P, unmounted, held */
192*3e78bf84SChristian Brauner 	int cfd;	/* C, unmounted, held */
193*3e78bf84SChristian Brauner 	int fd;		/* what a lookup on P finds at C's mountpoint */
194*3e78bf84SChristian Brauner 	int tfd;	/* T, unmounted, held */
195*3e78bf84SChristian Brauner 	int ufd;	/* U, a bind of T, unmounted, held */
196*3e78bf84SChristian Brauner 	int fan;	/* fanotify group from before the user namespace, or -1 */
197*3e78bf84SChristian Brauner 	struct readdir_hold hold;	/* likewise from before, uffd -1 without */
198*3e78bf84SChristian Brauner };
199*3e78bf84SChristian Brauner 
200*3e78bf84SChristian Brauner /*
201*3e78bf84SChristian Brauner  * Everything the setup has made. The harness does not run the teardown
202*3e78bf84SChristian Brauner  * when an assertion of the setup fails, so the setup calls this itself.
203*3e78bf84SChristian Brauner  */
cover_cleanup(FIXTURE_DATA (mount_cover)* self)204*3e78bf84SChristian Brauner static void cover_cleanup(FIXTURE_DATA(mount_cover) *self)
205*3e78bf84SChristian Brauner {
206*3e78bf84SChristian Brauner 	char cmd = CMD_QUIT;
207*3e78bf84SChristian Brauner 	int status;
208*3e78bf84SChristian Brauner 
209*3e78bf84SChristian Brauner 	if (self->fd >= 0)
210*3e78bf84SChristian Brauner 		close(self->fd);
211*3e78bf84SChristian Brauner 	if (self->cfd >= 0)
212*3e78bf84SChristian Brauner 		close(self->cfd);
213*3e78bf84SChristian Brauner 	if (self->dfd >= 0)
214*3e78bf84SChristian Brauner 		close(self->dfd);
215*3e78bf84SChristian Brauner 	if (self->tfd >= 0)
216*3e78bf84SChristian Brauner 		close(self->tfd);
217*3e78bf84SChristian Brauner 	if (self->ufd >= 0)
218*3e78bf84SChristian Brauner 		close(self->ufd);
219*3e78bf84SChristian Brauner 	if (self->fan >= 0)
220*3e78bf84SChristian Brauner 		close(self->fan);
221*3e78bf84SChristian Brauner 	readdir_hold_destroy(&self->hold);
222*3e78bf84SChristian Brauner 	if (self->child > 0) {
223*3e78bf84SChristian Brauner 		if (write(self->to_child, &cmd, 1) != 1)
224*3e78bf84SChristian Brauner 			kill(self->child, SIGKILL);
225*3e78bf84SChristian Brauner 		waitpid(self->child, &status, 0);
226*3e78bf84SChristian Brauner 	}
227*3e78bf84SChristian Brauner 	if (self->to_child >= 0)
228*3e78bf84SChristian Brauner 		close(self->to_child);
229*3e78bf84SChristian Brauner 	if (self->from_child >= 0)
230*3e78bf84SChristian Brauner 		close(self->from_child);
231*3e78bf84SChristian Brauner 	umount2(self->base, MNT_DETACH);
232*3e78bf84SChristian Brauner 	rmdir(self->base);
233*3e78bf84SChristian Brauner }
234*3e78bf84SChristian Brauner 
same_file(int fd1,int fd2)235*3e78bf84SChristian Brauner static int same_file(int fd1, int fd2)
236*3e78bf84SChristian Brauner {
237*3e78bf84SChristian Brauner 	struct stat st1, st2;
238*3e78bf84SChristian Brauner 
239*3e78bf84SChristian Brauner 	if (fstat(fd1, &st1) || fstat(fd2, &st2))
240*3e78bf84SChristian Brauner 		return 0;
241*3e78bf84SChristian Brauner 	return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
242*3e78bf84SChristian Brauner }
243*3e78bf84SChristian Brauner 
FIXTURE_SETUP(mount_cover)244*3e78bf84SChristian Brauner FIXTURE_SETUP(mount_cover)
245*3e78bf84SChristian Brauner {
246*3e78bf84SChristian Brauner 	int to_parent[2], to_child[2], fds[4], pidfd, status;
247*3e78bf84SChristian Brauner 	char dir[PATH_LEN];
248*3e78bf84SChristian Brauner 	struct statfs sf;
249*3e78bf84SChristian Brauner 
250*3e78bf84SChristian Brauner 	self->child = 0;
251*3e78bf84SChristian Brauner 	self->to_child = self->from_child = -1;
252*3e78bf84SChristian Brauner 	self->dfd = self->cfd = self->fd = self->tfd = self->ufd = -1;
253*3e78bf84SChristian Brauner 
254*3e78bf84SChristian Brauner 	/*
255*3e78bf84SChristian Brauner 	 * A group for plain events takes CAP_SYS_ADMIN in the initial user
256*3e78bf84SChristian Brauner 	 * namespace, so get one before that is gone. An inode mark can be
257*3e78bf84SChristian Brauner 	 * added to it from anywhere.
258*3e78bf84SChristian Brauner 	 */
259*3e78bf84SChristian Brauner 	self->fan = fanotify_init(FAN_CLASS_NOTIF | FAN_CLOEXEC, O_RDONLY);
260*3e78bf84SChristian Brauner 	/* same for the userfaultfd that holds a readdir in its fault */
261*3e78bf84SChristian Brauner 	readdir_hold_init(&self->hold);
262*3e78bf84SChristian Brauner 
263*3e78bf84SChristian Brauner 	snprintf(self->base, sizeof(self->base), "/tmp/mount_cover.XXXXXX");
264*3e78bf84SChristian Brauner 	ASSERT_NE(mkdtemp(self->base), NULL);
265*3e78bf84SChristian Brauner 	if (enter_userns()) {
266*3e78bf84SChristian Brauner 		cover_cleanup(self);
267*3e78bf84SChristian Brauner 		SKIP(return, "test requires user namespaces");
268*3e78bf84SChristian Brauner 	}
269*3e78bf84SChristian Brauner 	ASSERT_EQ(mount("tmpfs", self->base, "tmpfs", 0, NULL), 0)
270*3e78bf84SChristian Brauner 		cover_cleanup(self);
271*3e78bf84SChristian Brauner 
272*3e78bf84SChristian Brauner 	snprintf(dir, sizeof(dir), "%s/p", self->base);
273*3e78bf84SChristian Brauner 	ASSERT_EQ(pipe(to_parent), 0)
274*3e78bf84SChristian Brauner 		cover_cleanup(self);
275*3e78bf84SChristian Brauner 	ASSERT_EQ(pipe(to_child), 0)
276*3e78bf84SChristian Brauner 		cover_cleanup(self);
277*3e78bf84SChristian Brauner 	self->child = fork();
278*3e78bf84SChristian Brauner 	ASSERT_GE(self->child, 0)
279*3e78bf84SChristian Brauner 		cover_cleanup(self);
280*3e78bf84SChristian Brauner 	if (self->child == 0) {
281*3e78bf84SChristian Brauner 		close(to_parent[0]);
282*3e78bf84SChristian Brauner 		close(to_child[1]);
283*3e78bf84SChristian Brauner 		_exit(cover_child(self->base, to_parent[1], to_child[0]));
284*3e78bf84SChristian Brauner 	}
285*3e78bf84SChristian Brauner 	close(to_parent[1]);
286*3e78bf84SChristian Brauner 	close(to_child[0]);
287*3e78bf84SChristian Brauner 	self->to_child = to_child[1];
288*3e78bf84SChristian Brauner 	self->from_child = to_parent[0];
289*3e78bf84SChristian Brauner 	if (read(self->from_child, fds, sizeof(fds)) != sizeof(fds)) {
290*3e78bf84SChristian Brauner 		pid_t pid = self->child;
291*3e78bf84SChristian Brauner 
292*3e78bf84SChristian Brauner 		waitpid(pid, &status, 0);
293*3e78bf84SChristian Brauner 		self->child = 0;
294*3e78bf84SChristian Brauner 		cover_cleanup(self);
295*3e78bf84SChristian Brauner 		ASSERT_TRUE(false)
296*3e78bf84SChristian Brauner 			TH_LOG("child failed to set up: %s %d",
297*3e78bf84SChristian Brauner 			       WIFEXITED(status) ? "step" : "signal",
298*3e78bf84SChristian Brauner 			       WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status));
299*3e78bf84SChristian Brauner 	}
300*3e78bf84SChristian Brauner 
301*3e78bf84SChristian Brauner 	pidfd = syscall(__NR_pidfd_open, self->child, 0);
302*3e78bf84SChristian Brauner 	ASSERT_GE(pidfd, 0)
303*3e78bf84SChristian Brauner 		cover_cleanup(self);
304*3e78bf84SChristian Brauner 	self->dfd = syscall(__NR_pidfd_getfd, pidfd, fds[0], 0);
305*3e78bf84SChristian Brauner 	self->cfd = syscall(__NR_pidfd_getfd, pidfd, fds[1], 0);
306*3e78bf84SChristian Brauner 	self->tfd = syscall(__NR_pidfd_getfd, pidfd, fds[2], 0);
307*3e78bf84SChristian Brauner 	self->ufd = syscall(__NR_pidfd_getfd, pidfd, fds[3], 0);
308*3e78bf84SChristian Brauner 	close(pidfd);
309*3e78bf84SChristian Brauner 	ASSERT_GE(self->dfd, 0)
310*3e78bf84SChristian Brauner 		cover_cleanup(self);
311*3e78bf84SChristian Brauner 	ASSERT_GE(self->cfd, 0)
312*3e78bf84SChristian Brauner 		cover_cleanup(self);
313*3e78bf84SChristian Brauner 	ASSERT_GE(self->tfd, 0)
314*3e78bf84SChristian Brauner 		cover_cleanup(self);
315*3e78bf84SChristian Brauner 	ASSERT_GE(self->ufd, 0)
316*3e78bf84SChristian Brauner 		cover_cleanup(self);
317*3e78bf84SChristian Brauner 
318*3e78bf84SChristian Brauner 	/* unmounts P and C, C leaves its cover behind */
319*3e78bf84SChristian Brauner 	ASSERT_EQ(rmdir(dir), 0)
320*3e78bf84SChristian Brauner 		cover_cleanup(self);
321*3e78bf84SChristian Brauner 	/* unmounts T and U with their children, two covers on one mountpoint */
322*3e78bf84SChristian Brauner 	snprintf(dir, sizeof(dir), "%s/t", self->base);
323*3e78bf84SChristian Brauner 	ASSERT_EQ(rmdir(dir), 0)
324*3e78bf84SChristian Brauner 		cover_cleanup(self);
325*3e78bf84SChristian Brauner 	snprintf(dir, sizeof(dir), "%s/u", self->base);
326*3e78bf84SChristian Brauner 	ASSERT_EQ(rmdir(dir), 0)
327*3e78bf84SChristian Brauner 		cover_cleanup(self);
328*3e78bf84SChristian Brauner 
329*3e78bf84SChristian Brauner 	self->fd = openat(self->dfd, "covered", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
330*3e78bf84SChristian Brauner 	ASSERT_GE(self->fd, 0)
331*3e78bf84SChristian Brauner 		cover_cleanup(self);
332*3e78bf84SChristian Brauner 	ASSERT_EQ(fstatfs(self->fd, &sf), 0)
333*3e78bf84SChristian Brauner 		cover_cleanup(self);
334*3e78bf84SChristian Brauner 	ASSERT_EQ(sf.f_type, NULL_FS_MAGIC)
335*3e78bf84SChristian Brauner 		cover_cleanup(self);
336*3e78bf84SChristian Brauner }
337*3e78bf84SChristian Brauner 
FIXTURE_TEARDOWN(mount_cover)338*3e78bf84SChristian Brauner FIXTURE_TEARDOWN(mount_cover)
339*3e78bf84SChristian Brauner {
340*3e78bf84SChristian Brauner 	cover_cleanup(self);
341*3e78bf84SChristian Brauner }
342*3e78bf84SChristian Brauner 
TEST_F(mount_cover,not_watchable)343*3e78bf84SChristian Brauner TEST_F(mount_cover, not_watchable)
344*3e78bf84SChristian Brauner {
345*3e78bf84SChristian Brauner 	char p[PATH_LEN];
346*3e78bf84SChristian Brauner 	int ifd;
347*3e78bf84SChristian Brauner 
348*3e78bf84SChristian Brauner 	snprintf(p, sizeof(p), "/proc/self/fd/%d", self->fd);
349*3e78bf84SChristian Brauner 
350*3e78bf84SChristian Brauner 	ifd = inotify_init1(IN_CLOEXEC);
351*3e78bf84SChristian Brauner 	if (ifd < 0 && errno == ENOSYS) {
352*3e78bf84SChristian Brauner 		TH_LOG("no inotify in this kernel, skipping that part");
353*3e78bf84SChristian Brauner 	} else {
354*3e78bf84SChristian Brauner 		ASSERT_GE(ifd, 0);
355*3e78bf84SChristian Brauner 		EXPECT_EQ(inotify_add_watch(ifd, p, IN_OPEN), -1);
356*3e78bf84SChristian Brauner 		EXPECT_EQ(errno, EINVAL);
357*3e78bf84SChristian Brauner 		close(ifd);
358*3e78bf84SChristian Brauner 	}
359*3e78bf84SChristian Brauner 
360*3e78bf84SChristian Brauner 	/* dnotify ends up at the same place */
361*3e78bf84SChristian Brauner 	EXPECT_EQ(fcntl(self->fd, F_NOTIFY, DN_ACCESS), -1);
362*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, EINVAL);
363*3e78bf84SChristian Brauner 
364*3e78bf84SChristian Brauner 	if (self->fan < 0) {
365*3e78bf84SChristian Brauner 		TH_LOG("no fanotify group without CAP_SYS_ADMIN in the initial user namespace, skipping the fanotify part");
366*3e78bf84SChristian Brauner 		return;
367*3e78bf84SChristian Brauner 	}
368*3e78bf84SChristian Brauner 	EXPECT_EQ(fanotify_mark(self->fan, FAN_MARK_ADD, FAN_OPEN, self->fd, NULL), -1);
369*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, EINVAL);
370*3e78bf84SChristian Brauner }
371*3e78bf84SChristian Brauner 
TEST_F(mount_cover,not_lockable)372*3e78bf84SChristian Brauner TEST_F(mount_cover, not_lockable)
373*3e78bf84SChristian Brauner {
374*3e78bf84SChristian Brauner 	struct flock fl = {
375*3e78bf84SChristian Brauner 		.l_type = F_RDLCK,
376*3e78bf84SChristian Brauner 		.l_whence = SEEK_SET,
377*3e78bf84SChristian Brauner 	};
378*3e78bf84SChristian Brauner 
379*3e78bf84SChristian Brauner 	EXPECT_EQ(flock(self->fd, LOCK_EX | LOCK_NB), -1);
380*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, ENOLCK);
381*3e78bf84SChristian Brauner 	EXPECT_EQ(fcntl(self->fd, F_SETLK, &fl), -1);
382*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, ENOLCK);
383*3e78bf84SChristian Brauner 	EXPECT_EQ(fcntl(self->fd, F_GETLK, &fl), -1);
384*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, ENOLCK);
385*3e78bf84SChristian Brauner }
386*3e78bf84SChristian Brauner 
387*3e78bf84SChristian Brauner /* a lease is refused for the owner and for everybody else alike */
TEST_F(mount_cover,not_leasable)388*3e78bf84SChristian Brauner TEST_F(mount_cover, not_leasable)
389*3e78bf84SChristian Brauner {
390*3e78bf84SChristian Brauner 	struct delegation_req deleg = {
391*3e78bf84SChristian Brauner 		.d_type = F_RDLCK,
392*3e78bf84SChristian Brauner 	};
393*3e78bf84SChristian Brauner 
394*3e78bf84SChristian Brauner 	EXPECT_EQ(fcntl(self->fd, F_SETLEASE, F_RDLCK), -1);
395*3e78bf84SChristian Brauner 	EXPECT_TRUE(errno == EINVAL || errno == EACCES);
396*3e78bf84SChristian Brauner 	EXPECT_EQ(fcntl(self->fd, F_SETDELEG, &deleg), -1);
397*3e78bf84SChristian Brauner 	EXPECT_TRUE(errno == EINVAL || errno == EACCES);
398*3e78bf84SChristian Brauner }
399*3e78bf84SChristian Brauner 
TEST_F(mount_cover,not_mountable)400*3e78bf84SChristian Brauner TEST_F(mount_cover, not_mountable)
401*3e78bf84SChristian Brauner {
402*3e78bf84SChristian Brauner 	char p[PATH_LEN];
403*3e78bf84SChristian Brauner 
404*3e78bf84SChristian Brauner 	snprintf(p, sizeof(p), "/proc/self/fd/%d", self->fd);
405*3e78bf84SChristian Brauner 	EXPECT_EQ(mount("tmpfs", p, "tmpfs", 0, NULL), -1);
406*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, ENOENT);
407*3e78bf84SChristian Brauner }
408*3e78bf84SChristian Brauner 
TEST_F(mount_cover,read_only)409*3e78bf84SChristian Brauner TEST_F(mount_cover, read_only)
410*3e78bf84SChristian Brauner {
411*3e78bf84SChristian Brauner 	struct statvfs sv;
412*3e78bf84SChristian Brauner 
413*3e78bf84SChristian Brauner 	EXPECT_EQ(mkdirat(self->fd, "x", 0755), -1);
414*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, ENOENT);
415*3e78bf84SChristian Brauner 	EXPECT_EQ(fchmod(self->fd, 0777), -1);
416*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, EROFS);
417*3e78bf84SChristian Brauner 	/* the immutable inode is checked before the read-only mount */
418*3e78bf84SChristian Brauner 	EXPECT_EQ(faccessat(self->fd, ".", W_OK, 0), -1);
419*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, EPERM);
420*3e78bf84SChristian Brauner 	ASSERT_EQ(fstatvfs(self->fd, &sv), 0);
421*3e78bf84SChristian Brauner 	EXPECT_TRUE(sv.f_flag & ST_RDONLY);
422*3e78bf84SChristian Brauner }
423*3e78bf84SChristian Brauner 
424*3e78bf84SChristian Brauner /* the stand-in is a root of its own, ".." stays put */
TEST_F(mount_cover,island)425*3e78bf84SChristian Brauner TEST_F(mount_cover, island)
426*3e78bf84SChristian Brauner {
427*3e78bf84SChristian Brauner 	int fd;
428*3e78bf84SChristian Brauner 
429*3e78bf84SChristian Brauner 	fd = openat(self->fd, "..", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
430*3e78bf84SChristian Brauner 	ASSERT_GE(fd, 0);
431*3e78bf84SChristian Brauner 	EXPECT_TRUE(same_file(fd, self->fd));
432*3e78bf84SChristian Brauner 	close(fd);
433*3e78bf84SChristian Brauner }
434*3e78bf84SChristian Brauner 
435*3e78bf84SChristian Brauner /*
436*3e78bf84SChristian Brauner  * C is alive for as long as the child holds it, but it can't be reached
437*3e78bf84SChristian Brauner  * through P anymore and it's a root of its own as well.
438*3e78bf84SChristian Brauner  */
TEST_F(mount_cover,held_child_detached)439*3e78bf84SChristian Brauner TEST_F(mount_cover, held_child_detached)
440*3e78bf84SChristian Brauner {
441*3e78bf84SChristian Brauner 	struct statfs sf;
442*3e78bf84SChristian Brauner 	int fd;
443*3e78bf84SChristian Brauner 
444*3e78bf84SChristian Brauner 	ASSERT_EQ(fstatfs(self->cfd, &sf), 0);
445*3e78bf84SChristian Brauner 	EXPECT_EQ(sf.f_type, TMPFS_MAGIC);
446*3e78bf84SChristian Brauner 
447*3e78bf84SChristian Brauner 	fd = openat(self->cfd, "..", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
448*3e78bf84SChristian Brauner 	ASSERT_GE(fd, 0);
449*3e78bf84SChristian Brauner 	EXPECT_TRUE(same_file(fd, self->cfd));
450*3e78bf84SChristian Brauner 	close(fd);
451*3e78bf84SChristian Brauner 
452*3e78bf84SChristian Brauner 	fd = openat(self->dfd, "covered", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
453*3e78bf84SChristian Brauner 	ASSERT_GE(fd, 0);
454*3e78bf84SChristian Brauner 	ASSERT_EQ(fstatfs(fd, &sf), 0);
455*3e78bf84SChristian Brauner 	EXPECT_EQ(sf.f_type, NULL_FS_MAGIC);
456*3e78bf84SChristian Brauner 	close(fd);
457*3e78bf84SChristian Brauner }
458*3e78bf84SChristian Brauner 
459*3e78bf84SChristian Brauner /*
460*3e78bf84SChristian Brauner  * The cover goes with its mountpoint: once the child has removed C's
461*3e78bf84SChristian Brauner  * mountpoint through the bind of P, the name is gone from P as well.
462*3e78bf84SChristian Brauner  * What was opened through the cover before stays open.
463*3e78bf84SChristian Brauner  */
TEST_F(mount_cover,cover_goes_with_mountpoint)464*3e78bf84SChristian Brauner TEST_F(mount_cover, cover_goes_with_mountpoint)
465*3e78bf84SChristian Brauner {
466*3e78bf84SChristian Brauner 	char cmd = CMD_RMDIR;
467*3e78bf84SChristian Brauner 	struct statfs sf;
468*3e78bf84SChristian Brauner 	int ret;
469*3e78bf84SChristian Brauner 
470*3e78bf84SChristian Brauner 	ASSERT_EQ(write(self->to_child, &cmd, 1), 1);
471*3e78bf84SChristian Brauner 	ASSERT_EQ(read(self->from_child, &ret, sizeof(ret)), sizeof(ret));
472*3e78bf84SChristian Brauner 	ASSERT_EQ(ret, 0);
473*3e78bf84SChristian Brauner 
474*3e78bf84SChristian Brauner 	EXPECT_EQ(openat(self->dfd, "covered", O_RDONLY | O_DIRECTORY | O_CLOEXEC), -1);
475*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, ENOENT);
476*3e78bf84SChristian Brauner 	ASSERT_EQ(fstatfs(self->fd, &sf), 0);
477*3e78bf84SChristian Brauner 	EXPECT_EQ(sf.f_type, NULL_FS_MAGIC);
478*3e78bf84SChristian Brauner }
479*3e78bf84SChristian Brauner 
480*3e78bf84SChristian Brauner /*
481*3e78bf84SChristian Brauner  * A readdir of the stand-in holds nothing that others wait for: one holder
482*3e78bf84SChristian Brauner  * sticks in the page fault of its buffer, and a create and a lookup of
483*3e78bf84SChristian Brauner  * another come back meanwhile.
484*3e78bf84SChristian Brauner  */
TEST_F(mount_cover,readdir_blocks_nobody)485*3e78bf84SChristian Brauner TEST_F(mount_cover, readdir_blocks_nobody)
486*3e78bf84SChristian Brauner {
487*3e78bf84SChristian Brauner 	bool stalled;
488*3e78bf84SChristian Brauner 
489*3e78bf84SChristian Brauner 	if (self->hold.uffd < 0)
490*3e78bf84SChristian Brauner 		SKIP(return, "test requires userfaultfd");
491*3e78bf84SChristian Brauner 	ASSERT_EQ(readdir_hold_check(&self->hold, self->fd, &stalled), 0);
492*3e78bf84SChristian Brauner 	EXPECT_FALSE(stalled);
493*3e78bf84SChristian Brauner }
494*3e78bf84SChristian Brauner 
495*3e78bf84SChristian Brauner /* where a file was mounted, the stand-in is an empty regular file */
TEST_F(mount_cover,file_stand_in)496*3e78bf84SChristian Brauner TEST_F(mount_cover, file_stand_in)
497*3e78bf84SChristian Brauner {
498*3e78bf84SChristian Brauner 	char src[PATH_LEN], p[PATH_LEN];
499*3e78bf84SChristian Brauner 	struct statfs sf;
500*3e78bf84SChristian Brauner 	struct stat st;
501*3e78bf84SChristian Brauner 	char c;
502*3e78bf84SChristian Brauner 	int fd;
503*3e78bf84SChristian Brauner 
504*3e78bf84SChristian Brauner 	fd = openat(self->dfd, "file", O_RDONLY | O_CLOEXEC);
505*3e78bf84SChristian Brauner 	ASSERT_GE(fd, 0);
506*3e78bf84SChristian Brauner 	ASSERT_EQ(fstat(fd, &st), 0);
507*3e78bf84SChristian Brauner 	EXPECT_TRUE(S_ISREG(st.st_mode));
508*3e78bf84SChristian Brauner 	ASSERT_EQ(fstatfs(fd, &sf), 0);
509*3e78bf84SChristian Brauner 	EXPECT_EQ(sf.f_type, NULL_FS_MAGIC);
510*3e78bf84SChristian Brauner 	/* the two stand-ins are two inodes */
511*3e78bf84SChristian Brauner 	EXPECT_FALSE(same_file(fd, self->fd));
512*3e78bf84SChristian Brauner 	EXPECT_EQ(read(fd, &c, 1), 0);
513*3e78bf84SChristian Brauner 	EXPECT_EQ(flock(fd, LOCK_EX | LOCK_NB), -1);
514*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, ENOLCK);
515*3e78bf84SChristian Brauner 
516*3e78bf84SChristian Brauner 	snprintf(src, sizeof(src), "%s/src", self->base);
517*3e78bf84SChristian Brauner 	ASSERT_EQ(touch(src), 0);
518*3e78bf84SChristian Brauner 	snprintf(p, sizeof(p), "/proc/self/fd/%d", fd);
519*3e78bf84SChristian Brauner 	EXPECT_EQ(mount(src, p, NULL, MS_BIND, NULL), -1);
520*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, ENOENT);
521*3e78bf84SChristian Brauner 	close(fd);
522*3e78bf84SChristian Brauner 
523*3e78bf84SChristian Brauner 	/* writes and truncates go nowhere, like /dev/null */
524*3e78bf84SChristian Brauner 	fd = openat(self->dfd, "file", O_WRONLY | O_TRUNC | O_CLOEXEC);
525*3e78bf84SChristian Brauner 	ASSERT_GE(fd, 0);
526*3e78bf84SChristian Brauner 	EXPECT_EQ(write(fd, "xy", 2), 2);
527*3e78bf84SChristian Brauner 	EXPECT_EQ(ftruncate(fd, 4), 0);
528*3e78bf84SChristian Brauner 	ASSERT_EQ(fstat(fd, &st), 0);
529*3e78bf84SChristian Brauner 	EXPECT_EQ(st.st_size, 0);
530*3e78bf84SChristian Brauner 	/* the shared inode keeps its identity */
531*3e78bf84SChristian Brauner 	EXPECT_EQ(fchmod(fd, 0600), -1);
532*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, EPERM);
533*3e78bf84SChristian Brauner 	close(fd);
534*3e78bf84SChristian Brauner 	fd = openat(self->dfd, "file", O_RDONLY | O_CLOEXEC);
535*3e78bf84SChristian Brauner 	ASSERT_GE(fd, 0);
536*3e78bf84SChristian Brauner 	EXPECT_EQ(read(fd, &c, 1), 0);
537*3e78bf84SChristian Brauner 	close(fd);
538*3e78bf84SChristian Brauner 
539*3e78bf84SChristian Brauner 	EXPECT_EQ(openat(self->dfd, "file", O_RDONLY | O_DIRECTORY | O_CLOEXEC), -1);
540*3e78bf84SChristian Brauner 	EXPECT_EQ(errno, ENOTDIR);
541*3e78bf84SChristian Brauner }
542*3e78bf84SChristian Brauner 
543*3e78bf84SChristian Brauner /*
544*3e78bf84SChristian Brauner  * Two unmounted parents left covers on the same dentry. A lookup on
545*3e78bf84SChristian Brauner  * either finds a stand-in, and U's cover stays when T goes.
546*3e78bf84SChristian Brauner  */
TEST_F(mount_cover,shared_mountpoint)547*3e78bf84SChristian Brauner TEST_F(mount_cover, shared_mountpoint)
548*3e78bf84SChristian Brauner {
549*3e78bf84SChristian Brauner 	char cmd = CMD_CLOSE_T;
550*3e78bf84SChristian Brauner 	struct statfs sf;
551*3e78bf84SChristian Brauner 	int fd, ret;
552*3e78bf84SChristian Brauner 
553*3e78bf84SChristian Brauner 	fd = openat(self->tfd, "covered", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
554*3e78bf84SChristian Brauner 	ASSERT_GE(fd, 0);
555*3e78bf84SChristian Brauner 	ASSERT_EQ(fstatfs(fd, &sf), 0);
556*3e78bf84SChristian Brauner 	EXPECT_EQ(sf.f_type, NULL_FS_MAGIC);
557*3e78bf84SChristian Brauner 	close(fd);
558*3e78bf84SChristian Brauner 
559*3e78bf84SChristian Brauner 	fd = openat(self->ufd, "covered", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
560*3e78bf84SChristian Brauner 	ASSERT_GE(fd, 0);
561*3e78bf84SChristian Brauner 	ASSERT_EQ(fstatfs(fd, &sf), 0);
562*3e78bf84SChristian Brauner 	EXPECT_EQ(sf.f_type, NULL_FS_MAGIC);
563*3e78bf84SChristian Brauner 	close(fd);
564*3e78bf84SChristian Brauner 
565*3e78bf84SChristian Brauner 	/* the last references to T go, with T its cover */
566*3e78bf84SChristian Brauner 	ASSERT_EQ(write(self->to_child, &cmd, 1), 1);
567*3e78bf84SChristian Brauner 	ASSERT_EQ(read(self->from_child, &ret, sizeof(ret)), sizeof(ret));
568*3e78bf84SChristian Brauner 	ASSERT_EQ(ret, 0);
569*3e78bf84SChristian Brauner 	close(self->tfd);
570*3e78bf84SChristian Brauner 	self->tfd = -1;
571*3e78bf84SChristian Brauner 
572*3e78bf84SChristian Brauner 	fd = openat(self->ufd, "covered", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
573*3e78bf84SChristian Brauner 	ASSERT_GE(fd, 0);
574*3e78bf84SChristian Brauner 	ASSERT_EQ(fstatfs(fd, &sf), 0);
575*3e78bf84SChristian Brauner 	EXPECT_EQ(sf.f_type, NULL_FS_MAGIC);
576*3e78bf84SChristian Brauner 	close(fd);
577*3e78bf84SChristian Brauner }
578*3e78bf84SChristian Brauner 
579*3e78bf84SChristian Brauner TEST_HARNESS_MAIN
580