xref: /linux/tools/testing/selftests/filesystems/overlayfs/automount_in_layer.c (revision 39796254f4458a532dcef8197ec84b7c80ad2b18)
1*39796254SChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*39796254SChristian Brauner /*
3*39796254SChristian Brauner  * A layer of an overlay is a private clone of the mount it was given and
4*39796254SChristian Brauner  * belongs to no mount namespace. fanotify hands out descriptors on it. An
5*39796254SChristian Brauner  * automount triggered through one has no namespace to go into: the open has
6*39796254SChristian Brauner  * to fail, not oops with namespace_sem held.
7*39796254SChristian Brauner  */
8*39796254SChristian Brauner #define _GNU_SOURCE
9*39796254SChristian Brauner #include <dirent.h>
10*39796254SChristian Brauner #include <errno.h>
11*39796254SChristian Brauner #include <fcntl.h>
12*39796254SChristian Brauner #include <poll.h>
13*39796254SChristian Brauner #include <sched.h>
14*39796254SChristian Brauner #include <stdio.h>
15*39796254SChristian Brauner #include <stdlib.h>
16*39796254SChristian Brauner #include <string.h>
17*39796254SChristian Brauner #include <unistd.h>
18*39796254SChristian Brauner #include <linux/magic.h>
19*39796254SChristian Brauner #include <sys/fanotify.h>
20*39796254SChristian Brauner #include <sys/mount.h>
21*39796254SChristian Brauner #include <sys/stat.h>
22*39796254SChristian Brauner #include <sys/vfs.h>
23*39796254SChristian Brauner 
24*39796254SChristian Brauner #include "../../kselftest_harness.h"
25*39796254SChristian Brauner 
26*39796254SChristian Brauner #define DIR_LEN		64
27*39796254SChristian Brauner #define PATH_LEN	192
28*39796254SChristian Brauner 
29*39796254SChristian Brauner static bool have_fs(const char *name)
30*39796254SChristian Brauner {
31*39796254SChristian Brauner 	char line[128];
32*39796254SChristian Brauner 	bool found = false;
33*39796254SChristian Brauner 	FILE *f;
34*39796254SChristian Brauner 
35*39796254SChristian Brauner 	f = fopen("/proc/filesystems", "re");
36*39796254SChristian Brauner 	if (!f)
37*39796254SChristian Brauner 		return false;
38*39796254SChristian Brauner 	while (fgets(line, sizeof(line), f)) {
39*39796254SChristian Brauner 		char *nl = strchr(line, '\n');
40*39796254SChristian Brauner 		char *tab = strchr(line, '\t');
41*39796254SChristian Brauner 
42*39796254SChristian Brauner 		if (nl)
43*39796254SChristian Brauner 			*nl = 0;
44*39796254SChristian Brauner 		if (tab && !strcmp(tab + 1, name))
45*39796254SChristian Brauner 			found = true;
46*39796254SChristian Brauner 	}
47*39796254SChristian Brauner 	fclose(f);
48*39796254SChristian Brauner 	return found;
49*39796254SChristian Brauner }
50*39796254SChristian Brauner 
51*39796254SChristian Brauner static int mnt_id_of(int fd)
52*39796254SChristian Brauner {
53*39796254SChristian Brauner 	char path[64], buf[4096], *p;
54*39796254SChristian Brauner 	ssize_t n;
55*39796254SChristian Brauner 	int info;
56*39796254SChristian Brauner 
57*39796254SChristian Brauner 	snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
58*39796254SChristian Brauner 	info = open(path, O_RDONLY | O_CLOEXEC);
59*39796254SChristian Brauner 	if (info < 0)
60*39796254SChristian Brauner 		return -1;
61*39796254SChristian Brauner 	n = read(info, buf, sizeof(buf) - 1);
62*39796254SChristian Brauner 	close(info);
63*39796254SChristian Brauner 	if (n <= 0)
64*39796254SChristian Brauner 		return -1;
65*39796254SChristian Brauner 	buf[n] = 0;
66*39796254SChristian Brauner 	p = strstr(buf, "mnt_id:");
67*39796254SChristian Brauner 	return p ? atoi(p + strlen("mnt_id:")) : -1;
68*39796254SChristian Brauner }
69*39796254SChristian Brauner 
70*39796254SChristian Brauner static bool on_debugfs(int fd)
71*39796254SChristian Brauner {
72*39796254SChristian Brauner 	struct statfs sf;
73*39796254SChristian Brauner 
74*39796254SChristian Brauner 	return !fstatfs(fd, &sf) && sf.f_type == DEBUGFS_MAGIC;
75*39796254SChristian Brauner }
76*39796254SChristian Brauner 
77*39796254SChristian Brauner FIXTURE(layer) {
78*39796254SChristian Brauner 	char base[DIR_LEN];
79*39796254SChristian Brauner 	int fan;
80*39796254SChristian Brauner 	int evfd;	/* on the layer clone of the lower debugfs */
81*39796254SChristian Brauner };
82*39796254SChristian Brauner 
83*39796254SChristian Brauner FIXTURE_SETUP(layer)
84*39796254SChristian Brauner {
85*39796254SChristian Brauner 	char lower[PATH_LEN], other[PATH_LEN], ovl[PATH_LEN], opts[2 * PATH_LEN + 16];
86*39796254SChristian Brauner 	struct fanotify_event_metadata *ev;
87*39796254SChristian Brauner 	struct pollfd pfd;
88*39796254SChristian Brauner 	char buf[4096];
89*39796254SChristian Brauner 	int lfd, lower_id;
90*39796254SChristian Brauner 	ssize_t n;
91*39796254SChristian Brauner 	DIR *d;
92*39796254SChristian Brauner 
93*39796254SChristian Brauner 	self->fan = -1;
94*39796254SChristian Brauner 	self->evfd = -1;
95*39796254SChristian Brauner 	if (geteuid())
96*39796254SChristian Brauner 		SKIP(return, "test requires root");
97*39796254SChristian Brauner 	if (!have_fs("debugfs") || !have_fs("tracefs"))
98*39796254SChristian Brauner 		SKIP(return, "test requires debugfs with the tracefs automount");
99*39796254SChristian Brauner 	if (!have_fs("overlay"))
100*39796254SChristian Brauner 		SKIP(return, "test requires overlayfs");
101*39796254SChristian Brauner 
102*39796254SChristian Brauner 	snprintf(self->base, sizeof(self->base), "/tmp/layer.XXXXXX");
103*39796254SChristian Brauner 	ASSERT_NE(mkdtemp(self->base), NULL);
104*39796254SChristian Brauner 	ASSERT_EQ(unshare(CLONE_NEWNS), 0);
105*39796254SChristian Brauner 	ASSERT_EQ(mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL), 0);
106*39796254SChristian Brauner 	ASSERT_EQ(mount("tmpfs", self->base, "tmpfs", 0, "mode=0755"), 0);
107*39796254SChristian Brauner 	snprintf(lower, sizeof(lower), "%s/lower", self->base);
108*39796254SChristian Brauner 	snprintf(other, sizeof(other), "%s/other", self->base);
109*39796254SChristian Brauner 	snprintf(ovl, sizeof(ovl), "%s/ovl", self->base);
110*39796254SChristian Brauner 	ASSERT_EQ(mkdir(lower, 0755), 0);
111*39796254SChristian Brauner 	ASSERT_EQ(mkdir(other, 0755), 0);
112*39796254SChristian Brauner 	ASSERT_EQ(mkdir(ovl, 0755), 0);
113*39796254SChristian Brauner 	ASSERT_EQ(mount("debugfs", lower, "debugfs", 0, NULL), 0);
114*39796254SChristian Brauner 	snprintf(opts, sizeof(opts), "lowerdir=%s:%s", lower, other);
115*39796254SChristian Brauner 	ASSERT_EQ(mount("overlay", ovl, "overlay", MS_RDONLY, opts), 0);
116*39796254SChristian Brauner 
117*39796254SChristian Brauner 	self->fan = fanotify_init(FAN_CLASS_NOTIF | FAN_NONBLOCK | FAN_CLOEXEC,
118*39796254SChristian Brauner 				  O_RDONLY | O_CLOEXEC);
119*39796254SChristian Brauner 	ASSERT_GE(self->fan, 0);
120*39796254SChristian Brauner 	ASSERT_EQ(fanotify_mark(self->fan, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
121*39796254SChristian Brauner 				FAN_OPEN | FAN_ONDIR, AT_FDCWD, lower), 0);
122*39796254SChristian Brauner 	lfd = open(lower, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
123*39796254SChristian Brauner 	ASSERT_GE(lfd, 0);
124*39796254SChristian Brauner 	lower_id = mnt_id_of(lfd);
125*39796254SChristian Brauner 	close(lfd);
126*39796254SChristian Brauner 
127*39796254SChristian Brauner 	/* the overlay opens its lower directory through the layer clone */
128*39796254SChristian Brauner 	d = opendir(ovl);
129*39796254SChristian Brauner 	ASSERT_NE(d, NULL);
130*39796254SChristian Brauner 	closedir(d);
131*39796254SChristian Brauner 	pfd.fd = self->fan;
132*39796254SChristian Brauner 	pfd.events = POLLIN;
133*39796254SChristian Brauner 	ASSERT_EQ(poll(&pfd, 1, 5000), 1);
134*39796254SChristian Brauner 	n = read(self->fan, buf, sizeof(buf));
135*39796254SChristian Brauner 	ASSERT_GT(n, 0);
136*39796254SChristian Brauner 	for (ev = (void *)buf; FAN_EVENT_OK(ev, n); ev = FAN_EVENT_NEXT(ev, n)) {
137*39796254SChristian Brauner 		if (ev->fd < 0)
138*39796254SChristian Brauner 			continue;
139*39796254SChristian Brauner 		if (self->evfd < 0 && on_debugfs(ev->fd) &&
140*39796254SChristian Brauner 		    mnt_id_of(ev->fd) != lower_id)
141*39796254SChristian Brauner 			self->evfd = ev->fd;
142*39796254SChristian Brauner 		else
143*39796254SChristian Brauner 			close(ev->fd);
144*39796254SChristian Brauner 	}
145*39796254SChristian Brauner 	ASSERT_GE(self->evfd, 0)
146*39796254SChristian Brauner 		TH_LOG("no event on the layer clone");
147*39796254SChristian Brauner }
148*39796254SChristian Brauner 
149*39796254SChristian Brauner FIXTURE_TEARDOWN(layer)
150*39796254SChristian Brauner {
151*39796254SChristian Brauner 	if (self->evfd >= 0)
152*39796254SChristian Brauner 		close(self->evfd);
153*39796254SChristian Brauner 	if (self->fan >= 0)
154*39796254SChristian Brauner 		close(self->fan);
155*39796254SChristian Brauner 	umount2(self->base, MNT_DETACH);
156*39796254SChristian Brauner 	rmdir(self->base);
157*39796254SChristian Brauner }
158*39796254SChristian Brauner 
159*39796254SChristian Brauner TEST_F(layer, automount_below_the_clone_is_refused)
160*39796254SChristian Brauner {
161*39796254SChristian Brauner 	struct stat st;
162*39796254SChristian Brauner 	int fd;
163*39796254SChristian Brauner 
164*39796254SChristian Brauner 	/* the automount point is there */
165*39796254SChristian Brauner 	ASSERT_EQ(fstatat(self->evfd, "tracing", &st, AT_NO_AUTOMOUNT), 0);
166*39796254SChristian Brauner 	/* the clone is in no namespace, so the automount has nowhere to go */
167*39796254SChristian Brauner 	fd = openat(self->evfd, "tracing", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
168*39796254SChristian Brauner 	EXPECT_LT(fd, 0);
169*39796254SChristian Brauner 	EXPECT_EQ(errno, EINVAL);
170*39796254SChristian Brauner 	if (fd >= 0)
171*39796254SChristian Brauner 		close(fd);
172*39796254SChristian Brauner }
173*39796254SChristian Brauner 
174*39796254SChristian Brauner TEST_HARNESS_MAIN
175