1*2244ca56SChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*2244ca56SChristian Brauner /*
3*2244ca56SChristian Brauner * The root of every empty mount namespace is the same nullfs inode. A read
4*2244ca56SChristian Brauner * of it by one user must not change the access time another user sees.
5*2244ca56SChristian Brauner */
6*2244ca56SChristian Brauner #define _GNU_SOURCE
7*2244ca56SChristian Brauner #include <dirent.h>
8*2244ca56SChristian Brauner #include <errno.h>
9*2244ca56SChristian Brauner #include <fcntl.h>
10*2244ca56SChristian Brauner #include <sched.h>
11*2244ca56SChristian Brauner #include <stdio.h>
12*2244ca56SChristian Brauner #include <stdlib.h>
13*2244ca56SChristian Brauner #include <string.h>
14*2244ca56SChristian Brauner #include <unistd.h>
15*2244ca56SChristian Brauner #include <sys/stat.h>
16*2244ca56SChristian Brauner #include <sys/wait.h>
17*2244ca56SChristian Brauner
18*2244ca56SChristian Brauner #include "../../kselftest_harness.h"
19*2244ca56SChristian Brauner
20*2244ca56SChristian Brauner #ifndef UNSHARE_EMPTY_MNTNS
21*2244ca56SChristian Brauner #define UNSHARE_EMPTY_MNTNS 0x00100000
22*2244ca56SChristian Brauner #endif
23*2244ca56SChristian Brauner
24*2244ca56SChristian Brauner enum {
25*2244ca56SChristian Brauner CHILD_OK,
26*2244ca56SChristian Brauner CHILD_UNSUPPORTED,
27*2244ca56SChristian Brauner CHILD_SETUP,
28*2244ca56SChristian Brauner CHILD_CHANGED,
29*2244ca56SChristian Brauner };
30*2244ca56SChristian Brauner
wait_byte(int fd)31*2244ca56SChristian Brauner static int wait_byte(int fd)
32*2244ca56SChristian Brauner {
33*2244ca56SChristian Brauner char c;
34*2244ca56SChristian Brauner
35*2244ca56SChristian Brauner return read(fd, &c, 1) == 1 ? 0 : -1;
36*2244ca56SChristian Brauner }
37*2244ca56SChristian Brauner
send_byte(int fd)38*2244ca56SChristian Brauner static int send_byte(int fd)
39*2244ca56SChristian Brauner {
40*2244ca56SChristian Brauner return write(fd, "x", 1) == 1 ? 0 : -1;
41*2244ca56SChristian Brauner }
42*2244ca56SChristian Brauner
empty_mntns(void)43*2244ca56SChristian Brauner static int empty_mntns(void)
44*2244ca56SChristian Brauner {
45*2244ca56SChristian Brauner if (!unshare(UNSHARE_EMPTY_MNTNS))
46*2244ca56SChristian Brauner return 0;
47*2244ca56SChristian Brauner return errno == EINVAL ? CHILD_UNSUPPORTED : CHILD_SETUP;
48*2244ca56SChristian Brauner }
49*2244ca56SChristian Brauner
50*2244ca56SChristian Brauner /* the watcher: stats its root before and after the reader read its own */
watcher(int to_reader,int from_reader)51*2244ca56SChristian Brauner static int watcher(int to_reader, int from_reader)
52*2244ca56SChristian Brauner {
53*2244ca56SChristian Brauner struct stat before, after;
54*2244ca56SChristian Brauner int ret;
55*2244ca56SChristian Brauner
56*2244ca56SChristian Brauner ret = empty_mntns();
57*2244ca56SChristian Brauner if (ret)
58*2244ca56SChristian Brauner return ret;
59*2244ca56SChristian Brauner if (stat("/", &before))
60*2244ca56SChristian Brauner return CHILD_SETUP;
61*2244ca56SChristian Brauner if (send_byte(to_reader) || wait_byte(from_reader))
62*2244ca56SChristian Brauner return CHILD_SETUP;
63*2244ca56SChristian Brauner if (stat("/", &after))
64*2244ca56SChristian Brauner return CHILD_SETUP;
65*2244ca56SChristian Brauner if (before.st_atim.tv_sec != after.st_atim.tv_sec ||
66*2244ca56SChristian Brauner before.st_atim.tv_nsec != after.st_atim.tv_nsec)
67*2244ca56SChristian Brauner return CHILD_CHANGED;
68*2244ca56SChristian Brauner return CHILD_OK;
69*2244ca56SChristian Brauner }
70*2244ca56SChristian Brauner
71*2244ca56SChristian Brauner /* the reader: lists its own root, which is the same inode */
reader(int to_watcher,int from_watcher)72*2244ca56SChristian Brauner static int reader(int to_watcher, int from_watcher)
73*2244ca56SChristian Brauner {
74*2244ca56SChristian Brauner struct dirent *de;
75*2244ca56SChristian Brauner DIR *d;
76*2244ca56SChristian Brauner int ret;
77*2244ca56SChristian Brauner
78*2244ca56SChristian Brauner ret = empty_mntns();
79*2244ca56SChristian Brauner if (ret)
80*2244ca56SChristian Brauner return ret;
81*2244ca56SChristian Brauner if (wait_byte(from_watcher))
82*2244ca56SChristian Brauner return CHILD_SETUP;
83*2244ca56SChristian Brauner d = opendir("/");
84*2244ca56SChristian Brauner if (!d)
85*2244ca56SChristian Brauner return CHILD_SETUP;
86*2244ca56SChristian Brauner while ((de = readdir(d)))
87*2244ca56SChristian Brauner ;
88*2244ca56SChristian Brauner closedir(d);
89*2244ca56SChristian Brauner return send_byte(to_watcher) ? CHILD_SETUP : CHILD_OK;
90*2244ca56SChristian Brauner }
91*2244ca56SChristian Brauner
wait_child(pid_t pid)92*2244ca56SChristian Brauner static int wait_child(pid_t pid)
93*2244ca56SChristian Brauner {
94*2244ca56SChristian Brauner int status;
95*2244ca56SChristian Brauner
96*2244ca56SChristian Brauner if (waitpid(pid, &status, 0) != pid || !WIFEXITED(status))
97*2244ca56SChristian Brauner return -1;
98*2244ca56SChristian Brauner return WEXITSTATUS(status);
99*2244ca56SChristian Brauner }
100*2244ca56SChristian Brauner
TEST(empty_mntns_root_atime)101*2244ca56SChristian Brauner TEST(empty_mntns_root_atime)
102*2244ca56SChristian Brauner {
103*2244ca56SChristian Brauner int to_reader[2], to_watcher[2], w, r;
104*2244ca56SChristian Brauner pid_t watcher_pid, reader_pid;
105*2244ca56SChristian Brauner
106*2244ca56SChristian Brauner if (geteuid())
107*2244ca56SChristian Brauner SKIP(return, "test requires root");
108*2244ca56SChristian Brauner ASSERT_EQ(pipe(to_reader), 0);
109*2244ca56SChristian Brauner ASSERT_EQ(pipe(to_watcher), 0);
110*2244ca56SChristian Brauner
111*2244ca56SChristian Brauner watcher_pid = fork();
112*2244ca56SChristian Brauner ASSERT_GE(watcher_pid, 0);
113*2244ca56SChristian Brauner if (watcher_pid == 0)
114*2244ca56SChristian Brauner _exit(watcher(to_reader[1], to_watcher[0]));
115*2244ca56SChristian Brauner reader_pid = fork();
116*2244ca56SChristian Brauner ASSERT_GE(reader_pid, 0);
117*2244ca56SChristian Brauner if (reader_pid == 0)
118*2244ca56SChristian Brauner _exit(reader(to_watcher[1], to_reader[0]));
119*2244ca56SChristian Brauner
120*2244ca56SChristian Brauner r = wait_child(reader_pid);
121*2244ca56SChristian Brauner w = wait_child(watcher_pid);
122*2244ca56SChristian Brauner if (r == CHILD_UNSUPPORTED || w == CHILD_UNSUPPORTED)
123*2244ca56SChristian Brauner SKIP(return, "UNSHARE_EMPTY_MNTNS not supported");
124*2244ca56SChristian Brauner EXPECT_EQ(r, CHILD_OK);
125*2244ca56SChristian Brauner EXPECT_EQ(w, CHILD_OK)
126*2244ca56SChristian Brauner TH_LOG("the access time of the root changed while this namespace did nothing");
127*2244ca56SChristian Brauner }
128*2244ca56SChristian Brauner
129*2244ca56SChristian Brauner TEST_HARNESS_MAIN
130