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