xref: /openbmc/linux/tools/testing/selftests/landlock/fs_test.c (revision 366617a69e60610912836570546f118006ebc7cb)
1e1199815SMickaël Salaün // SPDX-License-Identifier: GPL-2.0
2e1199815SMickaël Salaün /*
3e1199815SMickaël Salaün  * Landlock tests - Filesystem
4e1199815SMickaël Salaün  *
5e1199815SMickaël Salaün  * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6e1199815SMickaël Salaün  * Copyright © 2020 ANSSI
755e55920SMickaël Salaün  * Copyright © 2020-2022 Microsoft Corporation
8e1199815SMickaël Salaün  */
9e1199815SMickaël Salaün 
10e1199815SMickaël Salaün #define _GNU_SOURCE
11e1199815SMickaël Salaün #include <fcntl.h>
12e1199815SMickaël Salaün #include <linux/landlock.h>
13e1199815SMickaël Salaün #include <sched.h>
14*366617a6SJeff Xu #include <stdio.h>
15e1199815SMickaël Salaün #include <string.h>
16e1199815SMickaël Salaün #include <sys/capability.h>
17e1199815SMickaël Salaün #include <sys/mount.h>
18e1199815SMickaël Salaün #include <sys/prctl.h>
19e1199815SMickaël Salaün #include <sys/sendfile.h>
20e1199815SMickaël Salaün #include <sys/stat.h>
21e1199815SMickaël Salaün #include <sys/sysmacros.h>
22e1199815SMickaël Salaün #include <unistd.h>
23e1199815SMickaël Salaün 
24e1199815SMickaël Salaün #include "common.h"
25e1199815SMickaël Salaün 
2687129ef1SMickaël Salaün #ifndef renameat2
2787129ef1SMickaël Salaün int renameat2(int olddirfd, const char *oldpath, int newdirfd,
2887129ef1SMickaël Salaün 	      const char *newpath, unsigned int flags)
2987129ef1SMickaël Salaün {
3087129ef1SMickaël Salaün 	return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath,
3187129ef1SMickaël Salaün 		       flags);
3287129ef1SMickaël Salaün }
3387129ef1SMickaël Salaün #endif
3487129ef1SMickaël Salaün 
3587129ef1SMickaël Salaün #ifndef RENAME_EXCHANGE
3687129ef1SMickaël Salaün #define RENAME_EXCHANGE (1 << 1)
3787129ef1SMickaël Salaün #endif
3887129ef1SMickaël Salaün 
39e1199815SMickaël Salaün #define TMP_DIR "tmp"
40e1199815SMickaël Salaün #define BINARY_PATH "./true"
41e1199815SMickaël Salaün 
42e1199815SMickaël Salaün /* Paths (sibling number and depth) */
43e1199815SMickaël Salaün static const char dir_s1d1[] = TMP_DIR "/s1d1";
44e1199815SMickaël Salaün static const char file1_s1d1[] = TMP_DIR "/s1d1/f1";
45e1199815SMickaël Salaün static const char file2_s1d1[] = TMP_DIR "/s1d1/f2";
46e1199815SMickaël Salaün static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2";
47e1199815SMickaël Salaün static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1";
48e1199815SMickaël Salaün static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2";
49e1199815SMickaël Salaün static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3";
50e1199815SMickaël Salaün static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1";
51e1199815SMickaël Salaün static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2";
52e1199815SMickaël Salaün 
53e1199815SMickaël Salaün static const char dir_s2d1[] = TMP_DIR "/s2d1";
54e1199815SMickaël Salaün static const char file1_s2d1[] = TMP_DIR "/s2d1/f1";
55e1199815SMickaël Salaün static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2";
56e1199815SMickaël Salaün static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1";
57e1199815SMickaël Salaün static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3";
58e1199815SMickaël Salaün static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1";
59e1199815SMickaël Salaün static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2";
60e1199815SMickaël Salaün 
61e1199815SMickaël Salaün static const char dir_s3d1[] = TMP_DIR "/s3d1";
62225351abSGünther Noack static const char file1_s3d1[] = TMP_DIR "/s3d1/f1";
63e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */
64e1199815SMickaël Salaün static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2";
65e1199815SMickaël Salaün static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3";
66e1199815SMickaël Salaün 
67e1199815SMickaël Salaün /*
68e1199815SMickaël Salaün  * layout1 hierarchy:
69e1199815SMickaël Salaün  *
70e1199815SMickaël Salaün  * tmp
71e1199815SMickaël Salaün  * ├── s1d1
72e1199815SMickaël Salaün  * │   ├── f1
73e1199815SMickaël Salaün  * │   ├── f2
74e1199815SMickaël Salaün  * │   └── s1d2
75e1199815SMickaël Salaün  * │       ├── f1
76e1199815SMickaël Salaün  * │       ├── f2
77e1199815SMickaël Salaün  * │       └── s1d3
78e1199815SMickaël Salaün  * │           ├── f1
79e1199815SMickaël Salaün  * │           └── f2
80e1199815SMickaël Salaün  * ├── s2d1
81e1199815SMickaël Salaün  * │   ├── f1
82e1199815SMickaël Salaün  * │   └── s2d2
83e1199815SMickaël Salaün  * │       ├── f1
84e1199815SMickaël Salaün  * │       └── s2d3
85e1199815SMickaël Salaün  * │           ├── f1
86e1199815SMickaël Salaün  * │           └── f2
87e1199815SMickaël Salaün  * └── s3d1
88225351abSGünther Noack  *     ├── f1
89e1199815SMickaël Salaün  *     └── s3d2
90e1199815SMickaël Salaün  *         └── s3d3
91e1199815SMickaël Salaün  */
92e1199815SMickaël Salaün 
93*366617a6SJeff Xu static bool fgrep(FILE *const inf, const char *const str)
94*366617a6SJeff Xu {
95*366617a6SJeff Xu 	char line[32];
96*366617a6SJeff Xu 	const int slen = strlen(str);
97*366617a6SJeff Xu 
98*366617a6SJeff Xu 	while (!feof(inf)) {
99*366617a6SJeff Xu 		if (!fgets(line, sizeof(line), inf))
100*366617a6SJeff Xu 			break;
101*366617a6SJeff Xu 		if (strncmp(line, str, slen))
102*366617a6SJeff Xu 			continue;
103*366617a6SJeff Xu 
104*366617a6SJeff Xu 		return true;
105*366617a6SJeff Xu 	}
106*366617a6SJeff Xu 
107*366617a6SJeff Xu 	return false;
108*366617a6SJeff Xu }
109*366617a6SJeff Xu 
110*366617a6SJeff Xu static bool supports_overlayfs(void)
111*366617a6SJeff Xu {
112*366617a6SJeff Xu 	bool res;
113*366617a6SJeff Xu 	FILE *const inf = fopen("/proc/filesystems", "r");
114*366617a6SJeff Xu 
115*366617a6SJeff Xu 	/*
116*366617a6SJeff Xu 	 * Consider that the filesystem is supported if we cannot get the
117*366617a6SJeff Xu 	 * supported ones.
118*366617a6SJeff Xu 	 */
119*366617a6SJeff Xu 	if (!inf)
120*366617a6SJeff Xu 		return true;
121*366617a6SJeff Xu 
122*366617a6SJeff Xu 	res = fgrep(inf, "nodev\toverlay\n");
123*366617a6SJeff Xu 	fclose(inf);
124*366617a6SJeff Xu 	return res;
125*366617a6SJeff Xu }
126*366617a6SJeff Xu 
127e1199815SMickaël Salaün static void mkdir_parents(struct __test_metadata *const _metadata,
128e1199815SMickaël Salaün 			  const char *const path)
129e1199815SMickaël Salaün {
130e1199815SMickaël Salaün 	char *walker;
131e1199815SMickaël Salaün 	const char *parent;
132e1199815SMickaël Salaün 	int i, err;
133e1199815SMickaël Salaün 
134e1199815SMickaël Salaün 	ASSERT_NE(path[0], '\0');
135e1199815SMickaël Salaün 	walker = strdup(path);
136e1199815SMickaël Salaün 	ASSERT_NE(NULL, walker);
137e1199815SMickaël Salaün 	parent = walker;
138e1199815SMickaël Salaün 	for (i = 1; walker[i]; i++) {
139e1199815SMickaël Salaün 		if (walker[i] != '/')
140e1199815SMickaël Salaün 			continue;
141e1199815SMickaël Salaün 		walker[i] = '\0';
142e1199815SMickaël Salaün 		err = mkdir(parent, 0700);
143371183faSMickaël Salaün 		ASSERT_FALSE(err && errno != EEXIST)
144371183faSMickaël Salaün 		{
145371183faSMickaël Salaün 			TH_LOG("Failed to create directory \"%s\": %s", parent,
146371183faSMickaël Salaün 			       strerror(errno));
147e1199815SMickaël Salaün 		}
148e1199815SMickaël Salaün 		walker[i] = '/';
149e1199815SMickaël Salaün 	}
150e1199815SMickaël Salaün 	free(walker);
151e1199815SMickaël Salaün }
152e1199815SMickaël Salaün 
153e1199815SMickaël Salaün static void create_directory(struct __test_metadata *const _metadata,
154e1199815SMickaël Salaün 			     const char *const path)
155e1199815SMickaël Salaün {
156e1199815SMickaël Salaün 	mkdir_parents(_metadata, path);
157371183faSMickaël Salaün 	ASSERT_EQ(0, mkdir(path, 0700))
158371183faSMickaël Salaün 	{
159e1199815SMickaël Salaün 		TH_LOG("Failed to create directory \"%s\": %s", path,
160e1199815SMickaël Salaün 		       strerror(errno));
161e1199815SMickaël Salaün 	}
162e1199815SMickaël Salaün }
163e1199815SMickaël Salaün 
164e1199815SMickaël Salaün static void create_file(struct __test_metadata *const _metadata,
165e1199815SMickaël Salaün 			const char *const path)
166e1199815SMickaël Salaün {
167e1199815SMickaël Salaün 	mkdir_parents(_metadata, path);
168371183faSMickaël Salaün 	ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0))
169371183faSMickaël Salaün 	{
170e1199815SMickaël Salaün 		TH_LOG("Failed to create file \"%s\": %s", path,
171e1199815SMickaël Salaün 		       strerror(errno));
172e1199815SMickaël Salaün 	}
173e1199815SMickaël Salaün }
174e1199815SMickaël Salaün 
175e1199815SMickaël Salaün static int remove_path(const char *const path)
176e1199815SMickaël Salaün {
177e1199815SMickaël Salaün 	char *walker;
178e1199815SMickaël Salaün 	int i, ret, err = 0;
179e1199815SMickaël Salaün 
180e1199815SMickaël Salaün 	walker = strdup(path);
181e1199815SMickaël Salaün 	if (!walker) {
182e1199815SMickaël Salaün 		err = ENOMEM;
183e1199815SMickaël Salaün 		goto out;
184e1199815SMickaël Salaün 	}
185e1199815SMickaël Salaün 	if (unlink(path) && rmdir(path)) {
186f4056b92SMickaël Salaün 		if (errno != ENOENT && errno != ENOTDIR)
187e1199815SMickaël Salaün 			err = errno;
188e1199815SMickaël Salaün 		goto out;
189e1199815SMickaël Salaün 	}
190e1199815SMickaël Salaün 	for (i = strlen(walker); i > 0; i--) {
191e1199815SMickaël Salaün 		if (walker[i] != '/')
192e1199815SMickaël Salaün 			continue;
193e1199815SMickaël Salaün 		walker[i] = '\0';
194e1199815SMickaël Salaün 		ret = rmdir(walker);
195e1199815SMickaël Salaün 		if (ret) {
196e1199815SMickaël Salaün 			if (errno != ENOTEMPTY && errno != EBUSY)
197e1199815SMickaël Salaün 				err = errno;
198e1199815SMickaël Salaün 			goto out;
199e1199815SMickaël Salaün 		}
200e1199815SMickaël Salaün 		if (strcmp(walker, TMP_DIR) == 0)
201e1199815SMickaël Salaün 			goto out;
202e1199815SMickaël Salaün 	}
203e1199815SMickaël Salaün 
204e1199815SMickaël Salaün out:
205e1199815SMickaël Salaün 	free(walker);
206e1199815SMickaël Salaün 	return err;
207e1199815SMickaël Salaün }
208e1199815SMickaël Salaün 
209e1199815SMickaël Salaün static void prepare_layout(struct __test_metadata *const _metadata)
210e1199815SMickaël Salaün {
211e1199815SMickaël Salaün 	disable_caps(_metadata);
212e1199815SMickaël Salaün 	umask(0077);
213e1199815SMickaël Salaün 	create_directory(_metadata, TMP_DIR);
214e1199815SMickaël Salaün 
215e1199815SMickaël Salaün 	/*
216e1199815SMickaël Salaün 	 * Do not pollute the rest of the system: creates a private mount point
217e1199815SMickaël Salaün 	 * for tests relying on pivot_root(2) and move_mount(2).
218e1199815SMickaël Salaün 	 */
219e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
220e1199815SMickaël Salaün 	ASSERT_EQ(0, unshare(CLONE_NEWNS));
221e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("tmp", TMP_DIR, "tmpfs", 0, "size=4m,mode=700"));
222e1199815SMickaël Salaün 	ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));
223e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
224e1199815SMickaël Salaün }
225e1199815SMickaël Salaün 
226e1199815SMickaël Salaün static void cleanup_layout(struct __test_metadata *const _metadata)
227e1199815SMickaël Salaün {
228e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
229e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(TMP_DIR));
230e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
231e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(TMP_DIR));
232e1199815SMickaël Salaün }
233e1199815SMickaël Salaün 
234e1199815SMickaël Salaün static void create_layout1(struct __test_metadata *const _metadata)
235e1199815SMickaël Salaün {
236e1199815SMickaël Salaün 	create_file(_metadata, file1_s1d1);
237e1199815SMickaël Salaün 	create_file(_metadata, file1_s1d2);
238e1199815SMickaël Salaün 	create_file(_metadata, file1_s1d3);
239e1199815SMickaël Salaün 	create_file(_metadata, file2_s1d1);
240e1199815SMickaël Salaün 	create_file(_metadata, file2_s1d2);
241e1199815SMickaël Salaün 	create_file(_metadata, file2_s1d3);
242e1199815SMickaël Salaün 
243e1199815SMickaël Salaün 	create_file(_metadata, file1_s2d1);
244e1199815SMickaël Salaün 	create_file(_metadata, file1_s2d2);
245e1199815SMickaël Salaün 	create_file(_metadata, file1_s2d3);
246e1199815SMickaël Salaün 	create_file(_metadata, file2_s2d3);
247e1199815SMickaël Salaün 
248225351abSGünther Noack 	create_file(_metadata, file1_s3d1);
249e1199815SMickaël Salaün 	create_directory(_metadata, dir_s3d2);
250e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
251e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700"));
252e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
253e1199815SMickaël Salaün 
254e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s3d3, 0700));
255e1199815SMickaël Salaün }
256e1199815SMickaël Salaün 
257e1199815SMickaël Salaün static void remove_layout1(struct __test_metadata *const _metadata)
258e1199815SMickaël Salaün {
259e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s1d3));
260e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s1d2));
261e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s1d1));
262e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s1d3));
263e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s1d2));
264e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s1d1));
265e1199815SMickaël Salaün 
266e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s2d3));
267e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s2d3));
268e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s2d2));
269e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s2d1));
270e1199815SMickaël Salaün 
271225351abSGünther Noack 	EXPECT_EQ(0, remove_path(file1_s3d1));
272e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(dir_s3d3));
273e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
274e1199815SMickaël Salaün 	umount(dir_s3d2);
275e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
276e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(dir_s3d2));
277e1199815SMickaël Salaün }
278e1199815SMickaël Salaün 
2794598d9abSMickaël Salaün /* clang-format off */
2804598d9abSMickaël Salaün FIXTURE(layout1) {};
2814598d9abSMickaël Salaün /* clang-format on */
282e1199815SMickaël Salaün 
283e1199815SMickaël Salaün FIXTURE_SETUP(layout1)
284e1199815SMickaël Salaün {
285e1199815SMickaël Salaün 	prepare_layout(_metadata);
286e1199815SMickaël Salaün 
287e1199815SMickaël Salaün 	create_layout1(_metadata);
288e1199815SMickaël Salaün }
289e1199815SMickaël Salaün 
290e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1)
291e1199815SMickaël Salaün {
292e1199815SMickaël Salaün 	remove_layout1(_metadata);
293e1199815SMickaël Salaün 
294e1199815SMickaël Salaün 	cleanup_layout(_metadata);
295e1199815SMickaël Salaün }
296e1199815SMickaël Salaün 
297e1199815SMickaël Salaün /*
298e1199815SMickaël Salaün  * This helper enables to use the ASSERT_* macros and print the line number
299e1199815SMickaël Salaün  * pointing to the test caller.
300e1199815SMickaël Salaün  */
301371183faSMickaël Salaün static int test_open_rel(const int dirfd, const char *const path,
302371183faSMickaël Salaün 			 const int flags)
303e1199815SMickaël Salaün {
304e1199815SMickaël Salaün 	int fd;
305e1199815SMickaël Salaün 
306e1199815SMickaël Salaün 	/* Works with file and directories. */
307e1199815SMickaël Salaün 	fd = openat(dirfd, path, flags | O_CLOEXEC);
308e1199815SMickaël Salaün 	if (fd < 0)
309e1199815SMickaël Salaün 		return errno;
310e1199815SMickaël Salaün 	/*
311e1199815SMickaël Salaün 	 * Mixing error codes from close(2) and open(2) should not lead to any
312e1199815SMickaël Salaün 	 * (access type) confusion for this test.
313e1199815SMickaël Salaün 	 */
314e1199815SMickaël Salaün 	if (close(fd) != 0)
315e1199815SMickaël Salaün 		return errno;
316e1199815SMickaël Salaün 	return 0;
317e1199815SMickaël Salaün }
318e1199815SMickaël Salaün 
319e1199815SMickaël Salaün static int test_open(const char *const path, const int flags)
320e1199815SMickaël Salaün {
321e1199815SMickaël Salaün 	return test_open_rel(AT_FDCWD, path, flags);
322e1199815SMickaël Salaün }
323e1199815SMickaël Salaün 
324e1199815SMickaël Salaün TEST_F_FORK(layout1, no_restriction)
325e1199815SMickaël Salaün {
326e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
327e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
328e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY));
329e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
330e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
331e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY));
332e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
333e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
334e1199815SMickaël Salaün 
335e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
336e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
337e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
338e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
339e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY));
340e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY));
341e1199815SMickaël Salaün 
342e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
343e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
344e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
345e1199815SMickaël Salaün }
346e1199815SMickaël Salaün 
347e1199815SMickaël Salaün TEST_F_FORK(layout1, inval)
348e1199815SMickaël Salaün {
349e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath = {
350e1199815SMickaël Salaün 		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
351e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
352e1199815SMickaël Salaün 		.parent_fd = -1,
353e1199815SMickaël Salaün 	};
354e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {
355e1199815SMickaël Salaün 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |
356e1199815SMickaël Salaün 				     LANDLOCK_ACCESS_FS_WRITE_FILE,
357e1199815SMickaël Salaün 	};
358e1199815SMickaël Salaün 	int ruleset_fd;
359e1199815SMickaël Salaün 
360371183faSMickaël Salaün 	path_beneath.parent_fd =
361371183faSMickaël Salaün 		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
362e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
363e1199815SMickaël Salaün 
364e1199815SMickaël Salaün 	ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);
365e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
366e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
367e1199815SMickaël Salaün 					&path_beneath, 0));
368e1199815SMickaël Salaün 	/* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */
369e1199815SMickaël Salaün 	ASSERT_EQ(EBADF, errno);
370e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
371e1199815SMickaël Salaün 
372e1199815SMickaël Salaün 	ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC);
373e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
374e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
375e1199815SMickaël Salaün 					&path_beneath, 0));
376e1199815SMickaël Salaün 	/* Returns EBADFD because ruleset_fd is not a valid ruleset. */
377e1199815SMickaël Salaün 	ASSERT_EQ(EBADFD, errno);
378e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
379e1199815SMickaël Salaün 
380e1199815SMickaël Salaün 	/* Gets a real ruleset. */
381371183faSMickaël Salaün 	ruleset_fd =
382371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
383e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
384e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
385e1199815SMickaël Salaün 				       &path_beneath, 0));
386e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
387e1199815SMickaël Salaün 
388e1199815SMickaël Salaün 	/* Tests without O_PATH. */
389e1199815SMickaël Salaün 	path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC);
390e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
391e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
392e1199815SMickaël Salaün 				       &path_beneath, 0));
393e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
394e1199815SMickaël Salaün 
395e1199815SMickaël Salaün 	/* Tests with a ruleset FD. */
396e1199815SMickaël Salaün 	path_beneath.parent_fd = ruleset_fd;
397e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
398e1199815SMickaël Salaün 					&path_beneath, 0));
399e1199815SMickaël Salaün 	ASSERT_EQ(EBADFD, errno);
400e1199815SMickaël Salaün 
401e1199815SMickaël Salaün 	/* Checks unhandled allowed_access. */
402371183faSMickaël Salaün 	path_beneath.parent_fd =
403371183faSMickaël Salaün 		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
404e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
405e1199815SMickaël Salaün 
406e1199815SMickaël Salaün 	/* Test with legitimate values. */
407e1199815SMickaël Salaün 	path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE;
408e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
409e1199815SMickaël Salaün 					&path_beneath, 0));
410e1199815SMickaël Salaün 	ASSERT_EQ(EINVAL, errno);
411e1199815SMickaël Salaün 	path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE;
412e1199815SMickaël Salaün 
41355e55920SMickaël Salaün 	/* Tests with denied-by-default access right. */
41455e55920SMickaël Salaün 	path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER;
41555e55920SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
41655e55920SMickaël Salaün 					&path_beneath, 0));
41755e55920SMickaël Salaün 	ASSERT_EQ(EINVAL, errno);
41855e55920SMickaël Salaün 	path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER;
41955e55920SMickaël Salaün 
420e1199815SMickaël Salaün 	/* Test with unknown (64-bits) value. */
421e1199815SMickaël Salaün 	path_beneath.allowed_access |= (1ULL << 60);
422e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
423e1199815SMickaël Salaün 					&path_beneath, 0));
424e1199815SMickaël Salaün 	ASSERT_EQ(EINVAL, errno);
425e1199815SMickaël Salaün 	path_beneath.allowed_access &= ~(1ULL << 60);
426e1199815SMickaël Salaün 
427e1199815SMickaël Salaün 	/* Test with no access. */
428e1199815SMickaël Salaün 	path_beneath.allowed_access = 0;
429e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
430e1199815SMickaël Salaün 					&path_beneath, 0));
431e1199815SMickaël Salaün 	ASSERT_EQ(ENOMSG, errno);
432e1199815SMickaël Salaün 	path_beneath.allowed_access &= ~(1ULL << 60);
433e1199815SMickaël Salaün 
434e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
435e1199815SMickaël Salaün 
436e1199815SMickaël Salaün 	/* Enforces the ruleset. */
437e1199815SMickaël Salaün 	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
438e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
439e1199815SMickaël Salaün 
440e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
441e1199815SMickaël Salaün }
442e1199815SMickaël Salaün 
4434598d9abSMickaël Salaün /* clang-format off */
4444598d9abSMickaël Salaün 
445e1199815SMickaël Salaün #define ACCESS_FILE ( \
446e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_EXECUTE | \
447e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_WRITE_FILE | \
448b9f5ce27SGünther Noack 	LANDLOCK_ACCESS_FS_READ_FILE | \
449b9f5ce27SGünther Noack 	LANDLOCK_ACCESS_FS_TRUNCATE)
450e1199815SMickaël Salaün 
451b9f5ce27SGünther Noack #define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE
452e1199815SMickaël Salaün 
453e1199815SMickaël Salaün #define ACCESS_ALL ( \
454e1199815SMickaël Salaün 	ACCESS_FILE | \
455e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_DIR | \
456e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_REMOVE_DIR | \
457e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_REMOVE_FILE | \
458e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_CHAR | \
459e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_DIR | \
460e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_REG | \
461e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_SOCK | \
462e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_FIFO | \
463e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
464b91c3e4eSMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_SYM | \
465b9f5ce27SGünther Noack 	LANDLOCK_ACCESS_FS_REFER)
466e1199815SMickaël Salaün 
4674598d9abSMickaël Salaün /* clang-format on */
4684598d9abSMickaël Salaün 
469d18955d0SMickaël Salaün TEST_F_FORK(layout1, file_and_dir_access_rights)
470e1199815SMickaël Salaün {
471e1199815SMickaël Salaün 	__u64 access;
472e1199815SMickaël Salaün 	int err;
473d18955d0SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath_file = {},
474d18955d0SMickaël Salaün 					  path_beneath_dir = {};
475e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {
476e1199815SMickaël Salaün 		.handled_access_fs = ACCESS_ALL,
477e1199815SMickaël Salaün 	};
478371183faSMickaël Salaün 	const int ruleset_fd =
479371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
480e1199815SMickaël Salaün 
481e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
482e1199815SMickaël Salaün 
483e1199815SMickaël Salaün 	/* Tests access rights for files. */
484d18955d0SMickaël Salaün 	path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);
485d18955d0SMickaël Salaün 	ASSERT_LE(0, path_beneath_file.parent_fd);
486d18955d0SMickaël Salaün 
487d18955d0SMickaël Salaün 	/* Tests access rights for directories. */
488d18955d0SMickaël Salaün 	path_beneath_dir.parent_fd =
489d18955d0SMickaël Salaün 		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
490d18955d0SMickaël Salaün 	ASSERT_LE(0, path_beneath_dir.parent_fd);
491d18955d0SMickaël Salaün 
492e1199815SMickaël Salaün 	for (access = 1; access <= ACCESS_LAST; access <<= 1) {
493d18955d0SMickaël Salaün 		path_beneath_dir.allowed_access = access;
494d18955d0SMickaël Salaün 		ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
495d18955d0SMickaël Salaün 					       LANDLOCK_RULE_PATH_BENEATH,
496d18955d0SMickaël Salaün 					       &path_beneath_dir, 0));
497d18955d0SMickaël Salaün 
498d18955d0SMickaël Salaün 		path_beneath_file.allowed_access = access;
499e1199815SMickaël Salaün 		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
500d18955d0SMickaël Salaün 					&path_beneath_file, 0);
501d18955d0SMickaël Salaün 		if (access & ACCESS_FILE) {
502e1199815SMickaël Salaün 			ASSERT_EQ(0, err);
503e1199815SMickaël Salaün 		} else {
504e1199815SMickaël Salaün 			ASSERT_EQ(-1, err);
505e1199815SMickaël Salaün 			ASSERT_EQ(EINVAL, errno);
506e1199815SMickaël Salaün 		}
507e1199815SMickaël Salaün 	}
508d18955d0SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath_file.parent_fd));
509d18955d0SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath_dir.parent_fd));
510d18955d0SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
511e1199815SMickaël Salaün }
512e1199815SMickaël Salaün 
513c56b3bf5SMickaël Salaün TEST_F_FORK(layout1, unknown_access_rights)
514c56b3bf5SMickaël Salaün {
515c56b3bf5SMickaël Salaün 	__u64 access_mask;
516c56b3bf5SMickaël Salaün 
517c56b3bf5SMickaël Salaün 	for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST;
518c56b3bf5SMickaël Salaün 	     access_mask >>= 1) {
519c56b3bf5SMickaël Salaün 		struct landlock_ruleset_attr ruleset_attr = {
520c56b3bf5SMickaël Salaün 			.handled_access_fs = access_mask,
521c56b3bf5SMickaël Salaün 		};
522c56b3bf5SMickaël Salaün 
523c56b3bf5SMickaël Salaün 		ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
524c56b3bf5SMickaël Salaün 						      sizeof(ruleset_attr), 0));
525c56b3bf5SMickaël Salaün 		ASSERT_EQ(EINVAL, errno);
526c56b3bf5SMickaël Salaün 	}
527c56b3bf5SMickaël Salaün }
528c56b3bf5SMickaël Salaün 
529e1199815SMickaël Salaün static void add_path_beneath(struct __test_metadata *const _metadata,
530e1199815SMickaël Salaün 			     const int ruleset_fd, const __u64 allowed_access,
531e1199815SMickaël Salaün 			     const char *const path)
532e1199815SMickaël Salaün {
533e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath = {
534e1199815SMickaël Salaün 		.allowed_access = allowed_access,
535e1199815SMickaël Salaün 	};
536e1199815SMickaël Salaün 
537e1199815SMickaël Salaün 	path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC);
538371183faSMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd)
539371183faSMickaël Salaün 	{
540e1199815SMickaël Salaün 		TH_LOG("Failed to open directory \"%s\": %s", path,
541e1199815SMickaël Salaün 		       strerror(errno));
542e1199815SMickaël Salaün 	}
543e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
544371183faSMickaël Salaün 				       &path_beneath, 0))
545371183faSMickaël Salaün 	{
546e1199815SMickaël Salaün 		TH_LOG("Failed to update the ruleset with \"%s\": %s", path,
547e1199815SMickaël Salaün 		       strerror(errno));
548e1199815SMickaël Salaün 	}
549e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
550e1199815SMickaël Salaün }
551e1199815SMickaël Salaün 
552e1199815SMickaël Salaün struct rule {
553e1199815SMickaël Salaün 	const char *path;
554e1199815SMickaël Salaün 	__u64 access;
555e1199815SMickaël Salaün };
556e1199815SMickaël Salaün 
5574598d9abSMickaël Salaün /* clang-format off */
5584598d9abSMickaël Salaün 
559e1199815SMickaël Salaün #define ACCESS_RO ( \
560e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_FILE | \
561e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_DIR)
562e1199815SMickaël Salaün 
563e1199815SMickaël Salaün #define ACCESS_RW ( \
564e1199815SMickaël Salaün 	ACCESS_RO | \
565e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_WRITE_FILE)
566e1199815SMickaël Salaün 
5674598d9abSMickaël Salaün /* clang-format on */
5684598d9abSMickaël Salaün 
569e1199815SMickaël Salaün static int create_ruleset(struct __test_metadata *const _metadata,
570371183faSMickaël Salaün 			  const __u64 handled_access_fs,
571371183faSMickaël Salaün 			  const struct rule rules[])
572e1199815SMickaël Salaün {
573e1199815SMickaël Salaün 	int ruleset_fd, i;
574e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {
575e1199815SMickaël Salaün 		.handled_access_fs = handled_access_fs,
576e1199815SMickaël Salaün 	};
577e1199815SMickaël Salaün 
578371183faSMickaël Salaün 	ASSERT_NE(NULL, rules)
579371183faSMickaël Salaün 	{
580e1199815SMickaël Salaün 		TH_LOG("No rule list");
581e1199815SMickaël Salaün 	}
582371183faSMickaël Salaün 	ASSERT_NE(NULL, rules[0].path)
583371183faSMickaël Salaün 	{
584e1199815SMickaël Salaün 		TH_LOG("Empty rule list");
585e1199815SMickaël Salaün 	}
586e1199815SMickaël Salaün 
587371183faSMickaël Salaün 	ruleset_fd =
588371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
589371183faSMickaël Salaün 	ASSERT_LE(0, ruleset_fd)
590371183faSMickaël Salaün 	{
591e1199815SMickaël Salaün 		TH_LOG("Failed to create a ruleset: %s", strerror(errno));
592e1199815SMickaël Salaün 	}
593e1199815SMickaël Salaün 
594e1199815SMickaël Salaün 	for (i = 0; rules[i].path; i++) {
595e1199815SMickaël Salaün 		add_path_beneath(_metadata, ruleset_fd, rules[i].access,
596e1199815SMickaël Salaün 				 rules[i].path);
597e1199815SMickaël Salaün 	}
598e1199815SMickaël Salaün 	return ruleset_fd;
599e1199815SMickaël Salaün }
600e1199815SMickaël Salaün 
601e1199815SMickaël Salaün static void enforce_ruleset(struct __test_metadata *const _metadata,
602e1199815SMickaël Salaün 			    const int ruleset_fd)
603e1199815SMickaël Salaün {
604e1199815SMickaël Salaün 	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
605371183faSMickaël Salaün 	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0))
606371183faSMickaël Salaün 	{
607e1199815SMickaël Salaün 		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
608e1199815SMickaël Salaün 	}
609e1199815SMickaël Salaün }
610e1199815SMickaël Salaün 
611e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_nsfs)
612e1199815SMickaël Salaün {
613e1199815SMickaël Salaün 	const struct rule rules[] = {
614e1199815SMickaël Salaün 		{
615e1199815SMickaël Salaün 			.path = "/dev/null",
616e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
617e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
618e1199815SMickaël Salaün 		},
619135464f9SMickaël Salaün 		{},
620e1199815SMickaël Salaün 	};
621e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath;
622371183faSMickaël Salaün 	const int ruleset_fd = create_ruleset(
623371183faSMickaël Salaün 		_metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR,
624371183faSMickaël Salaün 		rules);
625e1199815SMickaël Salaün 
626e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
627e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
628e1199815SMickaël Salaün 
629e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
630e1199815SMickaël Salaün 
631e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
632e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY));
633e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/dev/null", O_RDONLY));
634e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY));
635e1199815SMickaël Salaün 
636e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY));
637e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY));
638e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY));
639e1199815SMickaël Salaün 	/*
640e1199815SMickaël Salaün 	 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a
641e1199815SMickaël Salaün 	 * disconnected path.  Such path cannot be identified and must then be
642e1199815SMickaël Salaün 	 * allowed.
643e1199815SMickaël Salaün 	 */
644e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
645e1199815SMickaël Salaün 
646e1199815SMickaël Salaün 	/*
647e1199815SMickaël Salaün 	 * Checks that it is not possible to add nsfs-like filesystem
648e1199815SMickaël Salaün 	 * references to a ruleset.
649e1199815SMickaël Salaün 	 */
650e1199815SMickaël Salaün 	path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
651e1199815SMickaël Salaün 				      LANDLOCK_ACCESS_FS_WRITE_FILE,
652e1199815SMickaël Salaün 	path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC);
653e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
654e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
655e1199815SMickaël Salaün 					&path_beneath, 0));
656e1199815SMickaël Salaün 	ASSERT_EQ(EBADFD, errno);
657e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
658e1199815SMickaël Salaün }
659e1199815SMickaël Salaün 
660371183faSMickaël Salaün TEST_F_FORK(layout1, unpriv)
661371183faSMickaël Salaün {
662e1199815SMickaël Salaün 	const struct rule rules[] = {
663e1199815SMickaël Salaün 		{
664e1199815SMickaël Salaün 			.path = dir_s1d2,
665e1199815SMickaël Salaün 			.access = ACCESS_RO,
666e1199815SMickaël Salaün 		},
667135464f9SMickaël Salaün 		{},
668e1199815SMickaël Salaün 	};
669e1199815SMickaël Salaün 	int ruleset_fd;
670e1199815SMickaël Salaün 
671e1199815SMickaël Salaün 	drop_caps(_metadata);
672e1199815SMickaël Salaün 
673e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
674e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
675e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));
676e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
677e1199815SMickaël Salaün 
678e1199815SMickaël Salaün 	/* enforce_ruleset() calls prctl(no_new_privs). */
679e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
680e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
681e1199815SMickaël Salaün }
682e1199815SMickaël Salaün 
683e1199815SMickaël Salaün TEST_F_FORK(layout1, effective_access)
684e1199815SMickaël Salaün {
685e1199815SMickaël Salaün 	const struct rule rules[] = {
686e1199815SMickaël Salaün 		{
687e1199815SMickaël Salaün 			.path = dir_s1d2,
688e1199815SMickaël Salaün 			.access = ACCESS_RO,
689e1199815SMickaël Salaün 		},
690e1199815SMickaël Salaün 		{
691e1199815SMickaël Salaün 			.path = file1_s2d2,
692e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
693e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
694e1199815SMickaël Salaün 		},
695135464f9SMickaël Salaün 		{},
696e1199815SMickaël Salaün 	};
697e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
698e1199815SMickaël Salaün 	char buf;
699e1199815SMickaël Salaün 	int reg_fd;
700e1199815SMickaël Salaün 
701e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
702e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
703e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
704e1199815SMickaël Salaün 
705d1788ad9SMickaël Salaün 	/* Tests on a directory (with or without O_PATH). */
706e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
707d1788ad9SMickaël Salaün 	ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH));
708e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
709d1788ad9SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH));
710e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
711d1788ad9SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH));
712d1788ad9SMickaël Salaün 
713e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
714e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
715e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
716e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
717e1199815SMickaël Salaün 
718d1788ad9SMickaël Salaün 	/* Tests on a file (with or without O_PATH). */
719e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY));
720d1788ad9SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH));
721d1788ad9SMickaël Salaün 
722e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
723e1199815SMickaël Salaün 
724e1199815SMickaël Salaün 	/* Checks effective read and write actions. */
725e1199815SMickaël Salaün 	reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC);
726e1199815SMickaël Salaün 	ASSERT_LE(0, reg_fd);
727e1199815SMickaël Salaün 	ASSERT_EQ(1, write(reg_fd, ".", 1));
728e1199815SMickaël Salaün 	ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET));
729e1199815SMickaël Salaün 	ASSERT_EQ(1, read(reg_fd, &buf, 1));
730e1199815SMickaël Salaün 	ASSERT_EQ('.', buf);
731e1199815SMickaël Salaün 	ASSERT_EQ(0, close(reg_fd));
732e1199815SMickaël Salaün 
733e1199815SMickaël Salaün 	/* Just in case, double-checks effective actions. */
734e1199815SMickaël Salaün 	reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC);
735e1199815SMickaël Salaün 	ASSERT_LE(0, reg_fd);
736e1199815SMickaël Salaün 	ASSERT_EQ(-1, write(reg_fd, &buf, 1));
737e1199815SMickaël Salaün 	ASSERT_EQ(EBADF, errno);
738e1199815SMickaël Salaün 	ASSERT_EQ(0, close(reg_fd));
739e1199815SMickaël Salaün }
740e1199815SMickaël Salaün 
741e1199815SMickaël Salaün TEST_F_FORK(layout1, unhandled_access)
742e1199815SMickaël Salaün {
743e1199815SMickaël Salaün 	const struct rule rules[] = {
744e1199815SMickaël Salaün 		{
745e1199815SMickaël Salaün 			.path = dir_s1d2,
746e1199815SMickaël Salaün 			.access = ACCESS_RO,
747e1199815SMickaël Salaün 		},
748135464f9SMickaël Salaün 		{},
749e1199815SMickaël Salaün 	};
750e1199815SMickaël Salaün 	/* Here, we only handle read accesses, not write accesses. */
751e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
752e1199815SMickaël Salaün 
753e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
754e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
755e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
756e1199815SMickaël Salaün 
757e1199815SMickaël Salaün 	/*
758e1199815SMickaël Salaün 	 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE,
759e1199815SMickaël Salaün 	 * opening for write-only should be allowed, but not read-write.
760e1199815SMickaël Salaün 	 */
761e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY));
762e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
763e1199815SMickaël Salaün 
764e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
765e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
766e1199815SMickaël Salaün }
767e1199815SMickaël Salaün 
768e1199815SMickaël Salaün TEST_F_FORK(layout1, ruleset_overlap)
769e1199815SMickaël Salaün {
770e1199815SMickaël Salaün 	const struct rule rules[] = {
771e1199815SMickaël Salaün 		/* These rules should be ORed among them. */
772e1199815SMickaël Salaün 		{
773e1199815SMickaël Salaün 			.path = dir_s1d2,
774e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
775e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
776e1199815SMickaël Salaün 		},
777e1199815SMickaël Salaün 		{
778e1199815SMickaël Salaün 			.path = dir_s1d2,
779e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
780e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_READ_DIR,
781e1199815SMickaël Salaün 		},
782135464f9SMickaël Salaün 		{},
783e1199815SMickaël Salaün 	};
784e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
785e1199815SMickaël Salaün 
786e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
787e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
788e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
789e1199815SMickaël Salaün 
790e1199815SMickaël Salaün 	/* Checks s1d1 hierarchy. */
791e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
792e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
793e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
794e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
795e1199815SMickaël Salaün 
796e1199815SMickaël Salaün 	/* Checks s1d2 hierarchy. */
797e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
798e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
799e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
800e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
801e1199815SMickaël Salaün 
802e1199815SMickaël Salaün 	/* Checks s1d3 hierarchy. */
803e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
804e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
805e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
806e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
807e1199815SMickaël Salaün }
808e1199815SMickaël Salaün 
8098ba0005fSMickaël Salaün TEST_F_FORK(layout1, layer_rule_unions)
8108ba0005fSMickaël Salaün {
8118ba0005fSMickaël Salaün 	const struct rule layer1[] = {
8128ba0005fSMickaël Salaün 		{
8138ba0005fSMickaël Salaün 			.path = dir_s1d2,
8148ba0005fSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
8158ba0005fSMickaël Salaün 		},
8168ba0005fSMickaël Salaün 		/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
8178ba0005fSMickaël Salaün 		{
8188ba0005fSMickaël Salaün 			.path = dir_s1d3,
8198ba0005fSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
8208ba0005fSMickaël Salaün 		},
8218ba0005fSMickaël Salaün 		{},
8228ba0005fSMickaël Salaün 	};
8238ba0005fSMickaël Salaün 	const struct rule layer2[] = {
8248ba0005fSMickaël Salaün 		/* Doesn't change anything from layer1. */
8258ba0005fSMickaël Salaün 		{
8268ba0005fSMickaël Salaün 			.path = dir_s1d2,
8278ba0005fSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
8288ba0005fSMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
8298ba0005fSMickaël Salaün 		},
8308ba0005fSMickaël Salaün 		{},
8318ba0005fSMickaël Salaün 	};
8328ba0005fSMickaël Salaün 	const struct rule layer3[] = {
8338ba0005fSMickaël Salaün 		/* Only allows write (but not read) to dir_s1d3. */
8348ba0005fSMickaël Salaün 		{
8358ba0005fSMickaël Salaün 			.path = dir_s1d2,
8368ba0005fSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
8378ba0005fSMickaël Salaün 		},
8388ba0005fSMickaël Salaün 		{},
8398ba0005fSMickaël Salaün 	};
8408ba0005fSMickaël Salaün 	int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1);
8418ba0005fSMickaël Salaün 
8428ba0005fSMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
8438ba0005fSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
8448ba0005fSMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
8458ba0005fSMickaël Salaün 
8468ba0005fSMickaël Salaün 	/* Checks s1d1 hierarchy with layer1. */
8478ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
8488ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
8498ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
8508ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
8518ba0005fSMickaël Salaün 
8528ba0005fSMickaël Salaün 	/* Checks s1d2 hierarchy with layer1. */
8538ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
8548ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
8558ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
8568ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
8578ba0005fSMickaël Salaün 
8588ba0005fSMickaël Salaün 	/* Checks s1d3 hierarchy with layer1. */
8598ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
8608ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
8618ba0005fSMickaël Salaün 	/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
8628ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
8638ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
8648ba0005fSMickaël Salaün 
8658ba0005fSMickaël Salaün 	/* Doesn't change anything from layer1. */
8668ba0005fSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2);
8678ba0005fSMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
8688ba0005fSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
8698ba0005fSMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
8708ba0005fSMickaël Salaün 
8718ba0005fSMickaël Salaün 	/* Checks s1d1 hierarchy with layer2. */
8728ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
8738ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
8748ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
8758ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
8768ba0005fSMickaël Salaün 
8778ba0005fSMickaël Salaün 	/* Checks s1d2 hierarchy with layer2. */
8788ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
8798ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
8808ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
8818ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
8828ba0005fSMickaël Salaün 
8838ba0005fSMickaël Salaün 	/* Checks s1d3 hierarchy with layer2. */
8848ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
8858ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
8868ba0005fSMickaël Salaün 	/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
8878ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
8888ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
8898ba0005fSMickaël Salaün 
8908ba0005fSMickaël Salaün 	/* Only allows write (but not read) to dir_s1d3. */
8918ba0005fSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3);
8928ba0005fSMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
8938ba0005fSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
8948ba0005fSMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
8958ba0005fSMickaël Salaün 
8968ba0005fSMickaël Salaün 	/* Checks s1d1 hierarchy with layer3. */
8978ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
8988ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
8998ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
9008ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9018ba0005fSMickaël Salaün 
9028ba0005fSMickaël Salaün 	/* Checks s1d2 hierarchy with layer3. */
9038ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
9048ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
9058ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
9068ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9078ba0005fSMickaël Salaün 
9088ba0005fSMickaël Salaün 	/* Checks s1d3 hierarchy with layer3. */
9098ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
9108ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
9118ba0005fSMickaël Salaün 	/* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */
9128ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR));
9138ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9148ba0005fSMickaël Salaün }
9158ba0005fSMickaël Salaün 
916e1199815SMickaël Salaün TEST_F_FORK(layout1, non_overlapping_accesses)
917e1199815SMickaël Salaün {
918e1199815SMickaël Salaün 	const struct rule layer1[] = {
919e1199815SMickaël Salaün 		{
920e1199815SMickaël Salaün 			.path = dir_s1d2,
921e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
922e1199815SMickaël Salaün 		},
923135464f9SMickaël Salaün 		{},
924e1199815SMickaël Salaün 	};
925e1199815SMickaël Salaün 	const struct rule layer2[] = {
926e1199815SMickaël Salaün 		{
927e1199815SMickaël Salaün 			.path = dir_s1d3,
928e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
929e1199815SMickaël Salaün 		},
930135464f9SMickaël Salaün 		{},
931e1199815SMickaël Salaün 	};
932e1199815SMickaël Salaün 	int ruleset_fd;
933e1199815SMickaël Salaün 
934e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
935e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
936e1199815SMickaël Salaün 
937371183faSMickaël Salaün 	ruleset_fd =
938371183faSMickaël Salaün 		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1);
939e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
940e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
941e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
942e1199815SMickaël Salaün 
943e1199815SMickaël Salaün 	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
944e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
945e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
946e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
947e1199815SMickaël Salaün 
948e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE,
949e1199815SMickaël Salaün 				    layer2);
950e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
951e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
952e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
953e1199815SMickaël Salaün 
954e1199815SMickaël Salaün 	/* Unchanged accesses for file creation. */
955e1199815SMickaël Salaün 	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
956e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
957e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
958e1199815SMickaël Salaün 
959e1199815SMickaël Salaün 	/* Checks file removing. */
960e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlink(file1_s1d2));
961e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
962e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
963e1199815SMickaël Salaün }
964e1199815SMickaël Salaün 
965e1199815SMickaël Salaün TEST_F_FORK(layout1, interleaved_masked_accesses)
966e1199815SMickaël Salaün {
967e1199815SMickaël Salaün 	/*
968e1199815SMickaël Salaün 	 * Checks overly restrictive rules:
969e1199815SMickaël Salaün 	 * layer 1: allows R   s1d1/s1d2/s1d3/file1
970e1199815SMickaël Salaün 	 * layer 2: allows RW  s1d1/s1d2/s1d3
971e1199815SMickaël Salaün 	 *          allows  W  s1d1/s1d2
972e1199815SMickaël Salaün 	 *          denies R   s1d1/s1d2
973e1199815SMickaël Salaün 	 * layer 3: allows R   s1d1
974e1199815SMickaël Salaün 	 * layer 4: allows R   s1d1/s1d2
975e1199815SMickaël Salaün 	 *          denies  W  s1d1/s1d2
976e1199815SMickaël Salaün 	 * layer 5: allows R   s1d1/s1d2
977e1199815SMickaël Salaün 	 * layer 6: allows   X ----
978e1199815SMickaël Salaün 	 * layer 7: allows  W  s1d1/s1d2
979e1199815SMickaël Salaün 	 *          denies R   s1d1/s1d2
980e1199815SMickaël Salaün 	 */
981e1199815SMickaël Salaün 	const struct rule layer1_read[] = {
982e1199815SMickaël Salaün 		/* Allows read access to file1_s1d3 with the first layer. */
983e1199815SMickaël Salaün 		{
984e1199815SMickaël Salaün 			.path = file1_s1d3,
985e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
986e1199815SMickaël Salaün 		},
987135464f9SMickaël Salaün 		{},
988e1199815SMickaël Salaün 	};
989e1199815SMickaël Salaün 	/* First rule with write restrictions. */
990e1199815SMickaël Salaün 	const struct rule layer2_read_write[] = {
991e1199815SMickaël Salaün 		/* Start by granting read-write access via its parent directory... */
992e1199815SMickaël Salaün 		{
993e1199815SMickaël Salaün 			.path = dir_s1d3,
994e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
995e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
996e1199815SMickaël Salaün 		},
997e1199815SMickaël Salaün 		/* ...but also denies read access via its grandparent directory. */
998e1199815SMickaël Salaün 		{
999e1199815SMickaël Salaün 			.path = dir_s1d2,
1000e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1001e1199815SMickaël Salaün 		},
1002135464f9SMickaël Salaün 		{},
1003e1199815SMickaël Salaün 	};
1004e1199815SMickaël Salaün 	const struct rule layer3_read[] = {
1005e1199815SMickaël Salaün 		/* Allows read access via its great-grandparent directory. */
1006e1199815SMickaël Salaün 		{
1007e1199815SMickaël Salaün 			.path = dir_s1d1,
1008e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1009e1199815SMickaël Salaün 		},
1010135464f9SMickaël Salaün 		{},
1011e1199815SMickaël Salaün 	};
1012e1199815SMickaël Salaün 	const struct rule layer4_read_write[] = {
1013e1199815SMickaël Salaün 		/*
1014e1199815SMickaël Salaün 		 * Try to confuse the deny access by denying write (but not
1015e1199815SMickaël Salaün 		 * read) access via its grandparent directory.
1016e1199815SMickaël Salaün 		 */
1017e1199815SMickaël Salaün 		{
1018e1199815SMickaël Salaün 			.path = dir_s1d2,
1019e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1020e1199815SMickaël Salaün 		},
1021135464f9SMickaël Salaün 		{},
1022e1199815SMickaël Salaün 	};
1023e1199815SMickaël Salaün 	const struct rule layer5_read[] = {
1024e1199815SMickaël Salaün 		/*
1025e1199815SMickaël Salaün 		 * Try to override layer2's deny read access by explicitly
1026e1199815SMickaël Salaün 		 * allowing read access via file1_s1d3's grandparent.
1027e1199815SMickaël Salaün 		 */
1028e1199815SMickaël Salaün 		{
1029e1199815SMickaël Salaün 			.path = dir_s1d2,
1030e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1031e1199815SMickaël Salaün 		},
1032135464f9SMickaël Salaün 		{},
1033e1199815SMickaël Salaün 	};
1034e1199815SMickaël Salaün 	const struct rule layer6_execute[] = {
1035e1199815SMickaël Salaün 		/*
1036e1199815SMickaël Salaün 		 * Restricts an unrelated file hierarchy with a new access
1037e1199815SMickaël Salaün 		 * (non-overlapping) type.
1038e1199815SMickaël Salaün 		 */
1039e1199815SMickaël Salaün 		{
1040e1199815SMickaël Salaün 			.path = dir_s2d1,
1041e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
1042e1199815SMickaël Salaün 		},
1043135464f9SMickaël Salaün 		{},
1044e1199815SMickaël Salaün 	};
1045e1199815SMickaël Salaün 	const struct rule layer7_read_write[] = {
1046e1199815SMickaël Salaün 		/*
1047e1199815SMickaël Salaün 		 * Finally, denies read access to file1_s1d3 via its
1048e1199815SMickaël Salaün 		 * grandparent.
1049e1199815SMickaël Salaün 		 */
1050e1199815SMickaël Salaün 		{
1051e1199815SMickaël Salaün 			.path = dir_s1d2,
1052e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1053e1199815SMickaël Salaün 		},
1054135464f9SMickaël Salaün 		{},
1055e1199815SMickaël Salaün 	};
1056e1199815SMickaël Salaün 	int ruleset_fd;
1057e1199815SMickaël Salaün 
1058e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
1059e1199815SMickaël Salaün 				    layer1_read);
1060e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1061e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1062e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1063e1199815SMickaël Salaün 
1064e1199815SMickaël Salaün 	/* Checks that read access is granted for file1_s1d3 with layer 1. */
1065e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1066e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1067e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1068e1199815SMickaël Salaün 
1069371183faSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata,
1070371183faSMickaël Salaün 				    LANDLOCK_ACCESS_FS_READ_FILE |
1071371183faSMickaël Salaün 					    LANDLOCK_ACCESS_FS_WRITE_FILE,
1072371183faSMickaël Salaün 				    layer2_read_write);
1073e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1074e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1075e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1076e1199815SMickaël Salaün 
1077e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 2. */
1078e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1079e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1080e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1081e1199815SMickaël Salaün 
1082e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
1083e1199815SMickaël Salaün 				    layer3_read);
1084e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1085e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1086e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1087e1199815SMickaël Salaün 
1088e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 3. */
1089e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1090e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1091e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1092e1199815SMickaël Salaün 
1093e1199815SMickaël Salaün 	/* This time, denies write access for the file hierarchy. */
1094371183faSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata,
1095371183faSMickaël Salaün 				    LANDLOCK_ACCESS_FS_READ_FILE |
1096371183faSMickaël Salaün 					    LANDLOCK_ACCESS_FS_WRITE_FILE,
1097371183faSMickaël Salaün 				    layer4_read_write);
1098e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1099e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1100e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1101e1199815SMickaël Salaün 
1102e1199815SMickaël Salaün 	/*
1103e1199815SMickaël Salaün 	 * Checks that the only change with layer 4 is that write access is
1104e1199815SMickaël Salaün 	 * denied.
1105e1199815SMickaël Salaün 	 */
1106e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1107e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1108e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1109e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1110e1199815SMickaël Salaün 
1111e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
1112e1199815SMickaël Salaün 				    layer5_read);
1113e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1114e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1115e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1116e1199815SMickaël Salaün 
1117e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 5. */
1118e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1119e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1120e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1121e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1122e1199815SMickaël Salaün 
1123e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE,
1124e1199815SMickaël Salaün 				    layer6_execute);
1125e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1126e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1127e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1128e1199815SMickaël Salaün 
1129e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 6. */
1130e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1131e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1132e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1133e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1134e1199815SMickaël Salaün 
1135371183faSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata,
1136371183faSMickaël Salaün 				    LANDLOCK_ACCESS_FS_READ_FILE |
1137371183faSMickaël Salaün 					    LANDLOCK_ACCESS_FS_WRITE_FILE,
1138371183faSMickaël Salaün 				    layer7_read_write);
1139e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1140e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1141e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1142e1199815SMickaël Salaün 
1143e1199815SMickaël Salaün 	/* Checks read access is now denied with layer 7. */
1144e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
1145e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1146e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1147e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1148e1199815SMickaël Salaün }
1149e1199815SMickaël Salaün 
1150e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_subset)
1151e1199815SMickaël Salaün {
1152e1199815SMickaël Salaün 	const struct rule rules[] = {
1153e1199815SMickaël Salaün 		{
1154e1199815SMickaël Salaün 			.path = dir_s1d2,
1155e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
1156e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_READ_DIR,
1157e1199815SMickaël Salaün 		},
1158135464f9SMickaël Salaün 		{},
1159e1199815SMickaël Salaün 	};
1160e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1161e1199815SMickaël Salaün 
1162e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1163e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1164e1199815SMickaël Salaün 
1165e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1166e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1167e1199815SMickaël Salaün 
1168e1199815SMickaël Salaün 	/* Write access is forbidden. */
1169e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1170e1199815SMickaël Salaün 	/* Readdir access is allowed. */
1171e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1172e1199815SMickaël Salaün 
1173e1199815SMickaël Salaün 	/* Write access is forbidden. */
1174e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1175e1199815SMickaël Salaün 	/* Readdir access is allowed. */
1176e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1177e1199815SMickaël Salaün 
1178e1199815SMickaël Salaün 	/*
1179e1199815SMickaël Salaün 	 * Tests shared rule extension: the following rules should not grant
1180e1199815SMickaël Salaün 	 * any new access, only remove some.  Once enforced, these rules are
1181e1199815SMickaël Salaün 	 * ANDed with the previous ones.
1182e1199815SMickaël Salaün 	 */
1183e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
1184e1199815SMickaël Salaün 			 dir_s1d2);
1185e1199815SMickaël Salaün 	/*
1186e1199815SMickaël Salaün 	 * According to ruleset_fd, dir_s1d2 should now have the
1187e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE
1188e1199815SMickaël Salaün 	 * access rights (even if this directory is opened a second time).
1189e1199815SMickaël Salaün 	 * However, when enforcing this updated ruleset, the ruleset tied to
1190e1199815SMickaël Salaün 	 * the current process (i.e. its domain) will still only have the
1191e1199815SMickaël Salaün 	 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and
1192e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but
1193e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would
1194e1199815SMickaël Salaün 	 * be a privilege escalation.
1195e1199815SMickaël Salaün 	 */
1196e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1197e1199815SMickaël Salaün 
1198e1199815SMickaël Salaün 	/* Same tests and results as above. */
1199e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1200e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1201e1199815SMickaël Salaün 
1202e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d2. */
1203e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1204e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1205e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1206e1199815SMickaël Salaün 
1207e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d3. */
1208e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1209e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1210e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1211e1199815SMickaël Salaün 
1212e1199815SMickaël Salaün 	/*
1213e1199815SMickaël Salaün 	 * Try to get more privileges by adding new access rights to the parent
1214e1199815SMickaël Salaün 	 * directory: dir_s1d1.
1215e1199815SMickaël Salaün 	 */
1216e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1);
1217e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1218e1199815SMickaël Salaün 
1219e1199815SMickaël Salaün 	/* Same tests and results as above. */
1220e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1221e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1222e1199815SMickaël Salaün 
1223e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d2. */
1224e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1225e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1226e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1227e1199815SMickaël Salaün 
1228e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d3. */
1229e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1230e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1231e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1232e1199815SMickaël Salaün 
1233e1199815SMickaël Salaün 	/*
1234e1199815SMickaël Salaün 	 * Now, dir_s1d3 get a new rule tied to it, only allowing
1235e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_WRITE_FILE.  The (kernel internal) difference is
1236e1199815SMickaël Salaün 	 * that there was no rule tied to it before.
1237e1199815SMickaël Salaün 	 */
1238e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
1239e1199815SMickaël Salaün 			 dir_s1d3);
1240e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1241e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1242e1199815SMickaël Salaün 
1243e1199815SMickaël Salaün 	/*
1244e1199815SMickaël Salaün 	 * Same tests and results as above, except for open(dir_s1d3) which is
1245e1199815SMickaël Salaün 	 * now denied because the new rule mask the rule previously inherited
1246e1199815SMickaël Salaün 	 * from dir_s1d2.
1247e1199815SMickaël Salaün 	 */
1248e1199815SMickaël Salaün 
1249e1199815SMickaël Salaün 	/* Same tests and results as above. */
1250e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1251e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1252e1199815SMickaël Salaün 
1253e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d2. */
1254e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1255e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1256e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1257e1199815SMickaël Salaün 
1258e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d3. */
1259e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1260e1199815SMickaël Salaün 	/*
1261e1199815SMickaël Salaün 	 * Readdir of dir_s1d3 is still allowed because of the OR policy inside
1262e1199815SMickaël Salaün 	 * the same layer.
1263e1199815SMickaël Salaün 	 */
1264e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1265e1199815SMickaël Salaün }
1266e1199815SMickaël Salaün 
1267e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_superset)
1268e1199815SMickaël Salaün {
1269e1199815SMickaël Salaün 	const struct rule rules[] = {
1270e1199815SMickaël Salaün 		{
1271e1199815SMickaël Salaün 			.path = dir_s1d3,
1272e1199815SMickaël Salaün 			.access = ACCESS_RO,
1273e1199815SMickaël Salaün 		},
1274135464f9SMickaël Salaün 		{},
1275e1199815SMickaël Salaün 	};
1276e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1277e1199815SMickaël Salaün 
1278e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1279e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1280e1199815SMickaël Salaün 
1281e1199815SMickaël Salaün 	/* Readdir access is denied for dir_s1d2. */
1282e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1283e1199815SMickaël Salaün 	/* Readdir access is allowed for dir_s1d3. */
1284e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1285e1199815SMickaël Salaün 	/* File access is allowed for file1_s1d3. */
1286e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1287e1199815SMickaël Salaün 
1288e1199815SMickaël Salaün 	/* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */
1289371183faSMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd,
1290371183faSMickaël Salaün 			 LANDLOCK_ACCESS_FS_READ_FILE |
1291371183faSMickaël Salaün 				 LANDLOCK_ACCESS_FS_READ_DIR,
1292371183faSMickaël Salaün 			 dir_s1d2);
1293e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1294e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1295e1199815SMickaël Salaün 
1296e1199815SMickaël Salaün 	/* Readdir access is still denied for dir_s1d2. */
1297e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1298e1199815SMickaël Salaün 	/* Readdir access is still allowed for dir_s1d3. */
1299e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1300e1199815SMickaël Salaün 	/* File access is still allowed for file1_s1d3. */
1301e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1302e1199815SMickaël Salaün }
1303e1199815SMickaël Salaün 
1304e1199815SMickaël Salaün TEST_F_FORK(layout1, max_layers)
1305e1199815SMickaël Salaün {
1306e1199815SMickaël Salaün 	int i, err;
1307e1199815SMickaël Salaün 	const struct rule rules[] = {
1308e1199815SMickaël Salaün 		{
1309e1199815SMickaël Salaün 			.path = dir_s1d2,
1310e1199815SMickaël Salaün 			.access = ACCESS_RO,
1311e1199815SMickaël Salaün 		},
1312135464f9SMickaël Salaün 		{},
1313e1199815SMickaël Salaün 	};
1314e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1315e1199815SMickaël Salaün 
1316e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
131775c542d6SMickaël Salaün 	for (i = 0; i < 16; i++)
1318e1199815SMickaël Salaün 		enforce_ruleset(_metadata, ruleset_fd);
1319e1199815SMickaël Salaün 
1320e1199815SMickaël Salaün 	for (i = 0; i < 2; i++) {
1321e1199815SMickaël Salaün 		err = landlock_restrict_self(ruleset_fd, 0);
1322e1199815SMickaël Salaün 		ASSERT_EQ(-1, err);
1323e1199815SMickaël Salaün 		ASSERT_EQ(E2BIG, errno);
1324e1199815SMickaël Salaün 	}
1325e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1326e1199815SMickaël Salaün }
1327e1199815SMickaël Salaün 
1328e1199815SMickaël Salaün TEST_F_FORK(layout1, empty_or_same_ruleset)
1329e1199815SMickaël Salaün {
1330e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {};
1331e1199815SMickaël Salaün 	int ruleset_fd;
1332e1199815SMickaël Salaün 
1333e1199815SMickaël Salaün 	/* Tests empty handled_access_fs. */
1334371183faSMickaël Salaün 	ruleset_fd =
1335371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1336e1199815SMickaël Salaün 	ASSERT_LE(-1, ruleset_fd);
1337e1199815SMickaël Salaün 	ASSERT_EQ(ENOMSG, errno);
1338e1199815SMickaël Salaün 
1339e1199815SMickaël Salaün 	/* Enforces policy which deny read access to all files. */
1340e1199815SMickaël Salaün 	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE;
1341371183faSMickaël Salaün 	ruleset_fd =
1342371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1343e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1344e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1345e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1346e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1347e1199815SMickaël Salaün 
1348e1199815SMickaël Salaün 	/* Nests a policy which deny read access to all directories. */
1349e1199815SMickaël Salaün 	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR;
1350371183faSMickaël Salaün 	ruleset_fd =
1351371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1352e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1353e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1354e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1355e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1356e1199815SMickaël Salaün 
1357e1199815SMickaël Salaün 	/* Enforces a second time with the same ruleset. */
1358e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1359e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1360e1199815SMickaël Salaün }
1361e1199815SMickaël Salaün 
1362e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_on_mountpoint)
1363e1199815SMickaël Salaün {
1364e1199815SMickaël Salaün 	const struct rule rules[] = {
1365e1199815SMickaël Salaün 		{
1366e1199815SMickaël Salaün 			.path = dir_s1d1,
1367e1199815SMickaël Salaün 			.access = ACCESS_RO,
1368e1199815SMickaël Salaün 		},
1369e1199815SMickaël Salaün 		{
1370e1199815SMickaël Salaün 			/* dir_s3d2 is a mount point. */
1371e1199815SMickaël Salaün 			.path = dir_s3d2,
1372e1199815SMickaël Salaün 			.access = ACCESS_RO,
1373e1199815SMickaël Salaün 		},
1374135464f9SMickaël Salaün 		{},
1375e1199815SMickaël Salaün 	};
1376e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1377e1199815SMickaël Salaün 
1378e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1379e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1380e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1381e1199815SMickaël Salaün 
1382e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1383e1199815SMickaël Salaün 
1384e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1385e1199815SMickaël Salaün 
1386e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY));
1387e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1388e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1389e1199815SMickaël Salaün }
1390e1199815SMickaël Salaün 
1391e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_mountpoint)
1392e1199815SMickaël Salaün {
1393e1199815SMickaël Salaün 	const struct rule rules[] = {
1394e1199815SMickaël Salaün 		{
1395e1199815SMickaël Salaün 			.path = dir_s1d1,
1396e1199815SMickaël Salaün 			.access = ACCESS_RO,
1397e1199815SMickaël Salaün 		},
1398e1199815SMickaël Salaün 		{
1399e1199815SMickaël Salaün 			/* dir_s3d2 is a mount point. */
1400e1199815SMickaël Salaün 			.path = dir_s3d1,
1401e1199815SMickaël Salaün 			.access = ACCESS_RO,
1402e1199815SMickaël Salaün 		},
1403135464f9SMickaël Salaün 		{},
1404e1199815SMickaël Salaün 	};
1405e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1406e1199815SMickaël Salaün 
1407e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1408e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1409e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1410e1199815SMickaël Salaün 
1411e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1412e1199815SMickaël Salaün 
1413e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1414e1199815SMickaël Salaün 
1415e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
1416e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1417e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1418e1199815SMickaël Salaün }
1419e1199815SMickaël Salaün 
1420e1199815SMickaël Salaün /*
1421e1199815SMickaël Salaün  * This test verifies that we can apply a landlock rule on the root directory
1422e1199815SMickaël Salaün  * (which might require special handling).
1423e1199815SMickaël Salaün  */
1424e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_allow_then_deny)
1425e1199815SMickaël Salaün {
1426e1199815SMickaël Salaün 	struct rule rules[] = {
1427e1199815SMickaël Salaün 		{
1428e1199815SMickaël Salaün 			.path = "/",
1429e1199815SMickaël Salaün 			.access = ACCESS_RO,
1430e1199815SMickaël Salaün 		},
1431135464f9SMickaël Salaün 		{},
1432e1199815SMickaël Salaün 	};
1433e1199815SMickaël Salaün 	int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1434e1199815SMickaël Salaün 
1435e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1436e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1437e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1438e1199815SMickaël Salaün 
1439e1199815SMickaël Salaün 	/* Checks allowed access. */
1440e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/", O_RDONLY));
1441e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1442e1199815SMickaël Salaün 
1443e1199815SMickaël Salaün 	rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE;
1444e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1445e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1446e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1447e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1448e1199815SMickaël Salaün 
1449e1199815SMickaël Salaün 	/* Checks denied access (on a directory). */
1450e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1451e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1452e1199815SMickaël Salaün }
1453e1199815SMickaël Salaün 
1454e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_deny)
1455e1199815SMickaël Salaün {
1456e1199815SMickaël Salaün 	const struct rule rules[] = {
1457e1199815SMickaël Salaün 		{
1458e1199815SMickaël Salaün 			.path = "/",
1459e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1460e1199815SMickaël Salaün 		},
1461135464f9SMickaël Salaün 		{},
1462e1199815SMickaël Salaün 	};
1463e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1464e1199815SMickaël Salaün 
1465e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1466e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1467e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1468e1199815SMickaël Salaün 
1469e1199815SMickaël Salaün 	/* Checks denied access (on a directory). */
1470e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1471e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1472e1199815SMickaël Salaün }
1473e1199815SMickaël Salaün 
1474e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_inside_mount_ns)
1475e1199815SMickaël Salaün {
1476e1199815SMickaël Salaün 	const struct rule rules[] = {
1477e1199815SMickaël Salaün 		{
1478e1199815SMickaël Salaün 			.path = "s3d3",
1479e1199815SMickaël Salaün 			.access = ACCESS_RO,
1480e1199815SMickaël Salaün 		},
1481135464f9SMickaël Salaün 		{},
1482e1199815SMickaël Salaün 	};
1483e1199815SMickaël Salaün 	int ruleset_fd;
1484e1199815SMickaël Salaün 
1485e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
148687129ef1SMickaël Salaün 	ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3))
1487371183faSMickaël Salaün 	{
1488e1199815SMickaël Salaün 		TH_LOG("Failed to pivot root: %s", strerror(errno));
1489e1199815SMickaël Salaün 	};
1490e1199815SMickaël Salaün 	ASSERT_EQ(0, chdir("/"));
1491e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1492e1199815SMickaël Salaün 
1493e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1494e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1495e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1496e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1497e1199815SMickaël Salaün 
1498e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("s3d3", O_RDONLY));
1499e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1500e1199815SMickaël Salaün }
1501e1199815SMickaël Salaün 
1502e1199815SMickaël Salaün TEST_F_FORK(layout1, mount_and_pivot)
1503e1199815SMickaël Salaün {
1504e1199815SMickaël Salaün 	const struct rule rules[] = {
1505e1199815SMickaël Salaün 		{
1506e1199815SMickaël Salaün 			.path = dir_s3d2,
1507e1199815SMickaël Salaün 			.access = ACCESS_RO,
1508e1199815SMickaël Salaün 		},
1509135464f9SMickaël Salaün 		{},
1510e1199815SMickaël Salaün 	};
1511e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1512e1199815SMickaël Salaün 
1513e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1514e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1515e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1516e1199815SMickaël Salaün 
1517e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
1518e1199815SMickaël Salaün 	ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
1519e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
152087129ef1SMickaël Salaün 	ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));
1521e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
1522e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1523e1199815SMickaël Salaün }
1524e1199815SMickaël Salaün 
1525e1199815SMickaël Salaün TEST_F_FORK(layout1, move_mount)
1526e1199815SMickaël Salaün {
1527e1199815SMickaël Salaün 	const struct rule rules[] = {
1528e1199815SMickaël Salaün 		{
1529e1199815SMickaël Salaün 			.path = dir_s3d2,
1530e1199815SMickaël Salaün 			.access = ACCESS_RO,
1531e1199815SMickaël Salaün 		},
1532135464f9SMickaël Salaün 		{},
1533e1199815SMickaël Salaün 	};
1534e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1535e1199815SMickaël Salaün 
1536e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1537e1199815SMickaël Salaün 
1538e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
153987129ef1SMickaël Salaün 	ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1540371183faSMickaël Salaün 			     dir_s1d2, 0))
1541371183faSMickaël Salaün 	{
1542e1199815SMickaël Salaün 		TH_LOG("Failed to move mount: %s", strerror(errno));
1543e1199815SMickaël Salaün 	}
1544e1199815SMickaël Salaün 
154587129ef1SMickaël Salaün 	ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,
1546e1199815SMickaël Salaün 			     dir_s3d2, 0));
1547e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1548e1199815SMickaël Salaün 
1549e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1550e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1551e1199815SMickaël Salaün 
1552e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
155387129ef1SMickaël Salaün 	ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1554e1199815SMickaël Salaün 			      dir_s1d2, 0));
1555e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
1556e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1557e1199815SMickaël Salaün }
1558e1199815SMickaël Salaün 
1559e1199815SMickaël Salaün TEST_F_FORK(layout1, release_inodes)
1560e1199815SMickaël Salaün {
1561e1199815SMickaël Salaün 	const struct rule rules[] = {
1562e1199815SMickaël Salaün 		{
1563e1199815SMickaël Salaün 			.path = dir_s1d1,
1564e1199815SMickaël Salaün 			.access = ACCESS_RO,
1565e1199815SMickaël Salaün 		},
1566e1199815SMickaël Salaün 		{
1567e1199815SMickaël Salaün 			.path = dir_s3d2,
1568e1199815SMickaël Salaün 			.access = ACCESS_RO,
1569e1199815SMickaël Salaün 		},
1570e1199815SMickaël Salaün 		{
1571e1199815SMickaël Salaün 			.path = dir_s3d3,
1572e1199815SMickaël Salaün 			.access = ACCESS_RO,
1573e1199815SMickaël Salaün 		},
1574135464f9SMickaël Salaün 		{},
1575e1199815SMickaël Salaün 	};
1576e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1577e1199815SMickaël Salaün 
1578e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1579e1199815SMickaël Salaün 	/* Unmount a file hierarchy while it is being used by a ruleset. */
1580e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
1581e1199815SMickaël Salaün 	ASSERT_EQ(0, umount(dir_s3d2));
1582e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1583e1199815SMickaël Salaün 
1584e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1585e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1586e1199815SMickaël Salaün 
1587e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
1588e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));
1589e1199815SMickaël Salaün 	/* This dir_s3d3 would not be allowed and does not exist anyway. */
1590e1199815SMickaël Salaün 	ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY));
1591e1199815SMickaël Salaün }
1592e1199815SMickaël Salaün 
1593e1199815SMickaël Salaün enum relative_access {
1594e1199815SMickaël Salaün 	REL_OPEN,
1595e1199815SMickaël Salaün 	REL_CHDIR,
1596e1199815SMickaël Salaün 	REL_CHROOT_ONLY,
1597e1199815SMickaël Salaün 	REL_CHROOT_CHDIR,
1598e1199815SMickaël Salaün };
1599e1199815SMickaël Salaün 
1600e1199815SMickaël Salaün static void test_relative_path(struct __test_metadata *const _metadata,
1601e1199815SMickaël Salaün 			       const enum relative_access rel)
1602e1199815SMickaël Salaün {
1603e1199815SMickaël Salaün 	/*
1604e1199815SMickaël Salaün 	 * Common layer to check that chroot doesn't ignore it (i.e. a chroot
1605e1199815SMickaël Salaün 	 * is not a disconnected root directory).
1606e1199815SMickaël Salaün 	 */
1607e1199815SMickaël Salaün 	const struct rule layer1_base[] = {
1608e1199815SMickaël Salaün 		{
1609e1199815SMickaël Salaün 			.path = TMP_DIR,
1610e1199815SMickaël Salaün 			.access = ACCESS_RO,
1611e1199815SMickaël Salaün 		},
1612135464f9SMickaël Salaün 		{},
1613e1199815SMickaël Salaün 	};
1614e1199815SMickaël Salaün 	const struct rule layer2_subs[] = {
1615e1199815SMickaël Salaün 		{
1616e1199815SMickaël Salaün 			.path = dir_s1d2,
1617e1199815SMickaël Salaün 			.access = ACCESS_RO,
1618e1199815SMickaël Salaün 		},
1619e1199815SMickaël Salaün 		{
1620e1199815SMickaël Salaün 			.path = dir_s2d2,
1621e1199815SMickaël Salaün 			.access = ACCESS_RO,
1622e1199815SMickaël Salaün 		},
1623135464f9SMickaël Salaün 		{},
1624e1199815SMickaël Salaün 	};
1625e1199815SMickaël Salaün 	int dirfd, ruleset_fd;
1626e1199815SMickaël Salaün 
1627e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
1628e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1629e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1630e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1631e1199815SMickaël Salaün 
1632e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs);
1633e1199815SMickaël Salaün 
1634e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1635e1199815SMickaël Salaün 	switch (rel) {
1636e1199815SMickaël Salaün 	case REL_OPEN:
1637e1199815SMickaël Salaün 	case REL_CHDIR:
1638e1199815SMickaël Salaün 		break;
1639e1199815SMickaël Salaün 	case REL_CHROOT_ONLY:
1640e1199815SMickaël Salaün 		ASSERT_EQ(0, chdir(dir_s2d2));
1641e1199815SMickaël Salaün 		break;
1642e1199815SMickaël Salaün 	case REL_CHROOT_CHDIR:
1643e1199815SMickaël Salaün 		ASSERT_EQ(0, chdir(dir_s1d2));
1644e1199815SMickaël Salaün 		break;
1645e1199815SMickaël Salaün 	default:
1646e1199815SMickaël Salaün 		ASSERT_TRUE(false);
1647e1199815SMickaël Salaün 		return;
1648e1199815SMickaël Salaün 	}
1649e1199815SMickaël Salaün 
1650e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_CHROOT);
1651e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1652e1199815SMickaël Salaün 
1653e1199815SMickaël Salaün 	switch (rel) {
1654e1199815SMickaël Salaün 	case REL_OPEN:
1655e1199815SMickaël Salaün 		dirfd = open(dir_s1d2, O_DIRECTORY);
1656e1199815SMickaël Salaün 		ASSERT_LE(0, dirfd);
1657e1199815SMickaël Salaün 		break;
1658e1199815SMickaël Salaün 	case REL_CHDIR:
1659e1199815SMickaël Salaün 		ASSERT_EQ(0, chdir(dir_s1d2));
1660e1199815SMickaël Salaün 		dirfd = AT_FDCWD;
1661e1199815SMickaël Salaün 		break;
1662e1199815SMickaël Salaün 	case REL_CHROOT_ONLY:
1663e1199815SMickaël Salaün 		/* Do chroot into dir_s1d2 (relative to dir_s2d2). */
1664371183faSMickaël Salaün 		ASSERT_EQ(0, chroot("../../s1d1/s1d2"))
1665371183faSMickaël Salaün 		{
1666e1199815SMickaël Salaün 			TH_LOG("Failed to chroot: %s", strerror(errno));
1667e1199815SMickaël Salaün 		}
1668e1199815SMickaël Salaün 		dirfd = AT_FDCWD;
1669e1199815SMickaël Salaün 		break;
1670e1199815SMickaël Salaün 	case REL_CHROOT_CHDIR:
1671e1199815SMickaël Salaün 		/* Do chroot into dir_s1d2. */
1672371183faSMickaël Salaün 		ASSERT_EQ(0, chroot("."))
1673371183faSMickaël Salaün 		{
1674e1199815SMickaël Salaün 			TH_LOG("Failed to chroot: %s", strerror(errno));
1675e1199815SMickaël Salaün 		}
1676e1199815SMickaël Salaün 		dirfd = AT_FDCWD;
1677e1199815SMickaël Salaün 		break;
1678e1199815SMickaël Salaün 	}
1679e1199815SMickaël Salaün 
1680e1199815SMickaël Salaün 	ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES,
1681e1199815SMickaël Salaün 		  test_open_rel(dirfd, "..", O_RDONLY));
1682e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY));
1683e1199815SMickaël Salaün 
1684e1199815SMickaël Salaün 	if (rel == REL_CHROOT_ONLY) {
1685e1199815SMickaël Salaün 		/* The current directory is dir_s2d2. */
1686e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY));
1687e1199815SMickaël Salaün 	} else {
1688e1199815SMickaël Salaün 		/* The current directory is dir_s1d2. */
1689e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY));
1690e1199815SMickaël Salaün 	}
1691e1199815SMickaël Salaün 
1692e1199815SMickaël Salaün 	if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) {
1693e1199815SMickaël Salaün 		/* Checks the root dir_s1d2. */
1694e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY));
1695e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY));
1696e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY));
1697e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY));
1698e1199815SMickaël Salaün 	}
1699e1199815SMickaël Salaün 
1700e1199815SMickaël Salaün 	if (rel != REL_CHROOT_CHDIR) {
1701e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY));
1702e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY));
1703371183faSMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3",
1704371183faSMickaël Salaün 					   O_RDONLY));
1705e1199815SMickaël Salaün 
1706e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY));
1707e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY));
1708371183faSMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3",
1709371183faSMickaël Salaün 					   O_RDONLY));
1710e1199815SMickaël Salaün 	}
1711e1199815SMickaël Salaün 
1712e1199815SMickaël Salaün 	if (rel == REL_OPEN)
1713e1199815SMickaël Salaün 		ASSERT_EQ(0, close(dirfd));
1714e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1715e1199815SMickaël Salaün }
1716e1199815SMickaël Salaün 
1717e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_open)
1718e1199815SMickaël Salaün {
1719e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_OPEN);
1720e1199815SMickaël Salaün }
1721e1199815SMickaël Salaün 
1722e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chdir)
1723e1199815SMickaël Salaün {
1724e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_CHDIR);
1725e1199815SMickaël Salaün }
1726e1199815SMickaël Salaün 
1727e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_only)
1728e1199815SMickaël Salaün {
1729e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_CHROOT_ONLY);
1730e1199815SMickaël Salaün }
1731e1199815SMickaël Salaün 
1732e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_chdir)
1733e1199815SMickaël Salaün {
1734e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_CHROOT_CHDIR);
1735e1199815SMickaël Salaün }
1736e1199815SMickaël Salaün 
1737e1199815SMickaël Salaün static void copy_binary(struct __test_metadata *const _metadata,
1738e1199815SMickaël Salaün 			const char *const dst_path)
1739e1199815SMickaël Salaün {
1740e1199815SMickaël Salaün 	int dst_fd, src_fd;
1741e1199815SMickaël Salaün 	struct stat statbuf;
1742e1199815SMickaël Salaün 
1743e1199815SMickaël Salaün 	dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC);
1744371183faSMickaël Salaün 	ASSERT_LE(0, dst_fd)
1745371183faSMickaël Salaün 	{
1746371183faSMickaël Salaün 		TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno));
1747e1199815SMickaël Salaün 	}
1748e1199815SMickaël Salaün 	src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC);
1749371183faSMickaël Salaün 	ASSERT_LE(0, src_fd)
1750371183faSMickaël Salaün 	{
1751e1199815SMickaël Salaün 		TH_LOG("Failed to open \"" BINARY_PATH "\": %s",
1752e1199815SMickaël Salaün 		       strerror(errno));
1753e1199815SMickaël Salaün 	}
1754e1199815SMickaël Salaün 	ASSERT_EQ(0, fstat(src_fd, &statbuf));
1755371183faSMickaël Salaün 	ASSERT_EQ(statbuf.st_size,
1756371183faSMickaël Salaün 		  sendfile(dst_fd, src_fd, 0, statbuf.st_size));
1757e1199815SMickaël Salaün 	ASSERT_EQ(0, close(src_fd));
1758e1199815SMickaël Salaün 	ASSERT_EQ(0, close(dst_fd));
1759e1199815SMickaël Salaün }
1760e1199815SMickaël Salaün 
1761371183faSMickaël Salaün static void test_execute(struct __test_metadata *const _metadata, const int err,
1762371183faSMickaël Salaün 			 const char *const path)
1763e1199815SMickaël Salaün {
1764e1199815SMickaël Salaün 	int status;
1765e1199815SMickaël Salaün 	char *const argv[] = { (char *)path, NULL };
1766e1199815SMickaël Salaün 	const pid_t child = fork();
1767e1199815SMickaël Salaün 
1768e1199815SMickaël Salaün 	ASSERT_LE(0, child);
1769e1199815SMickaël Salaün 	if (child == 0) {
1770371183faSMickaël Salaün 		ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL))
1771371183faSMickaël Salaün 		{
1772e1199815SMickaël Salaün 			TH_LOG("Failed to execute \"%s\": %s", path,
1773e1199815SMickaël Salaün 			       strerror(errno));
1774e1199815SMickaël Salaün 		};
1775e1199815SMickaël Salaün 		ASSERT_EQ(err, errno);
1776e1199815SMickaël Salaün 		_exit(_metadata->passed ? 2 : 1);
1777e1199815SMickaël Salaün 		return;
1778e1199815SMickaël Salaün 	}
1779e1199815SMickaël Salaün 	ASSERT_EQ(child, waitpid(child, &status, 0));
1780e1199815SMickaël Salaün 	ASSERT_EQ(1, WIFEXITED(status));
1781371183faSMickaël Salaün 	ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status))
1782371183faSMickaël Salaün 	{
1783e1199815SMickaël Salaün 		TH_LOG("Unexpected return code for \"%s\": %s", path,
1784e1199815SMickaël Salaün 		       strerror(errno));
1785e1199815SMickaël Salaün 	};
1786e1199815SMickaël Salaün }
1787e1199815SMickaël Salaün 
1788e1199815SMickaël Salaün TEST_F_FORK(layout1, execute)
1789e1199815SMickaël Salaün {
1790e1199815SMickaël Salaün 	const struct rule rules[] = {
1791e1199815SMickaël Salaün 		{
1792e1199815SMickaël Salaün 			.path = dir_s1d2,
1793e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
1794e1199815SMickaël Salaün 		},
1795135464f9SMickaël Salaün 		{},
1796e1199815SMickaël Salaün 	};
1797371183faSMickaël Salaün 	const int ruleset_fd =
1798371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
1799e1199815SMickaël Salaün 
1800e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1801e1199815SMickaël Salaün 	copy_binary(_metadata, file1_s1d1);
1802e1199815SMickaël Salaün 	copy_binary(_metadata, file1_s1d2);
1803e1199815SMickaël Salaün 	copy_binary(_metadata, file1_s1d3);
1804e1199815SMickaël Salaün 
1805e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1806e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1807e1199815SMickaël Salaün 
1808e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1809e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
1810e1199815SMickaël Salaün 	test_execute(_metadata, EACCES, file1_s1d1);
1811e1199815SMickaël Salaün 
1812e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
1813e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
1814e1199815SMickaël Salaün 	test_execute(_metadata, 0, file1_s1d2);
1815e1199815SMickaël Salaün 
1816e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
1817e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1818e1199815SMickaël Salaün 	test_execute(_metadata, 0, file1_s1d3);
1819e1199815SMickaël Salaün }
1820e1199815SMickaël Salaün 
1821e1199815SMickaël Salaün TEST_F_FORK(layout1, link)
1822e1199815SMickaël Salaün {
18236a1bdd4aSMickaël Salaün 	const struct rule layer1[] = {
1824e1199815SMickaël Salaün 		{
1825e1199815SMickaël Salaün 			.path = dir_s1d2,
1826e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
1827e1199815SMickaël Salaün 		},
1828135464f9SMickaël Salaün 		{},
1829e1199815SMickaël Salaün 	};
18306a1bdd4aSMickaël Salaün 	const struct rule layer2[] = {
18316a1bdd4aSMickaël Salaün 		{
18326a1bdd4aSMickaël Salaün 			.path = dir_s1d3,
18336a1bdd4aSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
18346a1bdd4aSMickaël Salaün 		},
18356a1bdd4aSMickaël Salaün 		{},
18366a1bdd4aSMickaël Salaün 	};
18376a1bdd4aSMickaël Salaün 	int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
1838e1199815SMickaël Salaün 
1839e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1840e1199815SMickaël Salaün 
1841e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
1842e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1843e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1844e1199815SMickaël Salaün 
1845e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1846e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1847e1199815SMickaël Salaün 
1848e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
1849e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
18506a1bdd4aSMickaël Salaün 
1851e1199815SMickaël Salaün 	/* Denies linking because of reparenting. */
1852e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
1853e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1854e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
1855e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
18566a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
18576a1bdd4aSMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1858e1199815SMickaël Salaün 
1859e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
1860e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
18616a1bdd4aSMickaël Salaün 
18626a1bdd4aSMickaël Salaün 	/* Prepares for next unlinks. */
18636a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
18646a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
18656a1bdd4aSMickaël Salaün 
18666a1bdd4aSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
18676a1bdd4aSMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
18686a1bdd4aSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
18696a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
18706a1bdd4aSMickaël Salaün 
18716a1bdd4aSMickaël Salaün 	/* Checks that linkind doesn't require the ability to delete a file. */
18726a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
18736a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
1874e1199815SMickaël Salaün }
1875e1199815SMickaël Salaün 
187655e55920SMickaël Salaün static int test_rename(const char *const oldpath, const char *const newpath)
187755e55920SMickaël Salaün {
187855e55920SMickaël Salaün 	if (rename(oldpath, newpath))
187955e55920SMickaël Salaün 		return errno;
188055e55920SMickaël Salaün 	return 0;
188155e55920SMickaël Salaün }
188255e55920SMickaël Salaün 
188355e55920SMickaël Salaün static int test_exchange(const char *const oldpath, const char *const newpath)
188455e55920SMickaël Salaün {
188555e55920SMickaël Salaün 	if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE))
188655e55920SMickaël Salaün 		return errno;
188755e55920SMickaël Salaün 	return 0;
188855e55920SMickaël Salaün }
188955e55920SMickaël Salaün 
1890e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_file)
1891e1199815SMickaël Salaün {
1892e1199815SMickaël Salaün 	const struct rule rules[] = {
1893e1199815SMickaël Salaün 		{
1894e1199815SMickaël Salaün 			.path = dir_s1d3,
1895e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1896e1199815SMickaël Salaün 		},
1897e1199815SMickaël Salaün 		{
1898e1199815SMickaël Salaün 			.path = dir_s2d2,
1899e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1900e1199815SMickaël Salaün 		},
1901135464f9SMickaël Salaün 		{},
1902e1199815SMickaël Salaün 	};
1903371183faSMickaël Salaün 	const int ruleset_fd =
1904371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
1905e1199815SMickaël Salaün 
1906e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1907e1199815SMickaël Salaün 
1908e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1909e1199815SMickaël Salaün 
1910e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1911e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1912e1199815SMickaël Salaün 
1913e1199815SMickaël Salaün 	/*
1914e1199815SMickaël Salaün 	 * Tries to replace a file, from a directory that allows file removal,
1915e1199815SMickaël Salaün 	 * but to a different directory (which also allows file removal).
1916e1199815SMickaël Salaün 	 */
1917e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3));
1918e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1919e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3,
1920e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1921e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1922e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
1923e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1924e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1925e1199815SMickaël Salaün 
1926e1199815SMickaël Salaün 	/*
1927e1199815SMickaël Salaün 	 * Tries to replace a file, from a directory that denies file removal,
1928e1199815SMickaël Salaün 	 * to a different directory (which allows file removal).
1929e1199815SMickaël Salaün 	 */
1930e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
193155e55920SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1932e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3,
1933e1199815SMickaël Salaün 				RENAME_EXCHANGE));
193455e55920SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1935e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3,
1936e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1937e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1938e1199815SMickaël Salaün 
1939e1199815SMickaël Salaün 	/* Exchanges files and directories that partially allow removal. */
1940e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1,
1941e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1942e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
19436a1bdd4aSMickaël Salaün 	/* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */
19446a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1));
19456a1bdd4aSMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1946e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2,
1947e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1948e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
19496a1bdd4aSMickaël Salaün 	/* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */
19506a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
19516a1bdd4aSMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1952e1199815SMickaël Salaün 
1953e1199815SMickaël Salaün 	/* Renames files with different parents. */
1954e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
1955e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1956e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1957e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
195855e55920SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1959e1199815SMickaël Salaün 
1960e1199815SMickaël Salaün 	/* Exchanges and renames files with same parent. */
1961e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3,
1962e1199815SMickaël Salaün 			       RENAME_EXCHANGE));
1963e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3));
1964e1199815SMickaël Salaün 
1965e1199815SMickaël Salaün 	/* Exchanges files and directories with same parent, twice. */
1966e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
1967e1199815SMickaël Salaün 			       RENAME_EXCHANGE));
1968e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
1969e1199815SMickaël Salaün 			       RENAME_EXCHANGE));
1970e1199815SMickaël Salaün }
1971e1199815SMickaël Salaün 
1972e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_dir)
1973e1199815SMickaël Salaün {
1974e1199815SMickaël Salaün 	const struct rule rules[] = {
1975e1199815SMickaël Salaün 		{
1976e1199815SMickaël Salaün 			.path = dir_s1d2,
1977e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
1978e1199815SMickaël Salaün 		},
1979e1199815SMickaël Salaün 		{
1980e1199815SMickaël Salaün 			.path = dir_s2d1,
1981e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
1982e1199815SMickaël Salaün 		},
1983135464f9SMickaël Salaün 		{},
1984e1199815SMickaël Salaün 	};
1985371183faSMickaël Salaün 	const int ruleset_fd =
1986371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
1987e1199815SMickaël Salaün 
1988e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1989e1199815SMickaël Salaün 
1990e1199815SMickaël Salaün 	/* Empties dir_s1d3 to allow renaming. */
1991e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1992e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
1993e1199815SMickaël Salaün 
1994e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1995e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1996e1199815SMickaël Salaün 
1997e1199815SMickaël Salaün 	/* Exchanges and renames directory to a different parent. */
1998e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
1999e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2000e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2001e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3));
2002e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2003e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
2004e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2005e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2006e1199815SMickaël Salaün 
2007e1199815SMickaël Salaün 	/*
2008e1199815SMickaël Salaün 	 * Exchanges directory to the same parent, which doesn't allow
2009e1199815SMickaël Salaün 	 * directory removal.
2010e1199815SMickaël Salaün 	 */
2011e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1,
2012e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2013e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
20146a1bdd4aSMickaël Salaün 	/* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */
20156a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1));
20166a1bdd4aSMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2017e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2,
2018e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2019e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
20206a1bdd4aSMickaël Salaün 	/* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */
20216a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
20226a1bdd4aSMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2023e1199815SMickaël Salaün 
2024e1199815SMickaël Salaün 	/*
2025e1199815SMickaël Salaün 	 * Exchanges and renames directory to the same parent, which allows
2026e1199815SMickaël Salaün 	 * directory removal.
2027e1199815SMickaël Salaün 	 */
2028e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2,
2029e1199815SMickaël Salaün 			       RENAME_EXCHANGE));
2030e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(dir_s1d3));
2031e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
2032e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3));
2033e1199815SMickaël Salaün 	ASSERT_EQ(0, rmdir(dir_s1d3));
2034e1199815SMickaël Salaün }
2035e1199815SMickaël Salaün 
2036f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_refer)
2037f4056b92SMickaël Salaün {
2038f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2039f4056b92SMickaël Salaün 		{
2040f4056b92SMickaël Salaün 			.path = dir_s1d2,
2041f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2042f4056b92SMickaël Salaün 		},
2043f4056b92SMickaël Salaün 		{
2044f4056b92SMickaël Salaün 			.path = dir_s2d2,
2045f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2046f4056b92SMickaël Salaün 		},
2047f4056b92SMickaël Salaün 		{},
2048f4056b92SMickaël Salaün 	};
2049f4056b92SMickaël Salaün 	int ruleset_fd =
2050f4056b92SMickaël Salaün 		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);
2051f4056b92SMickaël Salaün 
2052f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2053f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2054f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2055f4056b92SMickaël Salaün 
2056f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1));
2057f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2058f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2));
2059f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2060f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));
2061f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2062f4056b92SMickaël Salaün 
2063f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1));
2064f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2065f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2));
2066f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2067f4056b92SMickaël Salaün 	/*
2068f4056b92SMickaël Salaün 	 * Moving should only be allowed when the source and the destination
2069f4056b92SMickaël Salaün 	 * parent directory have REFER.
2070f4056b92SMickaël Salaün 	 */
2071f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3));
2072f4056b92SMickaël Salaün 	ASSERT_EQ(ENOTEMPTY, errno);
2073f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d3));
2074f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2075f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3));
2076f4056b92SMickaël Salaün }
2077f4056b92SMickaël Salaün 
207855e55920SMickaël Salaün /* Checks renames beneath dir_s1d1. */
207955e55920SMickaël Salaün static void refer_denied_by_default(struct __test_metadata *const _metadata,
208055e55920SMickaël Salaün 				    const struct rule layer1[],
208155e55920SMickaël Salaün 				    const int layer1_err,
208255e55920SMickaël Salaün 				    const struct rule layer2[])
208355e55920SMickaël Salaün {
208455e55920SMickaël Salaün 	int ruleset_fd;
208555e55920SMickaël Salaün 
208655e55920SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
208755e55920SMickaël Salaün 
208855e55920SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
208955e55920SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
209055e55920SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
209155e55920SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
209255e55920SMickaël Salaün 
209355e55920SMickaël Salaün 	/*
209455e55920SMickaël Salaün 	 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to
209555e55920SMickaël Salaün 	 * layer1_err), then it allows some different-parent renames and links.
209655e55920SMickaël Salaün 	 */
209755e55920SMickaël Salaün 	ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2));
209855e55920SMickaël Salaün 	if (layer1_err == 0)
209955e55920SMickaël Salaün 		ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1));
210055e55920SMickaël Salaün 	ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2));
210155e55920SMickaël Salaün 	ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1));
210255e55920SMickaël Salaün 
210355e55920SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
210455e55920SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
210555e55920SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
210655e55920SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
210755e55920SMickaël Salaün 
210855e55920SMickaël Salaün 	/*
210955e55920SMickaël Salaün 	 * Now, either the first or the second layer does not handle
211055e55920SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent
211155e55920SMickaël Salaün 	 * renames and links are denied, thus making the layer handling
211255e55920SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_REFER null and void.
211355e55920SMickaël Salaün 	 */
211455e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2));
211555e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2));
211655e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1));
211755e55920SMickaël Salaün }
211855e55920SMickaël Salaün 
211955e55920SMickaël Salaün const struct rule layer_dir_s1d1_refer[] = {
212055e55920SMickaël Salaün 	{
212155e55920SMickaël Salaün 		.path = dir_s1d1,
212255e55920SMickaël Salaün 		.access = LANDLOCK_ACCESS_FS_REFER,
212355e55920SMickaël Salaün 	},
212455e55920SMickaël Salaün 	{},
212555e55920SMickaël Salaün };
212655e55920SMickaël Salaün 
212755e55920SMickaël Salaün const struct rule layer_dir_s1d1_execute[] = {
212855e55920SMickaël Salaün 	{
212955e55920SMickaël Salaün 		/* Matches a parent directory. */
213055e55920SMickaël Salaün 		.path = dir_s1d1,
213155e55920SMickaël Salaün 		.access = LANDLOCK_ACCESS_FS_EXECUTE,
213255e55920SMickaël Salaün 	},
213355e55920SMickaël Salaün 	{},
213455e55920SMickaël Salaün };
213555e55920SMickaël Salaün 
213655e55920SMickaël Salaün const struct rule layer_dir_s2d1_execute[] = {
213755e55920SMickaël Salaün 	{
213855e55920SMickaël Salaün 		/* Does not match a parent directory. */
213955e55920SMickaël Salaün 		.path = dir_s2d1,
214055e55920SMickaël Salaün 		.access = LANDLOCK_ACCESS_FS_EXECUTE,
214155e55920SMickaël Salaün 	},
214255e55920SMickaël Salaün 	{},
214355e55920SMickaël Salaün };
214455e55920SMickaël Salaün 
214555e55920SMickaël Salaün /*
214655e55920SMickaël Salaün  * Tests precedence over renames: denied by default for different parent
214755e55920SMickaël Salaün  * directories, *with* a rule matching a parent directory, but not directly
214855e55920SMickaël Salaün  * denying access (with MAKE_REG nor REMOVE).
214955e55920SMickaël Salaün  */
215055e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default1)
215155e55920SMickaël Salaün {
215255e55920SMickaël Salaün 	refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
215355e55920SMickaël Salaün 				layer_dir_s1d1_execute);
215455e55920SMickaël Salaün }
215555e55920SMickaël Salaün 
215655e55920SMickaël Salaün /*
215755e55920SMickaël Salaün  * Same test but this time turning around the ABI version order: the first
215855e55920SMickaël Salaün  * layer does not handle LANDLOCK_ACCESS_FS_REFER.
215955e55920SMickaël Salaün  */
216055e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default2)
216155e55920SMickaël Salaün {
216255e55920SMickaël Salaün 	refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV,
216355e55920SMickaël Salaün 				layer_dir_s1d1_refer);
216455e55920SMickaël Salaün }
216555e55920SMickaël Salaün 
216655e55920SMickaël Salaün /*
216755e55920SMickaël Salaün  * Tests precedence over renames: denied by default for different parent
216855e55920SMickaël Salaün  * directories, *without* a rule matching a parent directory, but not directly
216955e55920SMickaël Salaün  * denying access (with MAKE_REG nor REMOVE).
217055e55920SMickaël Salaün  */
217155e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default3)
217255e55920SMickaël Salaün {
217355e55920SMickaël Salaün 	refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
217455e55920SMickaël Salaün 				layer_dir_s2d1_execute);
217555e55920SMickaël Salaün }
217655e55920SMickaël Salaün 
217755e55920SMickaël Salaün /*
217855e55920SMickaël Salaün  * Same test but this time turning around the ABI version order: the first
217955e55920SMickaël Salaün  * layer does not handle LANDLOCK_ACCESS_FS_REFER.
218055e55920SMickaël Salaün  */
218155e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default4)
218255e55920SMickaël Salaün {
218355e55920SMickaël Salaün 	refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV,
218455e55920SMickaël Salaün 				layer_dir_s1d1_refer);
218555e55920SMickaël Salaün }
218655e55920SMickaël Salaün 
2187f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_link)
2188f4056b92SMickaël Salaün {
2189f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2190f4056b92SMickaël Salaün 		{
2191f4056b92SMickaël Salaün 			.path = dir_s1d2,
2192f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2193f4056b92SMickaël Salaün 		},
2194f4056b92SMickaël Salaün 		{
2195f4056b92SMickaël Salaün 			.path = dir_s1d3,
2196f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2197f4056b92SMickaël Salaün 		},
2198f4056b92SMickaël Salaün 		{
2199f4056b92SMickaël Salaün 			.path = dir_s2d2,
2200f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2201f4056b92SMickaël Salaün 		},
2202f4056b92SMickaël Salaün 		{
2203f4056b92SMickaël Salaün 			.path = dir_s2d3,
2204f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2205f4056b92SMickaël Salaün 		},
2206f4056b92SMickaël Salaün 		{},
2207f4056b92SMickaël Salaün 	};
2208f4056b92SMickaël Salaün 	const int ruleset_fd = create_ruleset(
2209f4056b92SMickaël Salaün 		_metadata,
2210f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2211f4056b92SMickaël Salaün 
2212f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2213f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2214f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2215f4056b92SMickaël Salaün 
2216f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
2217f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2218f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2219f4056b92SMickaël Salaün 
2220f4056b92SMickaël Salaün 	/* Denies linking because of missing MAKE_REG. */
2221f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
2222f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2223f4056b92SMickaël Salaün 	/* Denies linking because of missing source and destination REFER. */
2224f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
2225f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2226f4056b92SMickaël Salaün 	/* Denies linking because of missing source REFER. */
2227f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3));
2228f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2229f4056b92SMickaël Salaün 
2230f4056b92SMickaël Salaün 	/* Denies linking because of missing MAKE_REG. */
2231f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1));
2232f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2233f4056b92SMickaël Salaün 	/* Denies linking because of missing destination REFER. */
2234f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2));
2235f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2236f4056b92SMickaël Salaün 
2237f4056b92SMickaël Salaün 	/* Allows linking because of REFER and MAKE_REG. */
2238f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file1_s2d2, file1_s1d3));
2239f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d2));
2240f4056b92SMickaël Salaün 	/* Reverse linking denied because of missing MAKE_REG. */
2241f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2));
2242f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2243f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d3));
2244f4056b92SMickaël Salaün 	/* Checks reverse linking. */
2245f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file1_s2d3));
2246f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2247f4056b92SMickaël Salaün 
2248f4056b92SMickaël Salaün 	/*
2249f4056b92SMickaël Salaün 	 * This is OK for a file link, but it should not be allowed for a
2250f4056b92SMickaël Salaün 	 * directory rename (because of the superset of access rights.
2251f4056b92SMickaël Salaün 	 */
2252f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file1_s2d3, file1_s1d3));
2253f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2254f4056b92SMickaël Salaün 
2255f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
2256f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2257f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
2258f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2259f4056b92SMickaël Salaün 
2260f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
2261f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
2262f4056b92SMickaël Salaün }
2263f4056b92SMickaël Salaün 
2264f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_rename)
2265f4056b92SMickaël Salaün {
2266f4056b92SMickaël Salaün 	/* Same rules as for reparent_link. */
2267f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2268f4056b92SMickaël Salaün 		{
2269f4056b92SMickaël Salaün 			.path = dir_s1d2,
2270f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2271f4056b92SMickaël Salaün 		},
2272f4056b92SMickaël Salaün 		{
2273f4056b92SMickaël Salaün 			.path = dir_s1d3,
2274f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2275f4056b92SMickaël Salaün 		},
2276f4056b92SMickaël Salaün 		{
2277f4056b92SMickaël Salaün 			.path = dir_s2d2,
2278f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2279f4056b92SMickaël Salaün 		},
2280f4056b92SMickaël Salaün 		{
2281f4056b92SMickaël Salaün 			.path = dir_s2d3,
2282f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2283f4056b92SMickaël Salaün 		},
2284f4056b92SMickaël Salaün 		{},
2285f4056b92SMickaël Salaün 	};
2286f4056b92SMickaël Salaün 	const int ruleset_fd = create_ruleset(
2287f4056b92SMickaël Salaün 		_metadata,
2288f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2289f4056b92SMickaël Salaün 
2290f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2291f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2292f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2293f4056b92SMickaël Salaün 
2294f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2295f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2296f4056b92SMickaël Salaün 
2297f4056b92SMickaël Salaün 	/* Denies renaming because of missing MAKE_REG. */
2298f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1,
2299f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2300f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2301f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1,
2302f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2303f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2304f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
2305f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
2306f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2307f4056b92SMickaël Salaün 	/* Even denies same file exchange. */
2308f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1,
2309f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2310f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2311f4056b92SMickaël Salaün 
2312f4056b92SMickaël Salaün 	/* Denies renaming because of missing source and destination REFER. */
2313f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2));
2314f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2315f4056b92SMickaël Salaün 	/*
2316f4056b92SMickaël Salaün 	 * Denies renaming because of missing MAKE_REG, source and destination
2317f4056b92SMickaël Salaün 	 * REFER.
2318f4056b92SMickaël Salaün 	 */
2319f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1,
2320f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2321f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2322f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1,
2323f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2324f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2325f4056b92SMickaël Salaün 
2326f4056b92SMickaël Salaün 	/* Denies renaming because of missing source REFER. */
2327f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2328f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2329f4056b92SMickaël Salaün 	/* Denies renaming because of missing MAKE_REG. */
2330f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3,
2331f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2332f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2333f4056b92SMickaël Salaün 
2334f4056b92SMickaël Salaün 	/* Denies renaming because of missing MAKE_REG. */
2335f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1));
2336f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2337f4056b92SMickaël Salaün 	/* Denies renaming because of missing destination REFER*/
2338f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
2339f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2340f4056b92SMickaël Salaün 
2341f4056b92SMickaël Salaün 	/* Denies exchange because of one missing MAKE_REG. */
2342f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3,
2343f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2344f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2345f4056b92SMickaël Salaün 	/* Allows renaming because of REFER and MAKE_REG. */
2346f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3));
2347f4056b92SMickaël Salaün 
2348f4056b92SMickaël Salaün 	/* Reverse renaming denied because of missing MAKE_REG. */
2349f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2));
2350f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2351f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d3));
2352f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2353f4056b92SMickaël Salaün 
2354f4056b92SMickaël Salaün 	/* Tests reverse renaming. */
2355f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2356f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3,
2357f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2358f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2359f4056b92SMickaël Salaün 
2360f4056b92SMickaël Salaün 	/*
2361f4056b92SMickaël Salaün 	 * This is OK for a file rename, but it should not be allowed for a
2362f4056b92SMickaël Salaün 	 * directory rename (because of the superset of access rights).
2363f4056b92SMickaël Salaün 	 */
2364f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2365f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2366f4056b92SMickaël Salaün 
2367f4056b92SMickaël Salaün 	/*
2368f4056b92SMickaël Salaün 	 * Tests superset restrictions applied to directories.  Not only the
2369f4056b92SMickaël Salaün 	 * dir_s2d3's parent (dir_s2d2) should be taken into account but also
2370f4056b92SMickaël Salaün 	 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right
2371f4056b92SMickaël Salaün 	 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided
2372f4056b92SMickaël Salaün 	 * directly by the moved dir_s2d3.
2373f4056b92SMickaël Salaün 	 */
2374f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3));
2375f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3));
2376f4056b92SMickaël Salaün 	/*
2377f4056b92SMickaël Salaün 	 * The first rename is allowed but not the exchange because dir_s1d3's
2378f4056b92SMickaël Salaün 	 * parent (dir_s1d2) doesn't have REFER.
2379f4056b92SMickaël Salaün 	 */
2380f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
2381f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2382f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2383f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3,
2384f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2385f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2386f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3));
2387f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2388f4056b92SMickaël Salaün 
2389f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3));
2390f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2391f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2));
2392f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2393f4056b92SMickaël Salaün 
2394f4056b92SMickaël Salaün 	/* Renaming in the same directory is always allowed. */
2395f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2));
2396f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3));
2397f4056b92SMickaël Salaün 
2398f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2399f4056b92SMickaël Salaün 	/* Denies because of missing source MAKE_REG and destination REFER. */
2400f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2));
2401f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2402f4056b92SMickaël Salaün 
2403f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2404f4056b92SMickaël Salaün 	/* Denies because of missing source MAKE_REG and REFER. */
2405f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3));
2406f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2407f4056b92SMickaël Salaün }
2408f4056b92SMickaël Salaün 
2409f4056b92SMickaël Salaün static void
2410f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata)
2411f4056b92SMickaël Salaün {
2412f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2413f4056b92SMickaël Salaün 		{
2414f4056b92SMickaël Salaün 			.path = dir_s1d2,
2415f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2416f4056b92SMickaël Salaün 		},
2417f4056b92SMickaël Salaün 		{
2418f4056b92SMickaël Salaün 			/* Interesting for the layer2 tests. */
2419f4056b92SMickaël Salaün 			.path = dir_s1d3,
2420f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2421f4056b92SMickaël Salaün 		},
2422f4056b92SMickaël Salaün 		{
2423f4056b92SMickaël Salaün 			.path = dir_s2d2,
2424f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2425f4056b92SMickaël Salaün 		},
2426f4056b92SMickaël Salaün 		{
2427f4056b92SMickaël Salaün 			.path = dir_s2d3,
2428f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2429f4056b92SMickaël Salaün 		},
2430f4056b92SMickaël Salaün 		{},
2431f4056b92SMickaël Salaün 	};
2432f4056b92SMickaël Salaün 	const int ruleset_fd = create_ruleset(
2433f4056b92SMickaël Salaün 		_metadata,
2434f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2435f4056b92SMickaël Salaün 
2436f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2437f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2438f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2439f4056b92SMickaël Salaün }
2440f4056b92SMickaël Salaün 
2441f4056b92SMickaël Salaün static void
2442f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata)
2443f4056b92SMickaël Salaün {
2444f4056b92SMickaël Salaün 	const struct rule layer2[] = {
2445f4056b92SMickaël Salaün 		{
2446f4056b92SMickaël Salaün 			.path = dir_s2d3,
2447f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_DIR,
2448f4056b92SMickaël Salaün 		},
2449f4056b92SMickaël Salaün 		{},
2450f4056b92SMickaël Salaün 	};
2451f4056b92SMickaël Salaün 	/*
2452f4056b92SMickaël Salaün 	 * Same checks as before but with a second layer and a new MAKE_DIR
2453f4056b92SMickaël Salaün 	 * rule (and no explicit handling of REFER).
2454f4056b92SMickaël Salaün 	 */
2455f4056b92SMickaël Salaün 	const int ruleset_fd =
2456f4056b92SMickaël Salaün 		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2);
2457f4056b92SMickaël Salaün 
2458f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2459f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2460f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2461f4056b92SMickaël Salaün }
2462f4056b92SMickaël Salaün 
2463f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename1)
2464f4056b92SMickaël Salaün {
2465f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d2));
2466f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d3));
2467f4056b92SMickaël Salaün 
2468f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2469f4056b92SMickaël Salaün 
2470f4056b92SMickaël Salaün 	/*
2471f4056b92SMickaël Salaün 	 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock
2472f4056b92SMickaël Salaün 	 * because it doesn't inherit new access rights.
2473f4056b92SMickaël Salaün 	 */
2474f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
2475f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
2476f4056b92SMickaël Salaün 
2477f4056b92SMickaël Salaün 	/*
2478f4056b92SMickaël Salaün 	 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it
2479f4056b92SMickaël Salaün 	 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is
2480f4056b92SMickaël Salaün 	 * already allowed for dir_s1d3.
2481f4056b92SMickaël Salaün 	 */
2482f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3));
2483f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3));
2484f4056b92SMickaël Salaün 
2485f4056b92SMickaël Salaün 	/*
2486f4056b92SMickaël Salaün 	 * However, moving the file1_s1d3 file below dir_s2d3 is allowed
2487f4056b92SMickaël Salaün 	 * because it cannot inherit MAKE_REG right (which is dedicated to
2488f4056b92SMickaël Salaün 	 * directories).
2489f4056b92SMickaël Salaün 	 */
2490f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2491f4056b92SMickaël Salaün 
2492f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce2(_metadata);
2493f4056b92SMickaël Salaün 
2494f4056b92SMickaël Salaün 	/*
2495f4056b92SMickaël Salaün 	 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because
2496f4056b92SMickaël Salaün 	 * MAKE_DIR is not tied to dir_s2d2.
2497f4056b92SMickaël Salaün 	 */
2498f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2));
2499f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2500f4056b92SMickaël Salaün 
2501f4056b92SMickaël Salaün 	/*
2502f4056b92SMickaël Salaün 	 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it
2503f4056b92SMickaël Salaün 	 * would grants MAKE_REG and MAKE_DIR rights to it.
2504f4056b92SMickaël Salaün 	 */
2505f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
2506f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2507f4056b92SMickaël Salaün 
2508f4056b92SMickaël Salaün 	/*
250955e55920SMickaël Salaün 	 * Moving the file2_s1d3 file below dir_s2d3 is denied because the
251055e55920SMickaël Salaün 	 * second layer does not handle REFER, which is always denied by
251155e55920SMickaël Salaün 	 * default.
2512f4056b92SMickaël Salaün 	 */
251355e55920SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3));
251455e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2515f4056b92SMickaël Salaün }
2516f4056b92SMickaël Salaün 
2517f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename2)
2518f4056b92SMickaël Salaün {
2519f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2520f4056b92SMickaël Salaün 
2521f4056b92SMickaël Salaün 	/* Checks EACCES predominance over EXDEV. */
2522f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
2523f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2524f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2));
2525f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2526f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
2527f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2528f4056b92SMickaël Salaün 	/* Modify layout! */
2529f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3));
2530f4056b92SMickaël Salaün 
2531f4056b92SMickaël Salaün 	/* Without REFER source. */
2532f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
2533f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2534f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
2535f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2536f4056b92SMickaël Salaün 
2537f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce2(_metadata);
2538f4056b92SMickaël Salaün 
2539f4056b92SMickaël Salaün 	/* Checks EACCES predominance over EXDEV. */
2540f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
2541f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2542f4056b92SMickaël Salaün 	/* Checks with actual file2_s1d2. */
2543f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2));
2544f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2545f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
2546f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
254755e55920SMickaël Salaün 	/*
254855e55920SMickaël Salaün 	 * Modifying the layout is now denied because the second layer does not
254955e55920SMickaël Salaün 	 * handle REFER, which is always denied by default.
255055e55920SMickaël Salaün 	 */
255155e55920SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
255255e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2553f4056b92SMickaël Salaün 
2554f4056b92SMickaël Salaün 	/* Without REFER source, EACCES wins over EXDEV. */
2555f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
2556f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2557f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
2558f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2559f4056b92SMickaël Salaün }
2560f4056b92SMickaël Salaün 
2561f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange1)
2562f4056b92SMickaël Salaün {
2563f4056b92SMickaël Salaün 	const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 =
2564f4056b92SMickaël Salaün 							       file2_s2d3;
2565f4056b92SMickaël Salaün 
2566f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2567f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
2568f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2569f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
2570f4056b92SMickaël Salaün 
2571f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2572f4056b92SMickaël Salaün 
2573f4056b92SMickaël Salaün 	/* Error predominance with file exchange: returns EXDEV and EACCES. */
2574f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
2575f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2576f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2577f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
2578f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2579f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2580f4056b92SMickaël Salaün 
2581f4056b92SMickaël Salaün 	/*
2582f4056b92SMickaël Salaün 	 * Checks with directories which creation could be allowed, but denied
2583f4056b92SMickaël Salaün 	 * because of access rights that would be inherited.
2584f4056b92SMickaël Salaün 	 */
2585f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
2586f4056b92SMickaël Salaün 				dir_file2_s2d3, RENAME_EXCHANGE));
2587f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2588f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
2589f4056b92SMickaël Salaün 				dir_file1_s1d2, RENAME_EXCHANGE));
2590f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2591f4056b92SMickaël Salaün 
2592f4056b92SMickaël Salaün 	/* Checks with same access rights. */
2593f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
2594f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2595f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2596f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2597f4056b92SMickaël Salaün 
2598f4056b92SMickaël Salaün 	/* Checks with different (child-only) access rights. */
2599f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
2600f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2601f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
2602f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2603f4056b92SMickaël Salaün 
2604f4056b92SMickaël Salaün 	/*
2605f4056b92SMickaël Salaün 	 * Checks that exchange between file and directory are consistent.
2606f4056b92SMickaël Salaün 	 *
2607f4056b92SMickaël Salaün 	 * Moving a file (file1_s2d2) to a directory which only grants more
2608f4056b92SMickaël Salaün 	 * directory-related access rights is allowed, and at the same time
2609f4056b92SMickaël Salaün 	 * moving a directory (dir_file2_s2d3) to another directory which
2610f4056b92SMickaël Salaün 	 * grants less access rights is allowed too.
2611f4056b92SMickaël Salaün 	 *
2612f4056b92SMickaël Salaün 	 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments.
2613f4056b92SMickaël Salaün 	 */
2614f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2615f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2616f4056b92SMickaël Salaün 	/*
2617f4056b92SMickaël Salaün 	 * However, moving back the directory is denied because it would get
2618f4056b92SMickaël Salaün 	 * more access rights than the current state and because file creation
2619f4056b92SMickaël Salaün 	 * is forbidden (in dir_s2d2).
2620f4056b92SMickaël Salaün 	 */
2621f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2622f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2623f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2624f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2625f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2626f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2627f4056b92SMickaël Salaün 
2628f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce2(_metadata);
2629f4056b92SMickaël Salaün 
2630f4056b92SMickaël Salaün 	/* Error predominance with file exchange: returns EXDEV and EACCES. */
2631f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
2632f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2633f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2634f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
2635f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2636f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2637f4056b92SMickaël Salaün 
2638f4056b92SMickaël Salaün 	/* Checks with directories which creation is now denied. */
2639f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
2640f4056b92SMickaël Salaün 				dir_file2_s2d3, RENAME_EXCHANGE));
2641f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2642f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
2643f4056b92SMickaël Salaün 				dir_file1_s1d2, RENAME_EXCHANGE));
2644f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2645f4056b92SMickaël Salaün 
2646f4056b92SMickaël Salaün 	/* Checks with different (child-only) access rights. */
2647f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
2648f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2649f4056b92SMickaël Salaün 	/* Denied because of MAKE_DIR. */
2650f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2651f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2652f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2653f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2654f4056b92SMickaël Salaün 
2655f4056b92SMickaël Salaün 	/* Checks with different (child-only) access rights. */
2656f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
2657f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2658f4056b92SMickaël Salaün 	/* Denied because of MAKE_DIR. */
2659f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2660f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
2661f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2662f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2663f4056b92SMickaël Salaün 
2664f4056b92SMickaël Salaün 	/* See layout1.reparent_exdev_layers_exchange2 for complement. */
2665f4056b92SMickaël Salaün }
2666f4056b92SMickaël Salaün 
2667f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange2)
2668f4056b92SMickaël Salaün {
2669f4056b92SMickaël Salaün 	const char *const dir_file2_s2d3 = file2_s2d3;
2670f4056b92SMickaël Salaün 
2671f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2672f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
2673f4056b92SMickaël Salaün 
2674f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2675f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce2(_metadata);
2676f4056b92SMickaël Salaün 
2677f4056b92SMickaël Salaün 	/* Checks that exchange between file and directory are consistent. */
2678f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2679f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2680f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2681f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2682f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2683f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2684f4056b92SMickaël Salaün }
2685f4056b92SMickaël Salaün 
2686f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange3)
2687f4056b92SMickaël Salaün {
2688f4056b92SMickaël Salaün 	const char *const dir_file2_s2d3 = file2_s2d3;
2689f4056b92SMickaël Salaün 
2690f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2691f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
2692f4056b92SMickaël Salaün 
2693f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2694f4056b92SMickaël Salaün 
2695f4056b92SMickaël Salaün 	/*
2696f4056b92SMickaël Salaün 	 * Checks that exchange between file and directory are consistent,
2697f4056b92SMickaël Salaün 	 * including with inverted arguments (see
2698f4056b92SMickaël Salaün 	 * layout1.reparent_exdev_layers_exchange1).
2699f4056b92SMickaël Salaün 	 */
2700f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2701f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2702f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2703f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2704f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2705f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2706f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2707f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2708f4056b92SMickaël Salaün }
2709f4056b92SMickaël Salaün 
2710f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_remove)
2711f4056b92SMickaël Salaün {
2712f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2713f4056b92SMickaël Salaün 		{
2714f4056b92SMickaël Salaün 			.path = dir_s1d1,
2715f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER |
2716f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_REMOVE_DIR,
2717f4056b92SMickaël Salaün 		},
2718f4056b92SMickaël Salaün 		{
2719f4056b92SMickaël Salaün 			.path = dir_s1d2,
2720f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2721f4056b92SMickaël Salaün 		},
2722f4056b92SMickaël Salaün 		{
2723f4056b92SMickaël Salaün 			.path = dir_s2d1,
2724f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER |
2725f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_REMOVE_FILE,
2726f4056b92SMickaël Salaün 		},
2727f4056b92SMickaël Salaün 		{},
2728f4056b92SMickaël Salaün 	};
2729f4056b92SMickaël Salaün 	const int ruleset_fd = create_ruleset(
2730f4056b92SMickaël Salaün 		_metadata,
2731f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR |
2732f4056b92SMickaël Salaün 			LANDLOCK_ACCESS_FS_REMOVE_FILE,
2733f4056b92SMickaël Salaün 		layer1);
2734f4056b92SMickaël Salaün 
2735f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2736f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2737f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2738f4056b92SMickaël Salaün 
2739f4056b92SMickaël Salaün 	/* Access denied because of wrong/swapped remove file/dir. */
2740f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2));
2741f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2742f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1));
2743f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2744f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2,
2745f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2746f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2747f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3,
2748f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2749f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2750f4056b92SMickaël Salaün 
2751f4056b92SMickaël Salaün 	/* Access allowed thanks to the matching rights. */
2752f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2));
2753f4056b92SMickaël Salaün 	ASSERT_EQ(EISDIR, errno);
2754f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1));
2755f4056b92SMickaël Salaün 	ASSERT_EQ(ENOTDIR, errno);
2756f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
2757f4056b92SMickaël Salaün 	ASSERT_EQ(ENOTDIR, errno);
2758f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d1));
2759f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2760f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
2761f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1));
2762f4056b92SMickaël Salaün 
2763f4056b92SMickaël Salaün 	/* Effectively removes a file and a directory by exchanging them. */
2764f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
2765f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
2766f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2767f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
2768f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2769f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2770f4056b92SMickaël Salaün }
2771f4056b92SMickaël Salaün 
2772f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_dom_superset)
2773f4056b92SMickaël Salaün {
2774f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2775f4056b92SMickaël Salaün 		{
2776f4056b92SMickaël Salaün 			.path = dir_s1d2,
2777f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2778f4056b92SMickaël Salaün 		},
2779f4056b92SMickaël Salaün 		{
2780f4056b92SMickaël Salaün 			.path = file1_s1d2,
2781f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
2782f4056b92SMickaël Salaün 		},
2783f4056b92SMickaël Salaün 		{
2784f4056b92SMickaël Salaün 			.path = dir_s1d3,
2785f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_SOCK |
2786f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_EXECUTE,
2787f4056b92SMickaël Salaün 		},
2788f4056b92SMickaël Salaün 		{
2789f4056b92SMickaël Salaün 			.path = dir_s2d2,
2790f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER |
2791f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_EXECUTE |
2792f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_MAKE_SOCK,
2793f4056b92SMickaël Salaün 		},
2794f4056b92SMickaël Salaün 		{
2795f4056b92SMickaël Salaün 			.path = dir_s2d3,
2796f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
2797f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_MAKE_FIFO,
2798f4056b92SMickaël Salaün 		},
2799f4056b92SMickaël Salaün 		{},
2800f4056b92SMickaël Salaün 	};
2801f4056b92SMickaël Salaün 	int ruleset_fd = create_ruleset(_metadata,
2802f4056b92SMickaël Salaün 					LANDLOCK_ACCESS_FS_REFER |
2803f4056b92SMickaël Salaün 						LANDLOCK_ACCESS_FS_EXECUTE |
2804f4056b92SMickaël Salaün 						LANDLOCK_ACCESS_FS_MAKE_SOCK |
2805f4056b92SMickaël Salaün 						LANDLOCK_ACCESS_FS_READ_FILE |
2806f4056b92SMickaël Salaün 						LANDLOCK_ACCESS_FS_MAKE_FIFO,
2807f4056b92SMickaël Salaün 					layer1);
2808f4056b92SMickaël Salaün 
2809f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2810f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2811f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2812f4056b92SMickaël Salaün 
2813f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1));
2814f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2815f4056b92SMickaël Salaün 	/*
2816f4056b92SMickaël Salaün 	 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE
2817f4056b92SMickaël Salaün 	 * access right.
2818f4056b92SMickaël Salaün 	 */
2819f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3));
2820f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2821f4056b92SMickaël Salaün 	/*
2822f4056b92SMickaël Salaün 	 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a
2823f4056b92SMickaël Salaün 	 * superset of access rights compared to dir_s1d2, because file1_s1d2
2824f4056b92SMickaël Salaün 	 * already has these access rights anyway.
2825f4056b92SMickaël Salaün 	 */
2826f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2));
2827f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2));
2828f4056b92SMickaël Salaün 
2829f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
2830f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2831f4056b92SMickaël Salaün 	/*
2832f4056b92SMickaël Salaün 	 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access
2833f4056b92SMickaël Salaün 	 * right.
2834f4056b92SMickaël Salaün 	 */
2835f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
2836f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2837f4056b92SMickaël Salaün 	/*
2838f4056b92SMickaël Salaün 	 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset
2839f4056b92SMickaël Salaün 	 * of access rights compared to dir_s1d2, because dir_s1d3 already has
2840f4056b92SMickaël Salaün 	 * these access rights anyway.
2841f4056b92SMickaël Salaün 	 */
2842f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
2843f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
2844f4056b92SMickaël Salaün 
2845f4056b92SMickaël Salaün 	/*
2846f4056b92SMickaël Salaün 	 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back
2847f4056b92SMickaël Salaün 	 * will be denied because the new inherited access rights from dir_s1d2
2848f4056b92SMickaël Salaün 	 * will be less than the destination (original) dir_s2d3.  This is a
2849f4056b92SMickaël Salaün 	 * sinkhole scenario where we cannot move back files or directories.
2850f4056b92SMickaël Salaün 	 */
2851f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2));
2852f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
2853f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2854f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
2855f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2856f4056b92SMickaël Salaün 	/*
2857f4056b92SMickaël Salaün 	 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and
2858f4056b92SMickaël Salaün 	 * MAKE_SOCK which were inherited from dir_s1d3.
2859f4056b92SMickaël Salaün 	 */
2860f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2));
2861f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3));
2862f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2863f4056b92SMickaël Salaün }
2864f4056b92SMickaël Salaün 
2865e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_dir)
2866e1199815SMickaël Salaün {
2867e1199815SMickaël Salaün 	const struct rule rules[] = {
2868e1199815SMickaël Salaün 		{
2869e1199815SMickaël Salaün 			.path = dir_s1d2,
2870e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2871e1199815SMickaël Salaün 		},
2872135464f9SMickaël Salaün 		{},
2873e1199815SMickaël Salaün 	};
2874371183faSMickaël Salaün 	const int ruleset_fd =
2875371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
2876e1199815SMickaël Salaün 
2877e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2878e1199815SMickaël Salaün 
2879e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
2880e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2881e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2882e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
2883e1199815SMickaël Salaün 
2884e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2885e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2886e1199815SMickaël Salaün 
2887e1199815SMickaël Salaün 	ASSERT_EQ(0, rmdir(dir_s1d3));
2888e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
2889e1199815SMickaël Salaün 	ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));
2890e1199815SMickaël Salaün 
2891e1199815SMickaël Salaün 	/* dir_s1d2 itself cannot be removed. */
2892e1199815SMickaël Salaün 	ASSERT_EQ(-1, rmdir(dir_s1d2));
2893e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2894e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR));
2895e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2896e1199815SMickaël Salaün 	ASSERT_EQ(-1, rmdir(dir_s1d1));
2897e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2898e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR));
2899e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2900e1199815SMickaël Salaün }
2901e1199815SMickaël Salaün 
2902e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_file)
2903e1199815SMickaël Salaün {
2904e1199815SMickaël Salaün 	const struct rule rules[] = {
2905e1199815SMickaël Salaün 		{
2906e1199815SMickaël Salaün 			.path = dir_s1d2,
2907e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2908e1199815SMickaël Salaün 		},
2909135464f9SMickaël Salaün 		{},
2910e1199815SMickaël Salaün 	};
2911371183faSMickaël Salaün 	const int ruleset_fd =
2912371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
2913e1199815SMickaël Salaün 
2914e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2915e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2916e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2917e1199815SMickaël Salaün 
2918e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlink(file1_s1d1));
2919e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2920e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0));
2921e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2922e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2923e1199815SMickaël Salaün 	ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0));
2924e1199815SMickaël Salaün }
2925e1199815SMickaël Salaün 
2926e1199815SMickaël Salaün static void test_make_file(struct __test_metadata *const _metadata,
2927371183faSMickaël Salaün 			   const __u64 access, const mode_t mode,
2928371183faSMickaël Salaün 			   const dev_t dev)
2929e1199815SMickaël Salaün {
2930e1199815SMickaël Salaün 	const struct rule rules[] = {
2931e1199815SMickaël Salaün 		{
2932e1199815SMickaël Salaün 			.path = dir_s1d2,
2933e1199815SMickaël Salaün 			.access = access,
2934e1199815SMickaël Salaün 		},
2935135464f9SMickaël Salaün 		{},
2936e1199815SMickaël Salaün 	};
2937e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, access, rules);
2938e1199815SMickaël Salaün 
2939e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2940e1199815SMickaël Salaün 
2941e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
2942e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d1));
2943371183faSMickaël Salaün 	ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev))
2944371183faSMickaël Salaün 	{
2945371183faSMickaël Salaün 		TH_LOG("Failed to make file \"%s\": %s", file2_s1d1,
2946371183faSMickaël Salaün 		       strerror(errno));
2947e1199815SMickaël Salaün 	};
2948e1199815SMickaël Salaün 
2949e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2950e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
2951e1199815SMickaël Salaün 
2952e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2953e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
2954e1199815SMickaël Salaün 
2955e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2956e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2957e1199815SMickaël Salaün 
2958e1199815SMickaël Salaün 	ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev));
2959e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2960e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
2961e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2962e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
2963e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2964e1199815SMickaël Salaün 
2965371183faSMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev))
2966371183faSMickaël Salaün 	{
2967371183faSMickaël Salaün 		TH_LOG("Failed to make file \"%s\": %s", file1_s1d2,
2968371183faSMickaël Salaün 		       strerror(errno));
2969e1199815SMickaël Salaün 	};
2970e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
2971e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
2972e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
2973e1199815SMickaël Salaün 
2974e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev));
2975e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
2976e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
2977e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
2978e1199815SMickaël Salaün }
2979e1199815SMickaël Salaün 
2980e1199815SMickaël Salaün TEST_F_FORK(layout1, make_char)
2981e1199815SMickaël Salaün {
2982e1199815SMickaël Salaün 	/* Creates a /dev/null device. */
2983e1199815SMickaël Salaün 	set_cap(_metadata, CAP_MKNOD);
2984e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR,
2985e1199815SMickaël Salaün 		       makedev(1, 3));
2986e1199815SMickaël Salaün }
2987e1199815SMickaël Salaün 
2988e1199815SMickaël Salaün TEST_F_FORK(layout1, make_block)
2989e1199815SMickaël Salaün {
2990e1199815SMickaël Salaün 	/* Creates a /dev/loop0 device. */
2991e1199815SMickaël Salaün 	set_cap(_metadata, CAP_MKNOD);
2992e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK,
2993e1199815SMickaël Salaün 		       makedev(7, 0));
2994e1199815SMickaël Salaün }
2995e1199815SMickaël Salaün 
2996e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_1)
2997e1199815SMickaël Salaün {
2998e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);
2999e1199815SMickaël Salaün }
3000e1199815SMickaël Salaün 
3001e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_2)
3002e1199815SMickaël Salaün {
3003e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);
3004e1199815SMickaël Salaün }
3005e1199815SMickaël Salaün 
3006e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sock)
3007e1199815SMickaël Salaün {
3008e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);
3009e1199815SMickaël Salaün }
3010e1199815SMickaël Salaün 
3011e1199815SMickaël Salaün TEST_F_FORK(layout1, make_fifo)
3012e1199815SMickaël Salaün {
3013e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);
3014e1199815SMickaël Salaün }
3015e1199815SMickaël Salaün 
3016e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sym)
3017e1199815SMickaël Salaün {
3018e1199815SMickaël Salaün 	const struct rule rules[] = {
3019e1199815SMickaël Salaün 		{
3020e1199815SMickaël Salaün 			.path = dir_s1d2,
3021e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_SYM,
3022e1199815SMickaël Salaün 		},
3023135464f9SMickaël Salaün 		{},
3024e1199815SMickaël Salaün 	};
3025371183faSMickaël Salaün 	const int ruleset_fd =
3026371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
3027e1199815SMickaël Salaün 
3028e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3029e1199815SMickaël Salaün 
3030e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
3031e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d1));
3032e1199815SMickaël Salaün 	ASSERT_EQ(0, symlink("none", file2_s1d1));
3033e1199815SMickaël Salaün 
3034e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
3035e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
3036e1199815SMickaël Salaün 
3037e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
3038e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
3039e1199815SMickaël Salaün 
3040e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3041e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3042e1199815SMickaël Salaün 
3043e1199815SMickaël Salaün 	ASSERT_EQ(-1, symlink("none", file1_s1d1));
3044e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3045e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
3046e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3047e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
3048e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3049e1199815SMickaël Salaün 
3050e1199815SMickaël Salaün 	ASSERT_EQ(0, symlink("none", file1_s1d2));
3051e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
3052e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
3053e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
3054e1199815SMickaël Salaün 
3055e1199815SMickaël Salaün 	ASSERT_EQ(0, symlink("none", file1_s1d3));
3056e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
3057e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
3058e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
3059e1199815SMickaël Salaün }
3060e1199815SMickaël Salaün 
3061e1199815SMickaël Salaün TEST_F_FORK(layout1, make_dir)
3062e1199815SMickaël Salaün {
3063e1199815SMickaël Salaün 	const struct rule rules[] = {
3064e1199815SMickaël Salaün 		{
3065e1199815SMickaël Salaün 			.path = dir_s1d2,
3066e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_DIR,
3067e1199815SMickaël Salaün 		},
3068135464f9SMickaël Salaün 		{},
3069e1199815SMickaël Salaün 	};
3070371183faSMickaël Salaün 	const int ruleset_fd =
3071371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
3072e1199815SMickaël Salaün 
3073e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3074e1199815SMickaël Salaün 
3075e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
3076e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
3077e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
3078e1199815SMickaël Salaün 
3079e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3080e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3081e1199815SMickaël Salaün 
3082e1199815SMickaël Salaün 	/* Uses file_* as directory names. */
3083e1199815SMickaël Salaün 	ASSERT_EQ(-1, mkdir(file1_s1d1, 0700));
3084e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3085e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
3086e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(file1_s1d3, 0700));
3087e1199815SMickaël Salaün }
3088e1199815SMickaël Salaün 
3089e1199815SMickaël Salaün static int open_proc_fd(struct __test_metadata *const _metadata, const int fd,
3090e1199815SMickaël Salaün 			const int open_flags)
3091e1199815SMickaël Salaün {
3092e1199815SMickaël Salaün 	static const char path_template[] = "/proc/self/fd/%d";
3093e1199815SMickaël Salaün 	char procfd_path[sizeof(path_template) + 10];
3094371183faSMickaël Salaün 	const int procfd_path_size =
3095371183faSMickaël Salaün 		snprintf(procfd_path, sizeof(procfd_path), path_template, fd);
3096e1199815SMickaël Salaün 
3097e1199815SMickaël Salaün 	ASSERT_LT(procfd_path_size, sizeof(procfd_path));
3098e1199815SMickaël Salaün 	return open(procfd_path, open_flags);
3099e1199815SMickaël Salaün }
3100e1199815SMickaël Salaün 
3101e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_unlinked_file)
3102e1199815SMickaël Salaün {
3103e1199815SMickaël Salaün 	const struct rule rules[] = {
3104e1199815SMickaël Salaün 		{
3105e1199815SMickaël Salaün 			.path = file1_s1d2,
3106e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
3107e1199815SMickaël Salaün 		},
3108135464f9SMickaël Salaün 		{},
3109e1199815SMickaël Salaün 	};
3110e1199815SMickaël Salaün 	int reg_fd, proc_fd;
3111371183faSMickaël Salaün 	const int ruleset_fd = create_ruleset(
3112371183faSMickaël Salaün 		_metadata,
3113371183faSMickaël Salaün 		LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
3114371183faSMickaël Salaün 		rules);
3115e1199815SMickaël Salaün 
3116e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3117e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3118e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3119e1199815SMickaël Salaün 
3120e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
3121e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3122e1199815SMickaël Salaün 	reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC);
3123e1199815SMickaël Salaün 	ASSERT_LE(0, reg_fd);
3124e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
3125e1199815SMickaël Salaün 
3126e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC);
3127e1199815SMickaël Salaün 	ASSERT_LE(0, proc_fd);
3128e1199815SMickaël Salaün 	ASSERT_EQ(0, close(proc_fd));
3129e1199815SMickaël Salaün 
3130e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC);
3131371183faSMickaël Salaün 	ASSERT_EQ(-1, proc_fd)
3132371183faSMickaël Salaün 	{
3133371183faSMickaël Salaün 		TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd,
3134371183faSMickaël Salaün 		       strerror(errno));
3135e1199815SMickaël Salaün 	}
3136e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3137e1199815SMickaël Salaün 
3138e1199815SMickaël Salaün 	ASSERT_EQ(0, close(reg_fd));
3139e1199815SMickaël Salaün }
3140e1199815SMickaël Salaün 
3141e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_pipe)
3142e1199815SMickaël Salaün {
3143e1199815SMickaël Salaün 	int proc_fd;
3144e1199815SMickaël Salaün 	int pipe_fds[2];
3145e1199815SMickaël Salaün 	char buf = '\0';
3146e1199815SMickaël Salaün 	const struct rule rules[] = {
3147e1199815SMickaël Salaün 		{
3148e1199815SMickaël Salaün 			.path = dir_s1d2,
3149e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
3150e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
3151e1199815SMickaël Salaün 		},
3152135464f9SMickaël Salaün 		{},
3153e1199815SMickaël Salaün 	};
3154e1199815SMickaël Salaün 	/* Limits read and write access to files tied to the filesystem. */
3155371183faSMickaël Salaün 	const int ruleset_fd =
3156371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
3157e1199815SMickaël Salaün 
3158e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3159e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3160e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3161e1199815SMickaël Salaün 
3162e1199815SMickaël Salaün 	/* Checks enforcement for normal files. */
3163e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
3164e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
3165e1199815SMickaël Salaün 
3166e1199815SMickaël Salaün 	/* Checks access to pipes through FD. */
3167e1199815SMickaël Salaün 	ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC));
3168371183faSMickaël Salaün 	ASSERT_EQ(1, write(pipe_fds[1], ".", 1))
3169371183faSMickaël Salaün 	{
3170e1199815SMickaël Salaün 		TH_LOG("Failed to write in pipe: %s", strerror(errno));
3171e1199815SMickaël Salaün 	}
3172e1199815SMickaël Salaün 	ASSERT_EQ(1, read(pipe_fds[0], &buf, 1));
3173e1199815SMickaël Salaün 	ASSERT_EQ('.', buf);
3174e1199815SMickaël Salaün 
3175e1199815SMickaël Salaün 	/* Checks write access to pipe through /proc/self/fd . */
3176e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC);
3177e1199815SMickaël Salaün 	ASSERT_LE(0, proc_fd);
3178371183faSMickaël Salaün 	ASSERT_EQ(1, write(proc_fd, ".", 1))
3179371183faSMickaël Salaün 	{
3180e1199815SMickaël Salaün 		TH_LOG("Failed to write through /proc/self/fd/%d: %s",
3181e1199815SMickaël Salaün 		       pipe_fds[1], strerror(errno));
3182e1199815SMickaël Salaün 	}
3183e1199815SMickaël Salaün 	ASSERT_EQ(0, close(proc_fd));
3184e1199815SMickaël Salaün 
3185e1199815SMickaël Salaün 	/* Checks read access to pipe through /proc/self/fd . */
3186e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC);
3187e1199815SMickaël Salaün 	ASSERT_LE(0, proc_fd);
3188e1199815SMickaël Salaün 	buf = '\0';
3189371183faSMickaël Salaün 	ASSERT_EQ(1, read(proc_fd, &buf, 1))
3190371183faSMickaël Salaün 	{
3191e1199815SMickaël Salaün 		TH_LOG("Failed to read through /proc/self/fd/%d: %s",
3192e1199815SMickaël Salaün 		       pipe_fds[1], strerror(errno));
3193e1199815SMickaël Salaün 	}
3194e1199815SMickaël Salaün 	ASSERT_EQ(0, close(proc_fd));
3195e1199815SMickaël Salaün 
3196e1199815SMickaël Salaün 	ASSERT_EQ(0, close(pipe_fds[0]));
3197e1199815SMickaël Salaün 	ASSERT_EQ(0, close(pipe_fds[1]));
3198e1199815SMickaël Salaün }
3199e1199815SMickaël Salaün 
3200225351abSGünther Noack /* Invokes truncate(2) and returns its errno or 0. */
3201225351abSGünther Noack static int test_truncate(const char *const path)
3202225351abSGünther Noack {
3203225351abSGünther Noack 	if (truncate(path, 10) < 0)
3204225351abSGünther Noack 		return errno;
3205225351abSGünther Noack 	return 0;
3206225351abSGünther Noack }
3207225351abSGünther Noack 
3208225351abSGünther Noack /*
3209225351abSGünther Noack  * Invokes creat(2) and returns its errno or 0.
3210225351abSGünther Noack  * Closes the opened file descriptor on success.
3211225351abSGünther Noack  */
3212225351abSGünther Noack static int test_creat(const char *const path)
3213225351abSGünther Noack {
3214225351abSGünther Noack 	int fd = creat(path, 0600);
3215225351abSGünther Noack 
3216225351abSGünther Noack 	if (fd < 0)
3217225351abSGünther Noack 		return errno;
3218225351abSGünther Noack 
3219225351abSGünther Noack 	/*
3220225351abSGünther Noack 	 * Mixing error codes from close(2) and creat(2) should not lead to any
3221225351abSGünther Noack 	 * (access type) confusion for this test.
3222225351abSGünther Noack 	 */
3223225351abSGünther Noack 	if (close(fd) < 0)
3224225351abSGünther Noack 		return errno;
3225225351abSGünther Noack 	return 0;
3226225351abSGünther Noack }
3227225351abSGünther Noack 
3228225351abSGünther Noack /*
3229225351abSGünther Noack  * Exercises file truncation when it's not restricted,
3230225351abSGünther Noack  * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed.
3231225351abSGünther Noack  */
3232225351abSGünther Noack TEST_F_FORK(layout1, truncate_unhandled)
3233225351abSGünther Noack {
3234225351abSGünther Noack 	const char *const file_r = file1_s1d1;
3235225351abSGünther Noack 	const char *const file_w = file2_s1d1;
3236225351abSGünther Noack 	const char *const file_none = file1_s1d2;
3237225351abSGünther Noack 	const struct rule rules[] = {
3238225351abSGünther Noack 		{
3239225351abSGünther Noack 			.path = file_r,
3240225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
3241225351abSGünther Noack 		},
3242225351abSGünther Noack 		{
3243225351abSGünther Noack 			.path = file_w,
3244225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3245225351abSGünther Noack 		},
3246225351abSGünther Noack 		/* Implicitly: No rights for file_none. */
3247225351abSGünther Noack 		{},
3248225351abSGünther Noack 	};
3249225351abSGünther Noack 
3250225351abSGünther Noack 	const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
3251225351abSGünther Noack 			      LANDLOCK_ACCESS_FS_WRITE_FILE;
3252225351abSGünther Noack 	int ruleset_fd;
3253225351abSGünther Noack 
3254225351abSGünther Noack 	/* Enable Landlock. */
3255225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled, rules);
3256225351abSGünther Noack 
3257225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3258225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3259225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3260225351abSGünther Noack 
3261225351abSGünther Noack 	/*
3262225351abSGünther Noack 	 * Checks read right: truncate and open with O_TRUNC work, unless the
3263225351abSGünther Noack 	 * file is attempted to be opened for writing.
3264225351abSGünther Noack 	 */
3265225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_r));
3266225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC));
3267225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC));
3268225351abSGünther Noack 	EXPECT_EQ(EACCES, test_creat(file_r));
3269225351abSGünther Noack 
3270225351abSGünther Noack 	/*
3271225351abSGünther Noack 	 * Checks write right: truncate and open with O_TRUNC work, unless the
3272225351abSGünther Noack 	 * file is attempted to be opened for reading.
3273225351abSGünther Noack 	 */
3274225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_w));
3275225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC));
3276225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC));
3277225351abSGünther Noack 	EXPECT_EQ(0, test_creat(file_w));
3278225351abSGünther Noack 
3279225351abSGünther Noack 	/*
3280225351abSGünther Noack 	 * Checks "no rights" case: truncate works but all open attempts fail,
3281225351abSGünther Noack 	 * including creat.
3282225351abSGünther Noack 	 */
3283225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_none));
3284225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3285225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3286225351abSGünther Noack 	EXPECT_EQ(EACCES, test_creat(file_none));
3287225351abSGünther Noack }
3288225351abSGünther Noack 
3289225351abSGünther Noack TEST_F_FORK(layout1, truncate)
3290225351abSGünther Noack {
3291225351abSGünther Noack 	const char *const file_rwt = file1_s1d1;
3292225351abSGünther Noack 	const char *const file_rw = file2_s1d1;
3293225351abSGünther Noack 	const char *const file_rt = file1_s1d2;
3294225351abSGünther Noack 	const char *const file_t = file2_s1d2;
3295225351abSGünther Noack 	const char *const file_none = file1_s1d3;
3296225351abSGünther Noack 	const char *const dir_t = dir_s2d1;
3297225351abSGünther Noack 	const char *const file_in_dir_t = file1_s2d1;
3298225351abSGünther Noack 	const char *const dir_w = dir_s3d1;
3299225351abSGünther Noack 	const char *const file_in_dir_w = file1_s3d1;
3300225351abSGünther Noack 	const struct rule rules[] = {
3301225351abSGünther Noack 		{
3302225351abSGünther Noack 			.path = file_rwt,
3303225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
3304225351abSGünther Noack 				  LANDLOCK_ACCESS_FS_WRITE_FILE |
3305225351abSGünther Noack 				  LANDLOCK_ACCESS_FS_TRUNCATE,
3306225351abSGünther Noack 		},
3307225351abSGünther Noack 		{
3308225351abSGünther Noack 			.path = file_rw,
3309225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
3310225351abSGünther Noack 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
3311225351abSGünther Noack 		},
3312225351abSGünther Noack 		{
3313225351abSGünther Noack 			.path = file_rt,
3314225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
3315225351abSGünther Noack 				  LANDLOCK_ACCESS_FS_TRUNCATE,
3316225351abSGünther Noack 		},
3317225351abSGünther Noack 		{
3318225351abSGünther Noack 			.path = file_t,
3319225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_TRUNCATE,
3320225351abSGünther Noack 		},
3321225351abSGünther Noack 		/* Implicitly: No access rights for file_none. */
3322225351abSGünther Noack 		{
3323225351abSGünther Noack 			.path = dir_t,
3324225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_TRUNCATE,
3325225351abSGünther Noack 		},
3326225351abSGünther Noack 		{
3327225351abSGünther Noack 			.path = dir_w,
3328225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3329225351abSGünther Noack 		},
3330225351abSGünther Noack 		{},
3331225351abSGünther Noack 	};
3332225351abSGünther Noack 	const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
3333225351abSGünther Noack 			      LANDLOCK_ACCESS_FS_WRITE_FILE |
3334225351abSGünther Noack 			      LANDLOCK_ACCESS_FS_TRUNCATE;
3335225351abSGünther Noack 	int ruleset_fd;
3336225351abSGünther Noack 
3337225351abSGünther Noack 	/* Enable Landlock. */
3338225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled, rules);
3339225351abSGünther Noack 
3340225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3341225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3342225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3343225351abSGünther Noack 
3344225351abSGünther Noack 	/* Checks read, write and truncate rights: truncation works. */
3345225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_rwt));
3346225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC));
3347225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC));
3348225351abSGünther Noack 
3349225351abSGünther Noack 	/* Checks read and write rights: no truncate variant works. */
3350225351abSGünther Noack 	EXPECT_EQ(EACCES, test_truncate(file_rw));
3351225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC));
3352225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC));
3353225351abSGünther Noack 
3354225351abSGünther Noack 	/*
3355225351abSGünther Noack 	 * Checks read and truncate rights: truncation works.
3356225351abSGünther Noack 	 *
3357225351abSGünther Noack 	 * Note: Files can get truncated using open() even with O_RDONLY.
3358225351abSGünther Noack 	 */
3359225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_rt));
3360225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC));
3361225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC));
3362225351abSGünther Noack 
3363225351abSGünther Noack 	/* Checks truncate right: truncate works, but can't open file. */
3364225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_t));
3365225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC));
3366225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC));
3367225351abSGünther Noack 
3368225351abSGünther Noack 	/* Checks "no rights" case: No form of truncation works. */
3369225351abSGünther Noack 	EXPECT_EQ(EACCES, test_truncate(file_none));
3370225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3371225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3372225351abSGünther Noack 
3373225351abSGünther Noack 	/*
3374225351abSGünther Noack 	 * Checks truncate right on directory: truncate works on contained
3375225351abSGünther Noack 	 * files.
3376225351abSGünther Noack 	 */
3377225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_in_dir_t));
3378225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC));
3379225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC));
3380225351abSGünther Noack 
3381225351abSGünther Noack 	/*
3382225351abSGünther Noack 	 * Checks creat in dir_w: This requires the truncate right when
3383225351abSGünther Noack 	 * overwriting an existing file, but does not require it when the file
3384225351abSGünther Noack 	 * is new.
3385225351abSGünther Noack 	 */
3386225351abSGünther Noack 	EXPECT_EQ(EACCES, test_creat(file_in_dir_w));
3387225351abSGünther Noack 
3388225351abSGünther Noack 	ASSERT_EQ(0, unlink(file_in_dir_w));
3389225351abSGünther Noack 	EXPECT_EQ(0, test_creat(file_in_dir_w));
3390225351abSGünther Noack }
3391225351abSGünther Noack 
3392225351abSGünther Noack /* Invokes ftruncate(2) and returns its errno or 0. */
3393225351abSGünther Noack static int test_ftruncate(int fd)
3394225351abSGünther Noack {
3395225351abSGünther Noack 	if (ftruncate(fd, 10) < 0)
3396225351abSGünther Noack 		return errno;
3397225351abSGünther Noack 	return 0;
3398225351abSGünther Noack }
3399225351abSGünther Noack 
3400225351abSGünther Noack TEST_F_FORK(layout1, ftruncate)
3401225351abSGünther Noack {
3402225351abSGünther Noack 	/*
3403225351abSGünther Noack 	 * This test opens a new file descriptor at different stages of
3404225351abSGünther Noack 	 * Landlock restriction:
3405225351abSGünther Noack 	 *
3406225351abSGünther Noack 	 * without restriction:                    ftruncate works
3407225351abSGünther Noack 	 * something else but truncate restricted: ftruncate works
3408225351abSGünther Noack 	 * truncate restricted and permitted:      ftruncate works
3409225351abSGünther Noack 	 * truncate restricted and not permitted:  ftruncate fails
3410225351abSGünther Noack 	 *
3411225351abSGünther Noack 	 * Whether this works or not is expected to depend on the time when the
3412225351abSGünther Noack 	 * FD was opened, not to depend on the time when ftruncate() was
3413225351abSGünther Noack 	 * called.
3414225351abSGünther Noack 	 */
3415225351abSGünther Noack 	const char *const path = file1_s1d1;
3416225351abSGünther Noack 	const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE |
3417225351abSGünther Noack 			       LANDLOCK_ACCESS_FS_WRITE_FILE;
3418225351abSGünther Noack 	const struct rule layer1[] = {
3419225351abSGünther Noack 		{
3420225351abSGünther Noack 			.path = path,
3421225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3422225351abSGünther Noack 		},
3423225351abSGünther Noack 		{},
3424225351abSGünther Noack 	};
3425225351abSGünther Noack 	const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE;
3426225351abSGünther Noack 	const struct rule layer2[] = {
3427225351abSGünther Noack 		{
3428225351abSGünther Noack 			.path = path,
3429225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_TRUNCATE,
3430225351abSGünther Noack 		},
3431225351abSGünther Noack 		{},
3432225351abSGünther Noack 	};
3433225351abSGünther Noack 	const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE |
3434225351abSGünther Noack 			       LANDLOCK_ACCESS_FS_WRITE_FILE;
3435225351abSGünther Noack 	const struct rule layer3[] = {
3436225351abSGünther Noack 		{
3437225351abSGünther Noack 			.path = path,
3438225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3439225351abSGünther Noack 		},
3440225351abSGünther Noack 		{},
3441225351abSGünther Noack 	};
3442225351abSGünther Noack 	int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd;
3443225351abSGünther Noack 
3444225351abSGünther Noack 	fd_layer0 = open(path, O_WRONLY);
3445225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer0));
3446225351abSGünther Noack 
3447225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled1, layer1);
3448225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3449225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3450225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3451225351abSGünther Noack 
3452225351abSGünther Noack 	fd_layer1 = open(path, O_WRONLY);
3453225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer0));
3454225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer1));
3455225351abSGünther Noack 
3456225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled2, layer2);
3457225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3458225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3459225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3460225351abSGünther Noack 
3461225351abSGünther Noack 	fd_layer2 = open(path, O_WRONLY);
3462225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer0));
3463225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer1));
3464225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer2));
3465225351abSGünther Noack 
3466225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled3, layer3);
3467225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3468225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3469225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3470225351abSGünther Noack 
3471225351abSGünther Noack 	fd_layer3 = open(path, O_WRONLY);
3472225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer0));
3473225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer1));
3474225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer2));
3475225351abSGünther Noack 	EXPECT_EQ(EACCES, test_ftruncate(fd_layer3));
3476225351abSGünther Noack 
3477225351abSGünther Noack 	ASSERT_EQ(0, close(fd_layer0));
3478225351abSGünther Noack 	ASSERT_EQ(0, close(fd_layer1));
3479225351abSGünther Noack 	ASSERT_EQ(0, close(fd_layer2));
3480225351abSGünther Noack 	ASSERT_EQ(0, close(fd_layer3));
3481225351abSGünther Noack }
3482225351abSGünther Noack 
34834598d9abSMickaël Salaün /* clang-format off */
348441729af2SGünther Noack FIXTURE(ftruncate) {};
348541729af2SGünther Noack /* clang-format on */
348641729af2SGünther Noack 
348741729af2SGünther Noack FIXTURE_SETUP(ftruncate)
348841729af2SGünther Noack {
348941729af2SGünther Noack 	prepare_layout(_metadata);
349041729af2SGünther Noack 	create_file(_metadata, file1_s1d1);
349141729af2SGünther Noack }
349241729af2SGünther Noack 
349341729af2SGünther Noack FIXTURE_TEARDOWN(ftruncate)
349441729af2SGünther Noack {
349541729af2SGünther Noack 	EXPECT_EQ(0, remove_path(file1_s1d1));
349641729af2SGünther Noack 	cleanup_layout(_metadata);
349741729af2SGünther Noack }
349841729af2SGünther Noack 
349941729af2SGünther Noack FIXTURE_VARIANT(ftruncate)
350041729af2SGünther Noack {
350141729af2SGünther Noack 	const __u64 handled;
350241729af2SGünther Noack 	const __u64 permitted;
350341729af2SGünther Noack 	const int expected_open_result;
350441729af2SGünther Noack 	const int expected_ftruncate_result;
350541729af2SGünther Noack };
350641729af2SGünther Noack 
350741729af2SGünther Noack /* clang-format off */
350841729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, w_w) {
350941729af2SGünther Noack 	/* clang-format on */
351041729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE,
351141729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_WRITE_FILE,
351241729af2SGünther Noack 	.expected_open_result = 0,
351341729af2SGünther Noack 	.expected_ftruncate_result = 0,
351441729af2SGünther Noack };
351541729af2SGünther Noack 
351641729af2SGünther Noack /* clang-format off */
351741729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, t_t) {
351841729af2SGünther Noack 	/* clang-format on */
351941729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_TRUNCATE,
352041729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_TRUNCATE,
352141729af2SGünther Noack 	.expected_open_result = 0,
352241729af2SGünther Noack 	.expected_ftruncate_result = 0,
352341729af2SGünther Noack };
352441729af2SGünther Noack 
352541729af2SGünther Noack /* clang-format off */
352641729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_w) {
352741729af2SGünther Noack 	/* clang-format on */
352841729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
352941729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_WRITE_FILE,
353041729af2SGünther Noack 	.expected_open_result = 0,
353141729af2SGünther Noack 	.expected_ftruncate_result = EACCES,
353241729af2SGünther Noack };
353341729af2SGünther Noack 
353441729af2SGünther Noack /* clang-format off */
353541729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_wt) {
353641729af2SGünther Noack 	/* clang-format on */
353741729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
353841729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_WRITE_FILE |
353941729af2SGünther Noack 		     LANDLOCK_ACCESS_FS_TRUNCATE,
354041729af2SGünther Noack 	.expected_open_result = 0,
354141729af2SGünther Noack 	.expected_ftruncate_result = 0,
354241729af2SGünther Noack };
354341729af2SGünther Noack 
354441729af2SGünther Noack /* clang-format off */
354541729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_t) {
354641729af2SGünther Noack 	/* clang-format on */
354741729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
354841729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_TRUNCATE,
354941729af2SGünther Noack 	.expected_open_result = EACCES,
355041729af2SGünther Noack };
355141729af2SGünther Noack 
355241729af2SGünther Noack TEST_F_FORK(ftruncate, open_and_ftruncate)
355341729af2SGünther Noack {
355441729af2SGünther Noack 	const char *const path = file1_s1d1;
355541729af2SGünther Noack 	const struct rule rules[] = {
355641729af2SGünther Noack 		{
355741729af2SGünther Noack 			.path = path,
355841729af2SGünther Noack 			.access = variant->permitted,
355941729af2SGünther Noack 		},
356041729af2SGünther Noack 		{},
356141729af2SGünther Noack 	};
356241729af2SGünther Noack 	int fd, ruleset_fd;
356341729af2SGünther Noack 
356441729af2SGünther Noack 	/* Enable Landlock. */
356541729af2SGünther Noack 	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
356641729af2SGünther Noack 	ASSERT_LE(0, ruleset_fd);
356741729af2SGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
356841729af2SGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
356941729af2SGünther Noack 
357041729af2SGünther Noack 	fd = open(path, O_WRONLY);
357141729af2SGünther Noack 	EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
357241729af2SGünther Noack 	if (fd >= 0) {
357341729af2SGünther Noack 		EXPECT_EQ(variant->expected_ftruncate_result,
357441729af2SGünther Noack 			  test_ftruncate(fd));
357541729af2SGünther Noack 		ASSERT_EQ(0, close(fd));
357641729af2SGünther Noack 	}
357741729af2SGünther Noack }
357841729af2SGünther Noack 
3579a1a202a5SGünther Noack TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)
3580a1a202a5SGünther Noack {
3581a1a202a5SGünther Noack 	int child, fd, status;
3582a1a202a5SGünther Noack 	int socket_fds[2];
3583a1a202a5SGünther Noack 
3584a1a202a5SGünther Noack 	ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
3585a1a202a5SGünther Noack 				socket_fds));
3586a1a202a5SGünther Noack 
3587a1a202a5SGünther Noack 	child = fork();
3588a1a202a5SGünther Noack 	ASSERT_LE(0, child);
3589a1a202a5SGünther Noack 	if (child == 0) {
3590a1a202a5SGünther Noack 		/*
3591a1a202a5SGünther Noack 		 * Enables Landlock in the child process, open a file descriptor
3592a1a202a5SGünther Noack 		 * where truncation is forbidden and send it to the
3593a1a202a5SGünther Noack 		 * non-landlocked parent process.
3594a1a202a5SGünther Noack 		 */
3595a1a202a5SGünther Noack 		const char *const path = file1_s1d1;
3596a1a202a5SGünther Noack 		const struct rule rules[] = {
3597a1a202a5SGünther Noack 			{
3598a1a202a5SGünther Noack 				.path = path,
3599a1a202a5SGünther Noack 				.access = variant->permitted,
3600a1a202a5SGünther Noack 			},
3601a1a202a5SGünther Noack 			{},
3602a1a202a5SGünther Noack 		};
3603a1a202a5SGünther Noack 		int fd, ruleset_fd;
3604a1a202a5SGünther Noack 
3605a1a202a5SGünther Noack 		ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
3606a1a202a5SGünther Noack 		ASSERT_LE(0, ruleset_fd);
3607a1a202a5SGünther Noack 		enforce_ruleset(_metadata, ruleset_fd);
3608a1a202a5SGünther Noack 		ASSERT_EQ(0, close(ruleset_fd));
3609a1a202a5SGünther Noack 
3610a1a202a5SGünther Noack 		fd = open(path, O_WRONLY);
3611a1a202a5SGünther Noack 		ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
3612a1a202a5SGünther Noack 
3613a1a202a5SGünther Noack 		if (fd >= 0) {
3614a1a202a5SGünther Noack 			ASSERT_EQ(0, send_fd(socket_fds[0], fd));
3615a1a202a5SGünther Noack 			ASSERT_EQ(0, close(fd));
3616a1a202a5SGünther Noack 		}
3617a1a202a5SGünther Noack 
3618a1a202a5SGünther Noack 		ASSERT_EQ(0, close(socket_fds[0]));
3619a1a202a5SGünther Noack 
3620a1a202a5SGünther Noack 		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
3621a1a202a5SGünther Noack 		return;
3622a1a202a5SGünther Noack 	}
3623a1a202a5SGünther Noack 
3624a1a202a5SGünther Noack 	if (variant->expected_open_result == 0) {
3625a1a202a5SGünther Noack 		fd = recv_fd(socket_fds[1]);
3626a1a202a5SGünther Noack 		ASSERT_LE(0, fd);
3627a1a202a5SGünther Noack 
3628a1a202a5SGünther Noack 		EXPECT_EQ(variant->expected_ftruncate_result,
3629a1a202a5SGünther Noack 			  test_ftruncate(fd));
3630a1a202a5SGünther Noack 		ASSERT_EQ(0, close(fd));
3631a1a202a5SGünther Noack 	}
3632a1a202a5SGünther Noack 
3633a1a202a5SGünther Noack 	ASSERT_EQ(child, waitpid(child, &status, 0));
3634a1a202a5SGünther Noack 	ASSERT_EQ(1, WIFEXITED(status));
3635a1a202a5SGünther Noack 	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
3636a1a202a5SGünther Noack 
3637a1a202a5SGünther Noack 	ASSERT_EQ(0, close(socket_fds[0]));
3638a1a202a5SGünther Noack 	ASSERT_EQ(0, close(socket_fds[1]));
3639a1a202a5SGünther Noack }
3640a1a202a5SGünther Noack 
36410d8c658bSGünther Noack TEST(memfd_ftruncate)
36420d8c658bSGünther Noack {
36430d8c658bSGünther Noack 	int fd;
36440d8c658bSGünther Noack 
36450d8c658bSGünther Noack 	fd = memfd_create("name", MFD_CLOEXEC);
36460d8c658bSGünther Noack 	ASSERT_LE(0, fd);
36470d8c658bSGünther Noack 
36480d8c658bSGünther Noack 	/*
36490d8c658bSGünther Noack 	 * Checks that ftruncate is permitted on file descriptors that are
36500d8c658bSGünther Noack 	 * created in ways other than open(2).
36510d8c658bSGünther Noack 	 */
36520d8c658bSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd));
36530d8c658bSGünther Noack 
36540d8c658bSGünther Noack 	ASSERT_EQ(0, close(fd));
36550d8c658bSGünther Noack }
36560d8c658bSGünther Noack 
365741729af2SGünther Noack /* clang-format off */
36584598d9abSMickaël Salaün FIXTURE(layout1_bind) {};
36594598d9abSMickaël Salaün /* clang-format on */
3660e1199815SMickaël Salaün 
3661e1199815SMickaël Salaün FIXTURE_SETUP(layout1_bind)
3662e1199815SMickaël Salaün {
3663e1199815SMickaël Salaün 	prepare_layout(_metadata);
3664e1199815SMickaël Salaün 
3665e1199815SMickaël Salaün 	create_layout1(_metadata);
3666e1199815SMickaël Salaün 
3667e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
3668e1199815SMickaël Salaün 	ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
3669e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
3670e1199815SMickaël Salaün }
3671e1199815SMickaël Salaün 
3672e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1_bind)
3673e1199815SMickaël Salaün {
3674e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
3675e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(dir_s2d2));
3676e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
3677e1199815SMickaël Salaün 
3678e1199815SMickaël Salaün 	remove_layout1(_metadata);
3679e1199815SMickaël Salaün 
3680e1199815SMickaël Salaün 	cleanup_layout(_metadata);
3681e1199815SMickaël Salaün }
3682e1199815SMickaël Salaün 
3683e1199815SMickaël Salaün static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";
3684e1199815SMickaël Salaün static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";
3685e1199815SMickaël Salaün 
3686e1199815SMickaël Salaün /*
3687e1199815SMickaël Salaün  * layout1_bind hierarchy:
3688e1199815SMickaël Salaün  *
3689e1199815SMickaël Salaün  * tmp
3690e1199815SMickaël Salaün  * ├── s1d1
3691e1199815SMickaël Salaün  * │   ├── f1
3692e1199815SMickaël Salaün  * │   ├── f2
3693e1199815SMickaël Salaün  * │   └── s1d2
3694e1199815SMickaël Salaün  * │       ├── f1
3695e1199815SMickaël Salaün  * │       ├── f2
3696e1199815SMickaël Salaün  * │       └── s1d3
3697e1199815SMickaël Salaün  * │           ├── f1
3698e1199815SMickaël Salaün  * │           └── f2
3699e1199815SMickaël Salaün  * ├── s2d1
3700e1199815SMickaël Salaün  * │   ├── f1
3701e1199815SMickaël Salaün  * │   └── s2d2
3702e1199815SMickaël Salaün  * │       ├── f1
3703e1199815SMickaël Salaün  * │       ├── f2
3704e1199815SMickaël Salaün  * │       └── s1d3
3705e1199815SMickaël Salaün  * │           ├── f1
3706e1199815SMickaël Salaün  * │           └── f2
3707e1199815SMickaël Salaün  * └── s3d1
3708e1199815SMickaël Salaün  *     └── s3d2
3709e1199815SMickaël Salaün  *         └── s3d3
3710e1199815SMickaël Salaün  */
3711e1199815SMickaël Salaün 
3712e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, no_restriction)
3713e1199815SMickaël Salaün {
3714e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
3715e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
3716e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
3717e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3718e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
3719e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
3720e1199815SMickaël Salaün 
3721e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
3722e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
3723e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
3724e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
3725e1199815SMickaël Salaün 	ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY));
3726e1199815SMickaël Salaün 	ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY));
3727e1199815SMickaël Salaün 
3728e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY));
3729e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
3730e1199815SMickaël Salaün 
3731e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
3732e1199815SMickaël Salaün }
3733e1199815SMickaël Salaün 
3734e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, same_content_same_file)
3735e1199815SMickaël Salaün {
3736e1199815SMickaël Salaün 	/*
3737e1199815SMickaël Salaün 	 * Sets access right on parent directories of both source and
3738e1199815SMickaël Salaün 	 * destination mount points.
3739e1199815SMickaël Salaün 	 */
3740e1199815SMickaël Salaün 	const struct rule layer1_parent[] = {
3741e1199815SMickaël Salaün 		{
3742e1199815SMickaël Salaün 			.path = dir_s1d1,
3743e1199815SMickaël Salaün 			.access = ACCESS_RO,
3744e1199815SMickaël Salaün 		},
3745e1199815SMickaël Salaün 		{
3746e1199815SMickaël Salaün 			.path = dir_s2d1,
3747e1199815SMickaël Salaün 			.access = ACCESS_RW,
3748e1199815SMickaël Salaün 		},
3749135464f9SMickaël Salaün 		{},
3750e1199815SMickaël Salaün 	};
3751e1199815SMickaël Salaün 	/*
3752e1199815SMickaël Salaün 	 * Sets access rights on the same bind-mounted directories.  The result
3753e1199815SMickaël Salaün 	 * should be ACCESS_RW for both directories, but not both hierarchies
3754e1199815SMickaël Salaün 	 * because of the first layer.
3755e1199815SMickaël Salaün 	 */
3756e1199815SMickaël Salaün 	const struct rule layer2_mount_point[] = {
3757e1199815SMickaël Salaün 		{
3758e1199815SMickaël Salaün 			.path = dir_s1d2,
3759e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
3760e1199815SMickaël Salaün 		},
3761e1199815SMickaël Salaün 		{
3762e1199815SMickaël Salaün 			.path = dir_s2d2,
3763e1199815SMickaël Salaün 			.access = ACCESS_RW,
3764e1199815SMickaël Salaün 		},
3765135464f9SMickaël Salaün 		{},
3766e1199815SMickaël Salaün 	};
3767e1199815SMickaël Salaün 	/* Only allow read-access to the s1d3 hierarchies. */
3768e1199815SMickaël Salaün 	const struct rule layer3_source[] = {
3769e1199815SMickaël Salaün 		{
3770e1199815SMickaël Salaün 			.path = dir_s1d3,
3771e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
3772e1199815SMickaël Salaün 		},
3773135464f9SMickaël Salaün 		{},
3774e1199815SMickaël Salaün 	};
3775e1199815SMickaël Salaün 	/* Removes all access rights. */
3776e1199815SMickaël Salaün 	const struct rule layer4_destination[] = {
3777e1199815SMickaël Salaün 		{
3778e1199815SMickaël Salaün 			.path = bind_file1_s1d3,
3779e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3780e1199815SMickaël Salaün 		},
3781135464f9SMickaël Salaün 		{},
3782e1199815SMickaël Salaün 	};
3783e1199815SMickaël Salaün 	int ruleset_fd;
3784e1199815SMickaël Salaün 
3785e1199815SMickaël Salaün 	/* Sets rules for the parent directories. */
3786e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent);
3787e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3788e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3789e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3790e1199815SMickaël Salaün 
3791e1199815SMickaël Salaün 	/* Checks source hierarchy. */
3792e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
3793e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
3794e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
3795e1199815SMickaël Salaün 
3796e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3797e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
3798e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
3799e1199815SMickaël Salaün 
3800e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
3801e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR));
3802e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
3803e1199815SMickaël Salaün 
3804e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
3805e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
3806e1199815SMickaël Salaün 
3807e1199815SMickaël Salaün 	/* Sets rules for the mount points. */
3808e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point);
3809e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3810e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3811e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3812e1199815SMickaël Salaün 
3813e1199815SMickaël Salaün 	/* Checks source hierarchy. */
3814e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
3815e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
3816e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
3817e1199815SMickaël Salaün 
3818e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3819e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
3820e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
3821e1199815SMickaël Salaün 
3822e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
3823e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY));
3824e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY));
3825e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
3826e1199815SMickaël Salaün 
3827e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
3828e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
3829e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
3830e1199815SMickaël Salaün 
3831e1199815SMickaël Salaün 	/* Sets a (shared) rule only on the source. */
3832e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source);
3833e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3834e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3835e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3836e1199815SMickaël Salaün 
3837e1199815SMickaël Salaün 	/* Checks source hierarchy. */
3838e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
3839e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
3840e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
3841e1199815SMickaël Salaün 
3842e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
3843e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
3844e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
3845e1199815SMickaël Salaün 
3846e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
3847e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY));
3848e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY));
3849e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
3850e1199815SMickaël Salaün 
3851e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
3852e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
3853e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
3854e1199815SMickaël Salaün 
3855e1199815SMickaël Salaün 	/* Sets a (shared) rule only on the destination. */
3856e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination);
3857e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3858e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3859e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3860e1199815SMickaël Salaün 
3861e1199815SMickaël Salaün 	/* Checks source hierarchy. */
3862e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
3863e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
3864e1199815SMickaël Salaün 
3865e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
3866e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY));
3867e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
3868e1199815SMickaël Salaün }
3869e1199815SMickaël Salaün 
3870f4056b92SMickaël Salaün TEST_F_FORK(layout1_bind, reparent_cross_mount)
3871f4056b92SMickaël Salaün {
3872f4056b92SMickaël Salaün 	const struct rule layer1[] = {
3873f4056b92SMickaël Salaün 		{
3874f4056b92SMickaël Salaün 			/* dir_s2d1 is beneath the dir_s2d2 mount point. */
3875f4056b92SMickaël Salaün 			.path = dir_s2d1,
3876f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
3877f4056b92SMickaël Salaün 		},
3878f4056b92SMickaël Salaün 		{
3879f4056b92SMickaël Salaün 			.path = bind_dir_s1d3,
3880f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
3881f4056b92SMickaël Salaün 		},
3882f4056b92SMickaël Salaün 		{},
3883f4056b92SMickaël Salaün 	};
3884f4056b92SMickaël Salaün 	int ruleset_fd = create_ruleset(
3885f4056b92SMickaël Salaün 		_metadata,
3886f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1);
3887f4056b92SMickaël Salaün 
3888f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3889f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3890f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3891f4056b92SMickaël Salaün 
3892f4056b92SMickaël Salaün 	/* Checks basic denied move. */
3893f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2));
3894f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
3895f4056b92SMickaël Salaün 
3896f4056b92SMickaël Salaün 	/* Checks real cross-mount move (Landlock is not involved). */
3897f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2));
3898f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
3899f4056b92SMickaël Salaün 
3900f4056b92SMickaël Salaün 	/* Checks move that will give more accesses. */
3901f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3));
3902f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
3903f4056b92SMickaël Salaün 
3904f4056b92SMickaël Salaün 	/* Checks legitimate downgrade move. */
3905f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2));
3906f4056b92SMickaël Salaün }
3907f4056b92SMickaël Salaün 
3908e1199815SMickaël Salaün #define LOWER_BASE TMP_DIR "/lower"
3909e1199815SMickaël Salaün #define LOWER_DATA LOWER_BASE "/data"
3910e1199815SMickaël Salaün static const char lower_fl1[] = LOWER_DATA "/fl1";
3911e1199815SMickaël Salaün static const char lower_dl1[] = LOWER_DATA "/dl1";
3912e1199815SMickaël Salaün static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";
3913e1199815SMickaël Salaün static const char lower_fo1[] = LOWER_DATA "/fo1";
3914e1199815SMickaël Salaün static const char lower_do1[] = LOWER_DATA "/do1";
3915e1199815SMickaël Salaün static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";
3916e1199815SMickaël Salaün static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";
3917e1199815SMickaël Salaün 
3918e1199815SMickaël Salaün static const char (*lower_base_files[])[] = {
3919e1199815SMickaël Salaün 	&lower_fl1,
3920e1199815SMickaël Salaün 	&lower_fo1,
3921135464f9SMickaël Salaün 	NULL,
3922e1199815SMickaël Salaün };
3923e1199815SMickaël Salaün static const char (*lower_base_directories[])[] = {
3924e1199815SMickaël Salaün 	&lower_dl1,
3925e1199815SMickaël Salaün 	&lower_do1,
3926135464f9SMickaël Salaün 	NULL,
3927e1199815SMickaël Salaün };
3928e1199815SMickaël Salaün static const char (*lower_sub_files[])[] = {
3929e1199815SMickaël Salaün 	&lower_dl1_fl2,
3930e1199815SMickaël Salaün 	&lower_do1_fo2,
3931e1199815SMickaël Salaün 	&lower_do1_fl3,
3932135464f9SMickaël Salaün 	NULL,
3933e1199815SMickaël Salaün };
3934e1199815SMickaël Salaün 
3935e1199815SMickaël Salaün #define UPPER_BASE TMP_DIR "/upper"
3936e1199815SMickaël Salaün #define UPPER_DATA UPPER_BASE "/data"
3937e1199815SMickaël Salaün #define UPPER_WORK UPPER_BASE "/work"
3938e1199815SMickaël Salaün static const char upper_fu1[] = UPPER_DATA "/fu1";
3939e1199815SMickaël Salaün static const char upper_du1[] = UPPER_DATA "/du1";
3940e1199815SMickaël Salaün static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";
3941e1199815SMickaël Salaün static const char upper_fo1[] = UPPER_DATA "/fo1";
3942e1199815SMickaël Salaün static const char upper_do1[] = UPPER_DATA "/do1";
3943e1199815SMickaël Salaün static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";
3944e1199815SMickaël Salaün static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";
3945e1199815SMickaël Salaün 
3946e1199815SMickaël Salaün static const char (*upper_base_files[])[] = {
3947e1199815SMickaël Salaün 	&upper_fu1,
3948e1199815SMickaël Salaün 	&upper_fo1,
3949135464f9SMickaël Salaün 	NULL,
3950e1199815SMickaël Salaün };
3951e1199815SMickaël Salaün static const char (*upper_base_directories[])[] = {
3952e1199815SMickaël Salaün 	&upper_du1,
3953e1199815SMickaël Salaün 	&upper_do1,
3954135464f9SMickaël Salaün 	NULL,
3955e1199815SMickaël Salaün };
3956e1199815SMickaël Salaün static const char (*upper_sub_files[])[] = {
3957e1199815SMickaël Salaün 	&upper_du1_fu2,
3958e1199815SMickaël Salaün 	&upper_do1_fo2,
3959e1199815SMickaël Salaün 	&upper_do1_fu3,
3960135464f9SMickaël Salaün 	NULL,
3961e1199815SMickaël Salaün };
3962e1199815SMickaël Salaün 
3963e1199815SMickaël Salaün #define MERGE_BASE TMP_DIR "/merge"
3964e1199815SMickaël Salaün #define MERGE_DATA MERGE_BASE "/data"
3965e1199815SMickaël Salaün static const char merge_fl1[] = MERGE_DATA "/fl1";
3966e1199815SMickaël Salaün static const char merge_dl1[] = MERGE_DATA "/dl1";
3967e1199815SMickaël Salaün static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";
3968e1199815SMickaël Salaün static const char merge_fu1[] = MERGE_DATA "/fu1";
3969e1199815SMickaël Salaün static const char merge_du1[] = MERGE_DATA "/du1";
3970e1199815SMickaël Salaün static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";
3971e1199815SMickaël Salaün static const char merge_fo1[] = MERGE_DATA "/fo1";
3972e1199815SMickaël Salaün static const char merge_do1[] = MERGE_DATA "/do1";
3973e1199815SMickaël Salaün static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";
3974e1199815SMickaël Salaün static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";
3975e1199815SMickaël Salaün static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";
3976e1199815SMickaël Salaün 
3977e1199815SMickaël Salaün static const char (*merge_base_files[])[] = {
3978e1199815SMickaël Salaün 	&merge_fl1,
3979e1199815SMickaël Salaün 	&merge_fu1,
3980e1199815SMickaël Salaün 	&merge_fo1,
3981135464f9SMickaël Salaün 	NULL,
3982e1199815SMickaël Salaün };
3983e1199815SMickaël Salaün static const char (*merge_base_directories[])[] = {
3984e1199815SMickaël Salaün 	&merge_dl1,
3985e1199815SMickaël Salaün 	&merge_du1,
3986e1199815SMickaël Salaün 	&merge_do1,
3987135464f9SMickaël Salaün 	NULL,
3988e1199815SMickaël Salaün };
3989e1199815SMickaël Salaün static const char (*merge_sub_files[])[] = {
3990371183faSMickaël Salaün 	&merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2,
3991371183faSMickaël Salaün 	&merge_do1_fl3, &merge_do1_fu3, NULL,
3992e1199815SMickaël Salaün };
3993e1199815SMickaël Salaün 
3994e1199815SMickaël Salaün /*
3995e1199815SMickaël Salaün  * layout2_overlay hierarchy:
3996e1199815SMickaël Salaün  *
3997e1199815SMickaël Salaün  * tmp
3998e1199815SMickaël Salaün  * ├── lower
3999e1199815SMickaël Salaün  * │   └── data
4000e1199815SMickaël Salaün  * │       ├── dl1
4001e1199815SMickaël Salaün  * │       │   └── fl2
4002e1199815SMickaël Salaün  * │       ├── do1
4003e1199815SMickaël Salaün  * │       │   ├── fl3
4004e1199815SMickaël Salaün  * │       │   └── fo2
4005e1199815SMickaël Salaün  * │       ├── fl1
4006e1199815SMickaël Salaün  * │       └── fo1
4007e1199815SMickaël Salaün  * ├── merge
4008e1199815SMickaël Salaün  * │   └── data
4009e1199815SMickaël Salaün  * │       ├── dl1
4010e1199815SMickaël Salaün  * │       │   └── fl2
4011e1199815SMickaël Salaün  * │       ├── do1
4012e1199815SMickaël Salaün  * │       │   ├── fl3
4013e1199815SMickaël Salaün  * │       │   ├── fo2
4014e1199815SMickaël Salaün  * │       │   └── fu3
4015e1199815SMickaël Salaün  * │       ├── du1
4016e1199815SMickaël Salaün  * │       │   └── fu2
4017e1199815SMickaël Salaün  * │       ├── fl1
4018e1199815SMickaël Salaün  * │       ├── fo1
4019e1199815SMickaël Salaün  * │       └── fu1
4020e1199815SMickaël Salaün  * └── upper
4021e1199815SMickaël Salaün  *     ├── data
4022e1199815SMickaël Salaün  *     │   ├── do1
4023e1199815SMickaël Salaün  *     │   │   ├── fo2
4024e1199815SMickaël Salaün  *     │   │   └── fu3
4025e1199815SMickaël Salaün  *     │   ├── du1
4026e1199815SMickaël Salaün  *     │   │   └── fu2
4027e1199815SMickaël Salaün  *     │   ├── fo1
4028e1199815SMickaël Salaün  *     │   └── fu1
4029e1199815SMickaël Salaün  *     └── work
4030e1199815SMickaël Salaün  *         └── work
4031e1199815SMickaël Salaün  */
4032e1199815SMickaël Salaün 
40334598d9abSMickaël Salaün /* clang-format off */
40344598d9abSMickaël Salaün FIXTURE(layout2_overlay) {};
40354598d9abSMickaël Salaün /* clang-format on */
4036e1199815SMickaël Salaün 
4037e1199815SMickaël Salaün FIXTURE_SETUP(layout2_overlay)
4038e1199815SMickaël Salaün {
4039*366617a6SJeff Xu 	if (!supports_overlayfs())
4040*366617a6SJeff Xu 		SKIP(return, "overlayfs is not supported");
4041*366617a6SJeff Xu 
4042e1199815SMickaël Salaün 	prepare_layout(_metadata);
4043e1199815SMickaël Salaün 
4044e1199815SMickaël Salaün 	create_directory(_metadata, LOWER_BASE);
4045e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4046e1199815SMickaël Salaün 	/* Creates tmpfs mount points to get deterministic overlayfs. */
4047e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("tmp", LOWER_BASE, "tmpfs", 0, "size=4m,mode=700"));
4048e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4049e1199815SMickaël Salaün 	create_file(_metadata, lower_fl1);
4050e1199815SMickaël Salaün 	create_file(_metadata, lower_dl1_fl2);
4051e1199815SMickaël Salaün 	create_file(_metadata, lower_fo1);
4052e1199815SMickaël Salaün 	create_file(_metadata, lower_do1_fo2);
4053e1199815SMickaël Salaün 	create_file(_metadata, lower_do1_fl3);
4054e1199815SMickaël Salaün 
4055e1199815SMickaël Salaün 	create_directory(_metadata, UPPER_BASE);
4056e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4057e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("tmp", UPPER_BASE, "tmpfs", 0, "size=4m,mode=700"));
4058e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4059e1199815SMickaël Salaün 	create_file(_metadata, upper_fu1);
4060e1199815SMickaël Salaün 	create_file(_metadata, upper_du1_fu2);
4061e1199815SMickaël Salaün 	create_file(_metadata, upper_fo1);
4062e1199815SMickaël Salaün 	create_file(_metadata, upper_do1_fo2);
4063e1199815SMickaël Salaün 	create_file(_metadata, upper_do1_fu3);
4064e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(UPPER_WORK, 0700));
4065e1199815SMickaël Salaün 
4066e1199815SMickaël Salaün 	create_directory(_metadata, MERGE_DATA);
4067e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4068e1199815SMickaël Salaün 	set_cap(_metadata, CAP_DAC_OVERRIDE);
4069e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0,
4070371183faSMickaël Salaün 			   "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA
4071e1199815SMickaël Salaün 			   ",workdir=" UPPER_WORK));
4072e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_DAC_OVERRIDE);
4073e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4074e1199815SMickaël Salaün }
4075e1199815SMickaël Salaün 
4076e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout2_overlay)
4077e1199815SMickaël Salaün {
4078*366617a6SJeff Xu 	if (!supports_overlayfs())
4079*366617a6SJeff Xu 		SKIP(return, "overlayfs is not supported");
4080*366617a6SJeff Xu 
4081e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_do1_fl3));
4082e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_dl1_fl2));
4083e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_fl1));
4084e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_do1_fo2));
4085e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_fo1));
4086e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4087e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(LOWER_BASE));
4088e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4089e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(LOWER_BASE));
4090e1199815SMickaël Salaün 
4091e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_do1_fu3));
4092e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_du1_fu2));
4093e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_fu1));
4094e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_do1_fo2));
4095e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_fo1));
4096e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(UPPER_WORK "/work"));
4097e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4098e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(UPPER_BASE));
4099e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4100e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(UPPER_BASE));
4101e1199815SMickaël Salaün 
4102e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4103e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(MERGE_DATA));
4104e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4105e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(MERGE_DATA));
4106e1199815SMickaël Salaün 
4107e1199815SMickaël Salaün 	cleanup_layout(_metadata);
4108e1199815SMickaël Salaün }
4109e1199815SMickaël Salaün 
4110e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, no_restriction)
4111e1199815SMickaël Salaün {
4112*366617a6SJeff Xu 	if (!supports_overlayfs())
4113*366617a6SJeff Xu 		SKIP(return, "overlayfs is not supported");
4114*366617a6SJeff Xu 
4115e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
4116e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
4117e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));
4118e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY));
4119e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_do1, O_RDONLY));
4120e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY));
4121e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY));
4122e1199815SMickaël Salaün 
4123e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY));
4124e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_du1, O_RDONLY));
4125e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY));
4126e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY));
4127e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_do1, O_RDONLY));
4128e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY));
4129e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY));
4130e1199815SMickaël Salaün 
4131e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY));
4132e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY));
4133e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY));
4134e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY));
4135e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_du1, O_RDONLY));
4136e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY));
4137e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY));
4138e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1, O_RDONLY));
4139e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY));
4140e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY));
4141e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY));
4142e1199815SMickaël Salaün }
4143e1199815SMickaël Salaün 
4144e1199815SMickaël Salaün #define for_each_path(path_list, path_entry, i)               \
4145e1199815SMickaël Salaün 	for (i = 0, path_entry = *path_list[i]; path_list[i]; \
4146e1199815SMickaël Salaün 	     path_entry = *path_list[++i])
4147e1199815SMickaël Salaün 
4148e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, same_content_different_file)
4149e1199815SMickaël Salaün {
4150e1199815SMickaël Salaün 	/* Sets access right on parent directories of both layers. */
4151e1199815SMickaël Salaün 	const struct rule layer1_base[] = {
4152e1199815SMickaël Salaün 		{
4153e1199815SMickaël Salaün 			.path = LOWER_BASE,
4154e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4155e1199815SMickaël Salaün 		},
4156e1199815SMickaël Salaün 		{
4157e1199815SMickaël Salaün 			.path = UPPER_BASE,
4158e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4159e1199815SMickaël Salaün 		},
4160e1199815SMickaël Salaün 		{
4161e1199815SMickaël Salaün 			.path = MERGE_BASE,
4162e1199815SMickaël Salaün 			.access = ACCESS_RW,
4163e1199815SMickaël Salaün 		},
4164135464f9SMickaël Salaün 		{},
4165e1199815SMickaël Salaün 	};
4166e1199815SMickaël Salaün 	const struct rule layer2_data[] = {
4167e1199815SMickaël Salaün 		{
4168e1199815SMickaël Salaün 			.path = LOWER_DATA,
4169e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4170e1199815SMickaël Salaün 		},
4171e1199815SMickaël Salaün 		{
4172e1199815SMickaël Salaün 			.path = UPPER_DATA,
4173e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4174e1199815SMickaël Salaün 		},
4175e1199815SMickaël Salaün 		{
4176e1199815SMickaël Salaün 			.path = MERGE_DATA,
4177e1199815SMickaël Salaün 			.access = ACCESS_RW,
4178e1199815SMickaël Salaün 		},
4179135464f9SMickaël Salaün 		{},
4180e1199815SMickaël Salaün 	};
4181e1199815SMickaël Salaün 	/* Sets access right on directories inside both layers. */
4182e1199815SMickaël Salaün 	const struct rule layer3_subdirs[] = {
4183e1199815SMickaël Salaün 		{
4184e1199815SMickaël Salaün 			.path = lower_dl1,
4185e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4186e1199815SMickaël Salaün 		},
4187e1199815SMickaël Salaün 		{
4188e1199815SMickaël Salaün 			.path = lower_do1,
4189e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4190e1199815SMickaël Salaün 		},
4191e1199815SMickaël Salaün 		{
4192e1199815SMickaël Salaün 			.path = upper_du1,
4193e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4194e1199815SMickaël Salaün 		},
4195e1199815SMickaël Salaün 		{
4196e1199815SMickaël Salaün 			.path = upper_do1,
4197e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4198e1199815SMickaël Salaün 		},
4199e1199815SMickaël Salaün 		{
4200e1199815SMickaël Salaün 			.path = merge_dl1,
4201e1199815SMickaël Salaün 			.access = ACCESS_RW,
4202e1199815SMickaël Salaün 		},
4203e1199815SMickaël Salaün 		{
4204e1199815SMickaël Salaün 			.path = merge_du1,
4205e1199815SMickaël Salaün 			.access = ACCESS_RW,
4206e1199815SMickaël Salaün 		},
4207e1199815SMickaël Salaün 		{
4208e1199815SMickaël Salaün 			.path = merge_do1,
4209e1199815SMickaël Salaün 			.access = ACCESS_RW,
4210e1199815SMickaël Salaün 		},
4211135464f9SMickaël Salaün 		{},
4212e1199815SMickaël Salaün 	};
4213e1199815SMickaël Salaün 	/* Tighten access rights to the files. */
4214e1199815SMickaël Salaün 	const struct rule layer4_files[] = {
4215e1199815SMickaël Salaün 		{
4216e1199815SMickaël Salaün 			.path = lower_dl1_fl2,
4217e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4218e1199815SMickaël Salaün 		},
4219e1199815SMickaël Salaün 		{
4220e1199815SMickaël Salaün 			.path = lower_do1_fo2,
4221e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4222e1199815SMickaël Salaün 		},
4223e1199815SMickaël Salaün 		{
4224e1199815SMickaël Salaün 			.path = lower_do1_fl3,
4225e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4226e1199815SMickaël Salaün 		},
4227e1199815SMickaël Salaün 		{
4228e1199815SMickaël Salaün 			.path = upper_du1_fu2,
4229e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4230e1199815SMickaël Salaün 		},
4231e1199815SMickaël Salaün 		{
4232e1199815SMickaël Salaün 			.path = upper_do1_fo2,
4233e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4234e1199815SMickaël Salaün 		},
4235e1199815SMickaël Salaün 		{
4236e1199815SMickaël Salaün 			.path = upper_do1_fu3,
4237e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4238e1199815SMickaël Salaün 		},
4239e1199815SMickaël Salaün 		{
4240e1199815SMickaël Salaün 			.path = merge_dl1_fl2,
4241e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4242e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4243e1199815SMickaël Salaün 		},
4244e1199815SMickaël Salaün 		{
4245e1199815SMickaël Salaün 			.path = merge_du1_fu2,
4246e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4247e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4248e1199815SMickaël Salaün 		},
4249e1199815SMickaël Salaün 		{
4250e1199815SMickaël Salaün 			.path = merge_do1_fo2,
4251e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4252e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4253e1199815SMickaël Salaün 		},
4254e1199815SMickaël Salaün 		{
4255e1199815SMickaël Salaün 			.path = merge_do1_fl3,
4256e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4257e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4258e1199815SMickaël Salaün 		},
4259e1199815SMickaël Salaün 		{
4260e1199815SMickaël Salaün 			.path = merge_do1_fu3,
4261e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4262e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4263e1199815SMickaël Salaün 		},
4264135464f9SMickaël Salaün 		{},
4265e1199815SMickaël Salaün 	};
4266e1199815SMickaël Salaün 	const struct rule layer5_merge_only[] = {
4267e1199815SMickaël Salaün 		{
4268e1199815SMickaël Salaün 			.path = MERGE_DATA,
4269e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4270e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4271e1199815SMickaël Salaün 		},
4272135464f9SMickaël Salaün 		{},
4273e1199815SMickaël Salaün 	};
4274e1199815SMickaël Salaün 	int ruleset_fd;
4275e1199815SMickaël Salaün 	size_t i;
4276e1199815SMickaël Salaün 	const char *path_entry;
4277e1199815SMickaël Salaün 
4278*366617a6SJeff Xu 	if (!supports_overlayfs())
4279*366617a6SJeff Xu 		SKIP(return, "overlayfs is not supported");
4280*366617a6SJeff Xu 
4281e1199815SMickaël Salaün 	/* Sets rules on base directories (i.e. outside overlay scope). */
4282e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
4283e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4284e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4285e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4286e1199815SMickaël Salaün 
4287e1199815SMickaël Salaün 	/* Checks lower layer. */
4288e1199815SMickaël Salaün 	for_each_path(lower_base_files, path_entry, i) {
4289e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4290e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4291e1199815SMickaël Salaün 	}
4292e1199815SMickaël Salaün 	for_each_path(lower_base_directories, path_entry, i) {
4293371183faSMickaël Salaün 		ASSERT_EQ(EACCES,
4294371183faSMickaël Salaün 			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
4295e1199815SMickaël Salaün 	}
4296e1199815SMickaël Salaün 	for_each_path(lower_sub_files, path_entry, i) {
4297e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4298e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4299e1199815SMickaël Salaün 	}
4300e1199815SMickaël Salaün 	/* Checks upper layer. */
4301e1199815SMickaël Salaün 	for_each_path(upper_base_files, path_entry, i) {
4302e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4303e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4304e1199815SMickaël Salaün 	}
4305e1199815SMickaël Salaün 	for_each_path(upper_base_directories, path_entry, i) {
4306371183faSMickaël Salaün 		ASSERT_EQ(EACCES,
4307371183faSMickaël Salaün 			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
4308e1199815SMickaël Salaün 	}
4309e1199815SMickaël Salaün 	for_each_path(upper_sub_files, path_entry, i) {
4310e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4311e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4312e1199815SMickaël Salaün 	}
4313e1199815SMickaël Salaün 	/*
4314e1199815SMickaël Salaün 	 * Checks that access rights are independent from the lower and upper
4315e1199815SMickaël Salaün 	 * layers: write access to upper files viewed through the merge point
4316e1199815SMickaël Salaün 	 * is still allowed, and write access to lower file viewed (and copied)
4317e1199815SMickaël Salaün 	 * through the merge point is still allowed.
4318e1199815SMickaël Salaün 	 */
4319e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4320e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4321e1199815SMickaël Salaün 	}
4322e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4323e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
4324e1199815SMickaël Salaün 	}
4325e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4326e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4327e1199815SMickaël Salaün 	}
4328e1199815SMickaël Salaün 
4329e1199815SMickaël Salaün 	/* Sets rules on data directories (i.e. inside overlay scope). */
4330e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data);
4331e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4332e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4333e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4334e1199815SMickaël Salaün 
4335e1199815SMickaël Salaün 	/* Checks merge. */
4336e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4337e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4338e1199815SMickaël Salaün 	}
4339e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4340e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
4341e1199815SMickaël Salaün 	}
4342e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4343e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4344e1199815SMickaël Salaün 	}
4345e1199815SMickaël Salaün 
4346e1199815SMickaël Salaün 	/* Same checks with tighter rules. */
4347e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs);
4348e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4349e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4350e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4351e1199815SMickaël Salaün 
4352e1199815SMickaël Salaün 	/* Checks changes for lower layer. */
4353e1199815SMickaël Salaün 	for_each_path(lower_base_files, path_entry, i) {
4354e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
4355e1199815SMickaël Salaün 	}
4356e1199815SMickaël Salaün 	/* Checks changes for upper layer. */
4357e1199815SMickaël Salaün 	for_each_path(upper_base_files, path_entry, i) {
4358e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
4359e1199815SMickaël Salaün 	}
4360e1199815SMickaël Salaün 	/* Checks all merge accesses. */
4361e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4362e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
4363e1199815SMickaël Salaün 	}
4364e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4365e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
4366e1199815SMickaël Salaün 	}
4367e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4368e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4369e1199815SMickaël Salaün 	}
4370e1199815SMickaël Salaün 
4371e1199815SMickaël Salaün 	/* Sets rules directly on overlayed files. */
4372e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files);
4373e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4374e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4375e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4376e1199815SMickaël Salaün 
4377e1199815SMickaël Salaün 	/* Checks unchanged accesses on lower layer. */
4378e1199815SMickaël Salaün 	for_each_path(lower_sub_files, path_entry, i) {
4379e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4380e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4381e1199815SMickaël Salaün 	}
4382e1199815SMickaël Salaün 	/* Checks unchanged accesses on upper layer. */
4383e1199815SMickaël Salaün 	for_each_path(upper_sub_files, path_entry, i) {
4384e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4385e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4386e1199815SMickaël Salaün 	}
4387e1199815SMickaël Salaün 	/* Checks all merge accesses. */
4388e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4389e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
4390e1199815SMickaël Salaün 	}
4391e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4392371183faSMickaël Salaün 		ASSERT_EQ(EACCES,
4393371183faSMickaël Salaün 			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
4394e1199815SMickaël Salaün 	}
4395e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4396e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4397e1199815SMickaël Salaün 	}
4398e1199815SMickaël Salaün 
4399e1199815SMickaël Salaün 	/* Only allowes access to the merge hierarchy. */
4400e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only);
4401e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4402e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4403e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4404e1199815SMickaël Salaün 
4405e1199815SMickaël Salaün 	/* Checks new accesses on lower layer. */
4406e1199815SMickaël Salaün 	for_each_path(lower_sub_files, path_entry, i) {
4407e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
4408e1199815SMickaël Salaün 	}
4409e1199815SMickaël Salaün 	/* Checks new accesses on upper layer. */
4410e1199815SMickaël Salaün 	for_each_path(upper_sub_files, path_entry, i) {
4411e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
4412e1199815SMickaël Salaün 	}
4413e1199815SMickaël Salaün 	/* Checks all merge accesses. */
4414e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4415e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
4416e1199815SMickaël Salaün 	}
4417e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4418371183faSMickaël Salaün 		ASSERT_EQ(EACCES,
4419371183faSMickaël Salaün 			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
4420e1199815SMickaël Salaün 	}
4421e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4422e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4423e1199815SMickaël Salaün 	}
4424e1199815SMickaël Salaün }
4425e1199815SMickaël Salaün 
4426e1199815SMickaël Salaün TEST_HARNESS_MAIN
4427