xref: /openbmc/linux/tools/testing/selftests/landlock/fs_test.c (revision e1199815b47be83346c03e20a3de76f934e4bb34)
1*e1199815SMickaël Salaün // SPDX-License-Identifier: GPL-2.0
2*e1199815SMickaël Salaün /*
3*e1199815SMickaël Salaün  * Landlock tests - Filesystem
4*e1199815SMickaël Salaün  *
5*e1199815SMickaël Salaün  * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6*e1199815SMickaël Salaün  * Copyright © 2020 ANSSI
7*e1199815SMickaël Salaün  * Copyright © 2020-2021 Microsoft Corporation
8*e1199815SMickaël Salaün  */
9*e1199815SMickaël Salaün 
10*e1199815SMickaël Salaün #define _GNU_SOURCE
11*e1199815SMickaël Salaün #include <fcntl.h>
12*e1199815SMickaël Salaün #include <linux/landlock.h>
13*e1199815SMickaël Salaün #include <sched.h>
14*e1199815SMickaël Salaün #include <string.h>
15*e1199815SMickaël Salaün #include <sys/capability.h>
16*e1199815SMickaël Salaün #include <sys/mount.h>
17*e1199815SMickaël Salaün #include <sys/prctl.h>
18*e1199815SMickaël Salaün #include <sys/sendfile.h>
19*e1199815SMickaël Salaün #include <sys/stat.h>
20*e1199815SMickaël Salaün #include <sys/sysmacros.h>
21*e1199815SMickaël Salaün #include <unistd.h>
22*e1199815SMickaël Salaün 
23*e1199815SMickaël Salaün #include "common.h"
24*e1199815SMickaël Salaün 
25*e1199815SMickaël Salaün #define TMP_DIR		"tmp"
26*e1199815SMickaël Salaün #define BINARY_PATH	"./true"
27*e1199815SMickaël Salaün 
28*e1199815SMickaël Salaün /* Paths (sibling number and depth) */
29*e1199815SMickaël Salaün static const char dir_s1d1[] = TMP_DIR "/s1d1";
30*e1199815SMickaël Salaün static const char file1_s1d1[] = TMP_DIR "/s1d1/f1";
31*e1199815SMickaël Salaün static const char file2_s1d1[] = TMP_DIR "/s1d1/f2";
32*e1199815SMickaël Salaün static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2";
33*e1199815SMickaël Salaün static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1";
34*e1199815SMickaël Salaün static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2";
35*e1199815SMickaël Salaün static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3";
36*e1199815SMickaël Salaün static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1";
37*e1199815SMickaël Salaün static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2";
38*e1199815SMickaël Salaün 
39*e1199815SMickaël Salaün static const char dir_s2d1[] = TMP_DIR "/s2d1";
40*e1199815SMickaël Salaün static const char file1_s2d1[] = TMP_DIR "/s2d1/f1";
41*e1199815SMickaël Salaün static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2";
42*e1199815SMickaël Salaün static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1";
43*e1199815SMickaël Salaün static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3";
44*e1199815SMickaël Salaün static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1";
45*e1199815SMickaël Salaün static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2";
46*e1199815SMickaël Salaün 
47*e1199815SMickaël Salaün static const char dir_s3d1[] = TMP_DIR "/s3d1";
48*e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */
49*e1199815SMickaël Salaün static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2";
50*e1199815SMickaël Salaün static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3";
51*e1199815SMickaël Salaün 
52*e1199815SMickaël Salaün /*
53*e1199815SMickaël Salaün  * layout1 hierarchy:
54*e1199815SMickaël Salaün  *
55*e1199815SMickaël Salaün  * tmp
56*e1199815SMickaël Salaün  * ├── s1d1
57*e1199815SMickaël Salaün  * │   ├── f1
58*e1199815SMickaël Salaün  * │   ├── f2
59*e1199815SMickaël Salaün  * │   └── s1d2
60*e1199815SMickaël Salaün  * │       ├── f1
61*e1199815SMickaël Salaün  * │       ├── f2
62*e1199815SMickaël Salaün  * │       └── s1d3
63*e1199815SMickaël Salaün  * │           ├── f1
64*e1199815SMickaël Salaün  * │           └── f2
65*e1199815SMickaël Salaün  * ├── s2d1
66*e1199815SMickaël Salaün  * │   ├── f1
67*e1199815SMickaël Salaün  * │   └── s2d2
68*e1199815SMickaël Salaün  * │       ├── f1
69*e1199815SMickaël Salaün  * │       └── s2d3
70*e1199815SMickaël Salaün  * │           ├── f1
71*e1199815SMickaël Salaün  * │           └── f2
72*e1199815SMickaël Salaün  * └── s3d1
73*e1199815SMickaël Salaün  *     └── s3d2
74*e1199815SMickaël Salaün  *         └── s3d3
75*e1199815SMickaël Salaün  */
76*e1199815SMickaël Salaün 
77*e1199815SMickaël Salaün static void mkdir_parents(struct __test_metadata *const _metadata,
78*e1199815SMickaël Salaün 		const char *const path)
79*e1199815SMickaël Salaün {
80*e1199815SMickaël Salaün 	char *walker;
81*e1199815SMickaël Salaün 	const char *parent;
82*e1199815SMickaël Salaün 	int i, err;
83*e1199815SMickaël Salaün 
84*e1199815SMickaël Salaün 	ASSERT_NE(path[0], '\0');
85*e1199815SMickaël Salaün 	walker = strdup(path);
86*e1199815SMickaël Salaün 	ASSERT_NE(NULL, walker);
87*e1199815SMickaël Salaün 	parent = walker;
88*e1199815SMickaël Salaün 	for (i = 1; walker[i]; i++) {
89*e1199815SMickaël Salaün 		if (walker[i] != '/')
90*e1199815SMickaël Salaün 			continue;
91*e1199815SMickaël Salaün 		walker[i] = '\0';
92*e1199815SMickaël Salaün 		err = mkdir(parent, 0700);
93*e1199815SMickaël Salaün 		ASSERT_FALSE(err && errno != EEXIST) {
94*e1199815SMickaël Salaün 			TH_LOG("Failed to create directory \"%s\": %s",
95*e1199815SMickaël Salaün 					parent, strerror(errno));
96*e1199815SMickaël Salaün 		}
97*e1199815SMickaël Salaün 		walker[i] = '/';
98*e1199815SMickaël Salaün 	}
99*e1199815SMickaël Salaün 	free(walker);
100*e1199815SMickaël Salaün }
101*e1199815SMickaël Salaün 
102*e1199815SMickaël Salaün static void create_directory(struct __test_metadata *const _metadata,
103*e1199815SMickaël Salaün 		const char *const path)
104*e1199815SMickaël Salaün {
105*e1199815SMickaël Salaün 	mkdir_parents(_metadata, path);
106*e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(path, 0700)) {
107*e1199815SMickaël Salaün 		TH_LOG("Failed to create directory \"%s\": %s", path,
108*e1199815SMickaël Salaün 				strerror(errno));
109*e1199815SMickaël Salaün 	}
110*e1199815SMickaël Salaün }
111*e1199815SMickaël Salaün 
112*e1199815SMickaël Salaün static void create_file(struct __test_metadata *const _metadata,
113*e1199815SMickaël Salaün 		const char *const path)
114*e1199815SMickaël Salaün {
115*e1199815SMickaël Salaün 	mkdir_parents(_metadata, path);
116*e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) {
117*e1199815SMickaël Salaün 		TH_LOG("Failed to create file \"%s\": %s", path,
118*e1199815SMickaël Salaün 				strerror(errno));
119*e1199815SMickaël Salaün 	}
120*e1199815SMickaël Salaün }
121*e1199815SMickaël Salaün 
122*e1199815SMickaël Salaün static int remove_path(const char *const path)
123*e1199815SMickaël Salaün {
124*e1199815SMickaël Salaün 	char *walker;
125*e1199815SMickaël Salaün 	int i, ret, err = 0;
126*e1199815SMickaël Salaün 
127*e1199815SMickaël Salaün 	walker = strdup(path);
128*e1199815SMickaël Salaün 	if (!walker) {
129*e1199815SMickaël Salaün 		err = ENOMEM;
130*e1199815SMickaël Salaün 		goto out;
131*e1199815SMickaël Salaün 	}
132*e1199815SMickaël Salaün 	if (unlink(path) && rmdir(path)) {
133*e1199815SMickaël Salaün 		if (errno != ENOENT)
134*e1199815SMickaël Salaün 			err = errno;
135*e1199815SMickaël Salaün 		goto out;
136*e1199815SMickaël Salaün 	}
137*e1199815SMickaël Salaün 	for (i = strlen(walker); i > 0; i--) {
138*e1199815SMickaël Salaün 		if (walker[i] != '/')
139*e1199815SMickaël Salaün 			continue;
140*e1199815SMickaël Salaün 		walker[i] = '\0';
141*e1199815SMickaël Salaün 		ret = rmdir(walker);
142*e1199815SMickaël Salaün 		if (ret) {
143*e1199815SMickaël Salaün 			if (errno != ENOTEMPTY && errno != EBUSY)
144*e1199815SMickaël Salaün 				err = errno;
145*e1199815SMickaël Salaün 			goto out;
146*e1199815SMickaël Salaün 		}
147*e1199815SMickaël Salaün 		if (strcmp(walker, TMP_DIR) == 0)
148*e1199815SMickaël Salaün 			goto out;
149*e1199815SMickaël Salaün 	}
150*e1199815SMickaël Salaün 
151*e1199815SMickaël Salaün out:
152*e1199815SMickaël Salaün 	free(walker);
153*e1199815SMickaël Salaün 	return err;
154*e1199815SMickaël Salaün }
155*e1199815SMickaël Salaün 
156*e1199815SMickaël Salaün static void prepare_layout(struct __test_metadata *const _metadata)
157*e1199815SMickaël Salaün {
158*e1199815SMickaël Salaün 	disable_caps(_metadata);
159*e1199815SMickaël Salaün 	umask(0077);
160*e1199815SMickaël Salaün 	create_directory(_metadata, TMP_DIR);
161*e1199815SMickaël Salaün 
162*e1199815SMickaël Salaün 	/*
163*e1199815SMickaël Salaün 	 * Do not pollute the rest of the system: creates a private mount point
164*e1199815SMickaël Salaün 	 * for tests relying on pivot_root(2) and move_mount(2).
165*e1199815SMickaël Salaün 	 */
166*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
167*e1199815SMickaël Salaün 	ASSERT_EQ(0, unshare(CLONE_NEWNS));
168*e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("tmp", TMP_DIR, "tmpfs", 0, "size=4m,mode=700"));
169*e1199815SMickaël Salaün 	ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));
170*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
171*e1199815SMickaël Salaün }
172*e1199815SMickaël Salaün 
173*e1199815SMickaël Salaün static void cleanup_layout(struct __test_metadata *const _metadata)
174*e1199815SMickaël Salaün {
175*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
176*e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(TMP_DIR));
177*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
178*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(TMP_DIR));
179*e1199815SMickaël Salaün }
180*e1199815SMickaël Salaün 
181*e1199815SMickaël Salaün static void create_layout1(struct __test_metadata *const _metadata)
182*e1199815SMickaël Salaün {
183*e1199815SMickaël Salaün 	create_file(_metadata, file1_s1d1);
184*e1199815SMickaël Salaün 	create_file(_metadata, file1_s1d2);
185*e1199815SMickaël Salaün 	create_file(_metadata, file1_s1d3);
186*e1199815SMickaël Salaün 	create_file(_metadata, file2_s1d1);
187*e1199815SMickaël Salaün 	create_file(_metadata, file2_s1d2);
188*e1199815SMickaël Salaün 	create_file(_metadata, file2_s1d3);
189*e1199815SMickaël Salaün 
190*e1199815SMickaël Salaün 	create_file(_metadata, file1_s2d1);
191*e1199815SMickaël Salaün 	create_file(_metadata, file1_s2d2);
192*e1199815SMickaël Salaün 	create_file(_metadata, file1_s2d3);
193*e1199815SMickaël Salaün 	create_file(_metadata, file2_s2d3);
194*e1199815SMickaël Salaün 
195*e1199815SMickaël Salaün 	create_directory(_metadata, dir_s3d2);
196*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
197*e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700"));
198*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
199*e1199815SMickaël Salaün 
200*e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s3d3, 0700));
201*e1199815SMickaël Salaün }
202*e1199815SMickaël Salaün 
203*e1199815SMickaël Salaün static void remove_layout1(struct __test_metadata *const _metadata)
204*e1199815SMickaël Salaün {
205*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s1d3));
206*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s1d2));
207*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s1d1));
208*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s1d3));
209*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s1d2));
210*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s1d1));
211*e1199815SMickaël Salaün 
212*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s2d3));
213*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s2d3));
214*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s2d2));
215*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s2d1));
216*e1199815SMickaël Salaün 
217*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(dir_s3d3));
218*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
219*e1199815SMickaël Salaün 	umount(dir_s3d2);
220*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
221*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(dir_s3d2));
222*e1199815SMickaël Salaün }
223*e1199815SMickaël Salaün 
224*e1199815SMickaël Salaün FIXTURE(layout1) {
225*e1199815SMickaël Salaün };
226*e1199815SMickaël Salaün 
227*e1199815SMickaël Salaün FIXTURE_SETUP(layout1)
228*e1199815SMickaël Salaün {
229*e1199815SMickaël Salaün 	prepare_layout(_metadata);
230*e1199815SMickaël Salaün 
231*e1199815SMickaël Salaün 	create_layout1(_metadata);
232*e1199815SMickaël Salaün }
233*e1199815SMickaël Salaün 
234*e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1)
235*e1199815SMickaël Salaün {
236*e1199815SMickaël Salaün 	remove_layout1(_metadata);
237*e1199815SMickaël Salaün 
238*e1199815SMickaël Salaün 	cleanup_layout(_metadata);
239*e1199815SMickaël Salaün }
240*e1199815SMickaël Salaün 
241*e1199815SMickaël Salaün /*
242*e1199815SMickaël Salaün  * This helper enables to use the ASSERT_* macros and print the line number
243*e1199815SMickaël Salaün  * pointing to the test caller.
244*e1199815SMickaël Salaün  */
245*e1199815SMickaël Salaün static int test_open_rel(const int dirfd, const char *const path, const int flags)
246*e1199815SMickaël Salaün {
247*e1199815SMickaël Salaün 	int fd;
248*e1199815SMickaël Salaün 
249*e1199815SMickaël Salaün 	/* Works with file and directories. */
250*e1199815SMickaël Salaün 	fd = openat(dirfd, path, flags | O_CLOEXEC);
251*e1199815SMickaël Salaün 	if (fd < 0)
252*e1199815SMickaël Salaün 		return errno;
253*e1199815SMickaël Salaün 	/*
254*e1199815SMickaël Salaün 	 * Mixing error codes from close(2) and open(2) should not lead to any
255*e1199815SMickaël Salaün 	 * (access type) confusion for this test.
256*e1199815SMickaël Salaün 	 */
257*e1199815SMickaël Salaün 	if (close(fd) != 0)
258*e1199815SMickaël Salaün 		return errno;
259*e1199815SMickaël Salaün 	return 0;
260*e1199815SMickaël Salaün }
261*e1199815SMickaël Salaün 
262*e1199815SMickaël Salaün static int test_open(const char *const path, const int flags)
263*e1199815SMickaël Salaün {
264*e1199815SMickaël Salaün 	return test_open_rel(AT_FDCWD, path, flags);
265*e1199815SMickaël Salaün }
266*e1199815SMickaël Salaün 
267*e1199815SMickaël Salaün TEST_F_FORK(layout1, no_restriction)
268*e1199815SMickaël Salaün {
269*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
270*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
271*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY));
272*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
273*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
274*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY));
275*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
276*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
277*e1199815SMickaël Salaün 
278*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
279*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
280*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
281*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
282*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY));
283*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY));
284*e1199815SMickaël Salaün 
285*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
286*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
287*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
288*e1199815SMickaël Salaün }
289*e1199815SMickaël Salaün 
290*e1199815SMickaël Salaün TEST_F_FORK(layout1, inval)
291*e1199815SMickaël Salaün {
292*e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath = {
293*e1199815SMickaël Salaün 		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
294*e1199815SMickaël Salaün 			LANDLOCK_ACCESS_FS_WRITE_FILE,
295*e1199815SMickaël Salaün 		.parent_fd = -1,
296*e1199815SMickaël Salaün 	};
297*e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {
298*e1199815SMickaël Salaün 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |
299*e1199815SMickaël Salaün 			LANDLOCK_ACCESS_FS_WRITE_FILE,
300*e1199815SMickaël Salaün 	};
301*e1199815SMickaël Salaün 	int ruleset_fd;
302*e1199815SMickaël Salaün 
303*e1199815SMickaël Salaün 	path_beneath.parent_fd = open(dir_s1d2, O_PATH | O_DIRECTORY |
304*e1199815SMickaël Salaün 			O_CLOEXEC);
305*e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
306*e1199815SMickaël Salaün 
307*e1199815SMickaël Salaün 	ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);
308*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
309*e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
310*e1199815SMickaël Salaün 				&path_beneath, 0));
311*e1199815SMickaël Salaün 	/* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */
312*e1199815SMickaël Salaün 	ASSERT_EQ(EBADF, errno);
313*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
314*e1199815SMickaël Salaün 
315*e1199815SMickaël Salaün 	ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC);
316*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
317*e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
318*e1199815SMickaël Salaün 				&path_beneath, 0));
319*e1199815SMickaël Salaün 	/* Returns EBADFD because ruleset_fd is not a valid ruleset. */
320*e1199815SMickaël Salaün 	ASSERT_EQ(EBADFD, errno);
321*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
322*e1199815SMickaël Salaün 
323*e1199815SMickaël Salaün 	/* Gets a real ruleset. */
324*e1199815SMickaël Salaün 	ruleset_fd = landlock_create_ruleset(&ruleset_attr,
325*e1199815SMickaël Salaün 			sizeof(ruleset_attr), 0);
326*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
327*e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
328*e1199815SMickaël Salaün 				&path_beneath, 0));
329*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
330*e1199815SMickaël Salaün 
331*e1199815SMickaël Salaün 	/* Tests without O_PATH. */
332*e1199815SMickaël Salaün 	path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC);
333*e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
334*e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
335*e1199815SMickaël Salaün 				&path_beneath, 0));
336*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
337*e1199815SMickaël Salaün 
338*e1199815SMickaël Salaün 	/* Tests with a ruleset FD. */
339*e1199815SMickaël Salaün 	path_beneath.parent_fd = ruleset_fd;
340*e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
341*e1199815SMickaël Salaün 				&path_beneath, 0));
342*e1199815SMickaël Salaün 	ASSERT_EQ(EBADFD, errno);
343*e1199815SMickaël Salaün 
344*e1199815SMickaël Salaün 	/* Checks unhandled allowed_access. */
345*e1199815SMickaël Salaün 	path_beneath.parent_fd = open(dir_s1d2, O_PATH | O_DIRECTORY |
346*e1199815SMickaël Salaün 			O_CLOEXEC);
347*e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
348*e1199815SMickaël Salaün 
349*e1199815SMickaël Salaün 	/* Test with legitimate values. */
350*e1199815SMickaël Salaün 	path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE;
351*e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
352*e1199815SMickaël Salaün 				&path_beneath, 0));
353*e1199815SMickaël Salaün 	ASSERT_EQ(EINVAL, errno);
354*e1199815SMickaël Salaün 	path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE;
355*e1199815SMickaël Salaün 
356*e1199815SMickaël Salaün 	/* Test with unknown (64-bits) value. */
357*e1199815SMickaël Salaün 	path_beneath.allowed_access |= (1ULL << 60);
358*e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
359*e1199815SMickaël Salaün 				&path_beneath, 0));
360*e1199815SMickaël Salaün 	ASSERT_EQ(EINVAL, errno);
361*e1199815SMickaël Salaün 	path_beneath.allowed_access &= ~(1ULL << 60);
362*e1199815SMickaël Salaün 
363*e1199815SMickaël Salaün 	/* Test with no access. */
364*e1199815SMickaël Salaün 	path_beneath.allowed_access = 0;
365*e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
366*e1199815SMickaël Salaün 				&path_beneath, 0));
367*e1199815SMickaël Salaün 	ASSERT_EQ(ENOMSG, errno);
368*e1199815SMickaël Salaün 	path_beneath.allowed_access &= ~(1ULL << 60);
369*e1199815SMickaël Salaün 
370*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
371*e1199815SMickaël Salaün 
372*e1199815SMickaël Salaün 	/* Enforces the ruleset. */
373*e1199815SMickaël Salaün 	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
374*e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
375*e1199815SMickaël Salaün 
376*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
377*e1199815SMickaël Salaün }
378*e1199815SMickaël Salaün 
379*e1199815SMickaël Salaün #define ACCESS_FILE ( \
380*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_EXECUTE | \
381*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_WRITE_FILE | \
382*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_FILE)
383*e1199815SMickaël Salaün 
384*e1199815SMickaël Salaün #define ACCESS_LAST LANDLOCK_ACCESS_FS_MAKE_SYM
385*e1199815SMickaël Salaün 
386*e1199815SMickaël Salaün #define ACCESS_ALL ( \
387*e1199815SMickaël Salaün 	ACCESS_FILE | \
388*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_DIR | \
389*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_REMOVE_DIR | \
390*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_REMOVE_FILE | \
391*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_CHAR | \
392*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_DIR | \
393*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_REG | \
394*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_SOCK | \
395*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_FIFO | \
396*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
397*e1199815SMickaël Salaün 	ACCESS_LAST)
398*e1199815SMickaël Salaün 
399*e1199815SMickaël Salaün TEST_F_FORK(layout1, file_access_rights)
400*e1199815SMickaël Salaün {
401*e1199815SMickaël Salaün 	__u64 access;
402*e1199815SMickaël Salaün 	int err;
403*e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath = {};
404*e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {
405*e1199815SMickaël Salaün 		.handled_access_fs = ACCESS_ALL,
406*e1199815SMickaël Salaün 	};
407*e1199815SMickaël Salaün 	const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
408*e1199815SMickaël Salaün 			sizeof(ruleset_attr), 0);
409*e1199815SMickaël Salaün 
410*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
411*e1199815SMickaël Salaün 
412*e1199815SMickaël Salaün 	/* Tests access rights for files. */
413*e1199815SMickaël Salaün 	path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);
414*e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
415*e1199815SMickaël Salaün 	for (access = 1; access <= ACCESS_LAST; access <<= 1) {
416*e1199815SMickaël Salaün 		path_beneath.allowed_access = access;
417*e1199815SMickaël Salaün 		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
418*e1199815SMickaël Salaün 				&path_beneath, 0);
419*e1199815SMickaël Salaün 		if ((access | ACCESS_FILE) == ACCESS_FILE) {
420*e1199815SMickaël Salaün 			ASSERT_EQ(0, err);
421*e1199815SMickaël Salaün 		} else {
422*e1199815SMickaël Salaün 			ASSERT_EQ(-1, err);
423*e1199815SMickaël Salaün 			ASSERT_EQ(EINVAL, errno);
424*e1199815SMickaël Salaün 		}
425*e1199815SMickaël Salaün 	}
426*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
427*e1199815SMickaël Salaün }
428*e1199815SMickaël Salaün 
429*e1199815SMickaël Salaün static void add_path_beneath(struct __test_metadata *const _metadata,
430*e1199815SMickaël Salaün 		const int ruleset_fd, const __u64 allowed_access,
431*e1199815SMickaël Salaün 		const char *const path)
432*e1199815SMickaël Salaün {
433*e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath = {
434*e1199815SMickaël Salaün 		.allowed_access = allowed_access,
435*e1199815SMickaël Salaün 	};
436*e1199815SMickaël Salaün 
437*e1199815SMickaël Salaün 	path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC);
438*e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd) {
439*e1199815SMickaël Salaün 		TH_LOG("Failed to open directory \"%s\": %s", path,
440*e1199815SMickaël Salaün 				strerror(errno));
441*e1199815SMickaël Salaün 	}
442*e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
443*e1199815SMickaël Salaün 				&path_beneath, 0)) {
444*e1199815SMickaël Salaün 		TH_LOG("Failed to update the ruleset with \"%s\": %s", path,
445*e1199815SMickaël Salaün 				strerror(errno));
446*e1199815SMickaël Salaün 	}
447*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
448*e1199815SMickaël Salaün }
449*e1199815SMickaël Salaün 
450*e1199815SMickaël Salaün struct rule {
451*e1199815SMickaël Salaün 	const char *path;
452*e1199815SMickaël Salaün 	__u64 access;
453*e1199815SMickaël Salaün };
454*e1199815SMickaël Salaün 
455*e1199815SMickaël Salaün #define ACCESS_RO ( \
456*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_FILE | \
457*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_DIR)
458*e1199815SMickaël Salaün 
459*e1199815SMickaël Salaün #define ACCESS_RW ( \
460*e1199815SMickaël Salaün 	ACCESS_RO | \
461*e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_WRITE_FILE)
462*e1199815SMickaël Salaün 
463*e1199815SMickaël Salaün static int create_ruleset(struct __test_metadata *const _metadata,
464*e1199815SMickaël Salaün 		const __u64 handled_access_fs, const struct rule rules[])
465*e1199815SMickaël Salaün {
466*e1199815SMickaël Salaün 	int ruleset_fd, i;
467*e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {
468*e1199815SMickaël Salaün 		.handled_access_fs = handled_access_fs,
469*e1199815SMickaël Salaün 	};
470*e1199815SMickaël Salaün 
471*e1199815SMickaël Salaün 	ASSERT_NE(NULL, rules) {
472*e1199815SMickaël Salaün 		TH_LOG("No rule list");
473*e1199815SMickaël Salaün 	}
474*e1199815SMickaël Salaün 	ASSERT_NE(NULL, rules[0].path) {
475*e1199815SMickaël Salaün 		TH_LOG("Empty rule list");
476*e1199815SMickaël Salaün 	}
477*e1199815SMickaël Salaün 
478*e1199815SMickaël Salaün 	ruleset_fd = landlock_create_ruleset(&ruleset_attr,
479*e1199815SMickaël Salaün 			sizeof(ruleset_attr), 0);
480*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd) {
481*e1199815SMickaël Salaün 		TH_LOG("Failed to create a ruleset: %s", strerror(errno));
482*e1199815SMickaël Salaün 	}
483*e1199815SMickaël Salaün 
484*e1199815SMickaël Salaün 	for (i = 0; rules[i].path; i++) {
485*e1199815SMickaël Salaün 		add_path_beneath(_metadata, ruleset_fd, rules[i].access,
486*e1199815SMickaël Salaün 				rules[i].path);
487*e1199815SMickaël Salaün 	}
488*e1199815SMickaël Salaün 	return ruleset_fd;
489*e1199815SMickaël Salaün }
490*e1199815SMickaël Salaün 
491*e1199815SMickaël Salaün static void enforce_ruleset(struct __test_metadata *const _metadata,
492*e1199815SMickaël Salaün 		const int ruleset_fd)
493*e1199815SMickaël Salaün {
494*e1199815SMickaël Salaün 	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
495*e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) {
496*e1199815SMickaël Salaün 		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
497*e1199815SMickaël Salaün 	}
498*e1199815SMickaël Salaün }
499*e1199815SMickaël Salaün 
500*e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_nsfs)
501*e1199815SMickaël Salaün {
502*e1199815SMickaël Salaün 	const struct rule rules[] = {
503*e1199815SMickaël Salaün 		{
504*e1199815SMickaël Salaün 			.path = "/dev/null",
505*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
506*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
507*e1199815SMickaël Salaün 		},
508*e1199815SMickaël Salaün 		{}
509*e1199815SMickaël Salaün 	};
510*e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath;
511*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access |
512*e1199815SMickaël Salaün 			LANDLOCK_ACCESS_FS_READ_DIR, rules);
513*e1199815SMickaël Salaün 
514*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
515*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
516*e1199815SMickaël Salaün 
517*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
518*e1199815SMickaël Salaün 
519*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
520*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY));
521*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/dev/null", O_RDONLY));
522*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY));
523*e1199815SMickaël Salaün 
524*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY));
525*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY));
526*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY));
527*e1199815SMickaël Salaün 	/*
528*e1199815SMickaël Salaün 	 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a
529*e1199815SMickaël Salaün 	 * disconnected path.  Such path cannot be identified and must then be
530*e1199815SMickaël Salaün 	 * allowed.
531*e1199815SMickaël Salaün 	 */
532*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
533*e1199815SMickaël Salaün 
534*e1199815SMickaël Salaün 	/*
535*e1199815SMickaël Salaün 	 * Checks that it is not possible to add nsfs-like filesystem
536*e1199815SMickaël Salaün 	 * references to a ruleset.
537*e1199815SMickaël Salaün 	 */
538*e1199815SMickaël Salaün 	path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
539*e1199815SMickaël Salaün 		LANDLOCK_ACCESS_FS_WRITE_FILE,
540*e1199815SMickaël Salaün 	path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC);
541*e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
542*e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
543*e1199815SMickaël Salaün 				&path_beneath, 0));
544*e1199815SMickaël Salaün 	ASSERT_EQ(EBADFD, errno);
545*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
546*e1199815SMickaël Salaün }
547*e1199815SMickaël Salaün 
548*e1199815SMickaël Salaün TEST_F_FORK(layout1, unpriv) {
549*e1199815SMickaël Salaün 	const struct rule rules[] = {
550*e1199815SMickaël Salaün 		{
551*e1199815SMickaël Salaün 			.path = dir_s1d2,
552*e1199815SMickaël Salaün 			.access = ACCESS_RO,
553*e1199815SMickaël Salaün 		},
554*e1199815SMickaël Salaün 		{}
555*e1199815SMickaël Salaün 	};
556*e1199815SMickaël Salaün 	int ruleset_fd;
557*e1199815SMickaël Salaün 
558*e1199815SMickaël Salaün 	drop_caps(_metadata);
559*e1199815SMickaël Salaün 
560*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
561*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
562*e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));
563*e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
564*e1199815SMickaël Salaün 
565*e1199815SMickaël Salaün 	/* enforce_ruleset() calls prctl(no_new_privs). */
566*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
567*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
568*e1199815SMickaël Salaün }
569*e1199815SMickaël Salaün 
570*e1199815SMickaël Salaün TEST_F_FORK(layout1, effective_access)
571*e1199815SMickaël Salaün {
572*e1199815SMickaël Salaün 	const struct rule rules[] = {
573*e1199815SMickaël Salaün 		{
574*e1199815SMickaël Salaün 			.path = dir_s1d2,
575*e1199815SMickaël Salaün 			.access = ACCESS_RO,
576*e1199815SMickaël Salaün 		},
577*e1199815SMickaël Salaün 		{
578*e1199815SMickaël Salaün 			.path = file1_s2d2,
579*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
580*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
581*e1199815SMickaël Salaün 		},
582*e1199815SMickaël Salaün 		{}
583*e1199815SMickaël Salaün 	};
584*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
585*e1199815SMickaël Salaün 	char buf;
586*e1199815SMickaël Salaün 	int reg_fd;
587*e1199815SMickaël Salaün 
588*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
589*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
590*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
591*e1199815SMickaël Salaün 
592*e1199815SMickaël Salaün 	/* Tests on a directory. */
593*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
594*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
595*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
596*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
597*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
598*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
599*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
600*e1199815SMickaël Salaün 
601*e1199815SMickaël Salaün 	/* Tests on a file. */
602*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY));
603*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
604*e1199815SMickaël Salaün 
605*e1199815SMickaël Salaün 	/* Checks effective read and write actions. */
606*e1199815SMickaël Salaün 	reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC);
607*e1199815SMickaël Salaün 	ASSERT_LE(0, reg_fd);
608*e1199815SMickaël Salaün 	ASSERT_EQ(1, write(reg_fd, ".", 1));
609*e1199815SMickaël Salaün 	ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET));
610*e1199815SMickaël Salaün 	ASSERT_EQ(1, read(reg_fd, &buf, 1));
611*e1199815SMickaël Salaün 	ASSERT_EQ('.', buf);
612*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(reg_fd));
613*e1199815SMickaël Salaün 
614*e1199815SMickaël Salaün 	/* Just in case, double-checks effective actions. */
615*e1199815SMickaël Salaün 	reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC);
616*e1199815SMickaël Salaün 	ASSERT_LE(0, reg_fd);
617*e1199815SMickaël Salaün 	ASSERT_EQ(-1, write(reg_fd, &buf, 1));
618*e1199815SMickaël Salaün 	ASSERT_EQ(EBADF, errno);
619*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(reg_fd));
620*e1199815SMickaël Salaün }
621*e1199815SMickaël Salaün 
622*e1199815SMickaël Salaün TEST_F_FORK(layout1, unhandled_access)
623*e1199815SMickaël Salaün {
624*e1199815SMickaël Salaün 	const struct rule rules[] = {
625*e1199815SMickaël Salaün 		{
626*e1199815SMickaël Salaün 			.path = dir_s1d2,
627*e1199815SMickaël Salaün 			.access = ACCESS_RO,
628*e1199815SMickaël Salaün 		},
629*e1199815SMickaël Salaün 		{}
630*e1199815SMickaël Salaün 	};
631*e1199815SMickaël Salaün 	/* Here, we only handle read accesses, not write accesses. */
632*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
633*e1199815SMickaël Salaün 
634*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
635*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
636*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
637*e1199815SMickaël Salaün 
638*e1199815SMickaël Salaün 	/*
639*e1199815SMickaël Salaün 	 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE,
640*e1199815SMickaël Salaün 	 * opening for write-only should be allowed, but not read-write.
641*e1199815SMickaël Salaün 	 */
642*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY));
643*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
644*e1199815SMickaël Salaün 
645*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
646*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
647*e1199815SMickaël Salaün }
648*e1199815SMickaël Salaün 
649*e1199815SMickaël Salaün TEST_F_FORK(layout1, ruleset_overlap)
650*e1199815SMickaël Salaün {
651*e1199815SMickaël Salaün 	const struct rule rules[] = {
652*e1199815SMickaël Salaün 		/* These rules should be ORed among them. */
653*e1199815SMickaël Salaün 		{
654*e1199815SMickaël Salaün 			.path = dir_s1d2,
655*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
656*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
657*e1199815SMickaël Salaün 		},
658*e1199815SMickaël Salaün 		{
659*e1199815SMickaël Salaün 			.path = dir_s1d2,
660*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
661*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_READ_DIR,
662*e1199815SMickaël Salaün 		},
663*e1199815SMickaël Salaün 		{}
664*e1199815SMickaël Salaün 	};
665*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
666*e1199815SMickaël Salaün 
667*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
668*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
669*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
670*e1199815SMickaël Salaün 
671*e1199815SMickaël Salaün 	/* Checks s1d1 hierarchy. */
672*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
673*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
674*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
675*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
676*e1199815SMickaël Salaün 
677*e1199815SMickaël Salaün 	/* Checks s1d2 hierarchy. */
678*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
679*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
680*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
681*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
682*e1199815SMickaël Salaün 
683*e1199815SMickaël Salaün 	/* Checks s1d3 hierarchy. */
684*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
685*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
686*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
687*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
688*e1199815SMickaël Salaün }
689*e1199815SMickaël Salaün 
690*e1199815SMickaël Salaün TEST_F_FORK(layout1, non_overlapping_accesses)
691*e1199815SMickaël Salaün {
692*e1199815SMickaël Salaün 	const struct rule layer1[] = {
693*e1199815SMickaël Salaün 		{
694*e1199815SMickaël Salaün 			.path = dir_s1d2,
695*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
696*e1199815SMickaël Salaün 		},
697*e1199815SMickaël Salaün 		{}
698*e1199815SMickaël Salaün 	};
699*e1199815SMickaël Salaün 	const struct rule layer2[] = {
700*e1199815SMickaël Salaün 		{
701*e1199815SMickaël Salaün 			.path = dir_s1d3,
702*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
703*e1199815SMickaël Salaün 		},
704*e1199815SMickaël Salaün 		{}
705*e1199815SMickaël Salaün 	};
706*e1199815SMickaël Salaün 	int ruleset_fd;
707*e1199815SMickaël Salaün 
708*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
709*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
710*e1199815SMickaël Salaün 
711*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG,
712*e1199815SMickaël Salaün 			layer1);
713*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
714*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
715*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
716*e1199815SMickaël Salaün 
717*e1199815SMickaël Salaün 	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
718*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
719*e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
720*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
721*e1199815SMickaël Salaün 
722*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE,
723*e1199815SMickaël Salaün 			layer2);
724*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
725*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
726*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
727*e1199815SMickaël Salaün 
728*e1199815SMickaël Salaün 	/* Unchanged accesses for file creation. */
729*e1199815SMickaël Salaün 	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
730*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
731*e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
732*e1199815SMickaël Salaün 
733*e1199815SMickaël Salaün 	/* Checks file removing. */
734*e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlink(file1_s1d2));
735*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
736*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
737*e1199815SMickaël Salaün }
738*e1199815SMickaël Salaün 
739*e1199815SMickaël Salaün TEST_F_FORK(layout1, interleaved_masked_accesses)
740*e1199815SMickaël Salaün {
741*e1199815SMickaël Salaün 	/*
742*e1199815SMickaël Salaün 	 * Checks overly restrictive rules:
743*e1199815SMickaël Salaün 	 * layer 1: allows R   s1d1/s1d2/s1d3/file1
744*e1199815SMickaël Salaün 	 * layer 2: allows RW  s1d1/s1d2/s1d3
745*e1199815SMickaël Salaün 	 *          allows  W  s1d1/s1d2
746*e1199815SMickaël Salaün 	 *          denies R   s1d1/s1d2
747*e1199815SMickaël Salaün 	 * layer 3: allows R   s1d1
748*e1199815SMickaël Salaün 	 * layer 4: allows R   s1d1/s1d2
749*e1199815SMickaël Salaün 	 *          denies  W  s1d1/s1d2
750*e1199815SMickaël Salaün 	 * layer 5: allows R   s1d1/s1d2
751*e1199815SMickaël Salaün 	 * layer 6: allows   X ----
752*e1199815SMickaël Salaün 	 * layer 7: allows  W  s1d1/s1d2
753*e1199815SMickaël Salaün 	 *          denies R   s1d1/s1d2
754*e1199815SMickaël Salaün 	 */
755*e1199815SMickaël Salaün 	const struct rule layer1_read[] = {
756*e1199815SMickaël Salaün 		/* Allows read access to file1_s1d3 with the first layer. */
757*e1199815SMickaël Salaün 		{
758*e1199815SMickaël Salaün 			.path = file1_s1d3,
759*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
760*e1199815SMickaël Salaün 		},
761*e1199815SMickaël Salaün 		{}
762*e1199815SMickaël Salaün 	};
763*e1199815SMickaël Salaün 	/* First rule with write restrictions. */
764*e1199815SMickaël Salaün 	const struct rule layer2_read_write[] = {
765*e1199815SMickaël Salaün 		/* Start by granting read-write access via its parent directory... */
766*e1199815SMickaël Salaün 		{
767*e1199815SMickaël Salaün 			.path = dir_s1d3,
768*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
769*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
770*e1199815SMickaël Salaün 		},
771*e1199815SMickaël Salaün 		/* ...but also denies read access via its grandparent directory. */
772*e1199815SMickaël Salaün 		{
773*e1199815SMickaël Salaün 			.path = dir_s1d2,
774*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
775*e1199815SMickaël Salaün 		},
776*e1199815SMickaël Salaün 		{}
777*e1199815SMickaël Salaün 	};
778*e1199815SMickaël Salaün 	const struct rule layer3_read[] = {
779*e1199815SMickaël Salaün 		/* Allows read access via its great-grandparent directory. */
780*e1199815SMickaël Salaün 		{
781*e1199815SMickaël Salaün 			.path = dir_s1d1,
782*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
783*e1199815SMickaël Salaün 		},
784*e1199815SMickaël Salaün 		{}
785*e1199815SMickaël Salaün 	};
786*e1199815SMickaël Salaün 	const struct rule layer4_read_write[] = {
787*e1199815SMickaël Salaün 		/*
788*e1199815SMickaël Salaün 		 * Try to confuse the deny access by denying write (but not
789*e1199815SMickaël Salaün 		 * read) access via its grandparent directory.
790*e1199815SMickaël Salaün 		 */
791*e1199815SMickaël Salaün 		{
792*e1199815SMickaël Salaün 			.path = dir_s1d2,
793*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
794*e1199815SMickaël Salaün 		},
795*e1199815SMickaël Salaün 		{}
796*e1199815SMickaël Salaün 	};
797*e1199815SMickaël Salaün 	const struct rule layer5_read[] = {
798*e1199815SMickaël Salaün 		/*
799*e1199815SMickaël Salaün 		 * Try to override layer2's deny read access by explicitly
800*e1199815SMickaël Salaün 		 * allowing read access via file1_s1d3's grandparent.
801*e1199815SMickaël Salaün 		 */
802*e1199815SMickaël Salaün 		{
803*e1199815SMickaël Salaün 			.path = dir_s1d2,
804*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
805*e1199815SMickaël Salaün 		},
806*e1199815SMickaël Salaün 		{}
807*e1199815SMickaël Salaün 	};
808*e1199815SMickaël Salaün 	const struct rule layer6_execute[] = {
809*e1199815SMickaël Salaün 		/*
810*e1199815SMickaël Salaün 		 * Restricts an unrelated file hierarchy with a new access
811*e1199815SMickaël Salaün 		 * (non-overlapping) type.
812*e1199815SMickaël Salaün 		 */
813*e1199815SMickaël Salaün 		{
814*e1199815SMickaël Salaün 			.path = dir_s2d1,
815*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
816*e1199815SMickaël Salaün 		},
817*e1199815SMickaël Salaün 		{}
818*e1199815SMickaël Salaün 	};
819*e1199815SMickaël Salaün 	const struct rule layer7_read_write[] = {
820*e1199815SMickaël Salaün 		/*
821*e1199815SMickaël Salaün 		 * Finally, denies read access to file1_s1d3 via its
822*e1199815SMickaël Salaün 		 * grandparent.
823*e1199815SMickaël Salaün 		 */
824*e1199815SMickaël Salaün 		{
825*e1199815SMickaël Salaün 			.path = dir_s1d2,
826*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
827*e1199815SMickaël Salaün 		},
828*e1199815SMickaël Salaün 		{}
829*e1199815SMickaël Salaün 	};
830*e1199815SMickaël Salaün 	int ruleset_fd;
831*e1199815SMickaël Salaün 
832*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
833*e1199815SMickaël Salaün 			layer1_read);
834*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
835*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
836*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
837*e1199815SMickaël Salaün 
838*e1199815SMickaël Salaün 	/* Checks that read access is granted for file1_s1d3 with layer 1. */
839*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
840*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
841*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
842*e1199815SMickaël Salaün 
843*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE |
844*e1199815SMickaël Salaün 			LANDLOCK_ACCESS_FS_WRITE_FILE, layer2_read_write);
845*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
846*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
847*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
848*e1199815SMickaël Salaün 
849*e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 2. */
850*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
851*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
852*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
853*e1199815SMickaël Salaün 
854*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
855*e1199815SMickaël Salaün 			layer3_read);
856*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
857*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
858*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
859*e1199815SMickaël Salaün 
860*e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 3. */
861*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
862*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
863*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
864*e1199815SMickaël Salaün 
865*e1199815SMickaël Salaün 	/* This time, denies write access for the file hierarchy. */
866*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE |
867*e1199815SMickaël Salaün 			LANDLOCK_ACCESS_FS_WRITE_FILE, layer4_read_write);
868*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
869*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
870*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
871*e1199815SMickaël Salaün 
872*e1199815SMickaël Salaün 	/*
873*e1199815SMickaël Salaün 	 * Checks that the only change with layer 4 is that write access is
874*e1199815SMickaël Salaün 	 * denied.
875*e1199815SMickaël Salaün 	 */
876*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
877*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
878*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
879*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
880*e1199815SMickaël Salaün 
881*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
882*e1199815SMickaël Salaün 			layer5_read);
883*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
884*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
885*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
886*e1199815SMickaël Salaün 
887*e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 5. */
888*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
889*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
890*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
891*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
892*e1199815SMickaël Salaün 
893*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE,
894*e1199815SMickaël Salaün 			layer6_execute);
895*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
896*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
897*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
898*e1199815SMickaël Salaün 
899*e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 6. */
900*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
901*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
902*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
903*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
904*e1199815SMickaël Salaün 
905*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE |
906*e1199815SMickaël Salaün 			LANDLOCK_ACCESS_FS_WRITE_FILE, layer7_read_write);
907*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
908*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
909*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
910*e1199815SMickaël Salaün 
911*e1199815SMickaël Salaün 	/* Checks read access is now denied with layer 7. */
912*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
913*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
914*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
915*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
916*e1199815SMickaël Salaün }
917*e1199815SMickaël Salaün 
918*e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_subset)
919*e1199815SMickaël Salaün {
920*e1199815SMickaël Salaün 	const struct rule rules[] = {
921*e1199815SMickaël Salaün 		{
922*e1199815SMickaël Salaün 			.path = dir_s1d2,
923*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
924*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_READ_DIR,
925*e1199815SMickaël Salaün 		},
926*e1199815SMickaël Salaün 		{}
927*e1199815SMickaël Salaün 	};
928*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
929*e1199815SMickaël Salaün 
930*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
931*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
932*e1199815SMickaël Salaün 
933*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
934*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
935*e1199815SMickaël Salaün 
936*e1199815SMickaël Salaün 	/* Write access is forbidden. */
937*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
938*e1199815SMickaël Salaün 	/* Readdir access is allowed. */
939*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
940*e1199815SMickaël Salaün 
941*e1199815SMickaël Salaün 	/* Write access is forbidden. */
942*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
943*e1199815SMickaël Salaün 	/* Readdir access is allowed. */
944*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
945*e1199815SMickaël Salaün 
946*e1199815SMickaël Salaün 	/*
947*e1199815SMickaël Salaün 	 * Tests shared rule extension: the following rules should not grant
948*e1199815SMickaël Salaün 	 * any new access, only remove some.  Once enforced, these rules are
949*e1199815SMickaël Salaün 	 * ANDed with the previous ones.
950*e1199815SMickaël Salaün 	 */
951*e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
952*e1199815SMickaël Salaün 			dir_s1d2);
953*e1199815SMickaël Salaün 	/*
954*e1199815SMickaël Salaün 	 * According to ruleset_fd, dir_s1d2 should now have the
955*e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE
956*e1199815SMickaël Salaün 	 * access rights (even if this directory is opened a second time).
957*e1199815SMickaël Salaün 	 * However, when enforcing this updated ruleset, the ruleset tied to
958*e1199815SMickaël Salaün 	 * the current process (i.e. its domain) will still only have the
959*e1199815SMickaël Salaün 	 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and
960*e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but
961*e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would
962*e1199815SMickaël Salaün 	 * be a privilege escalation.
963*e1199815SMickaël Salaün 	 */
964*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
965*e1199815SMickaël Salaün 
966*e1199815SMickaël Salaün 	/* Same tests and results as above. */
967*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
968*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
969*e1199815SMickaël Salaün 
970*e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d2. */
971*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
972*e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
973*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
974*e1199815SMickaël Salaün 
975*e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d3. */
976*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
977*e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
978*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
979*e1199815SMickaël Salaün 
980*e1199815SMickaël Salaün 	/*
981*e1199815SMickaël Salaün 	 * Try to get more privileges by adding new access rights to the parent
982*e1199815SMickaël Salaün 	 * directory: dir_s1d1.
983*e1199815SMickaël Salaün 	 */
984*e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1);
985*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
986*e1199815SMickaël Salaün 
987*e1199815SMickaël Salaün 	/* Same tests and results as above. */
988*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
989*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
990*e1199815SMickaël Salaün 
991*e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d2. */
992*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
993*e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
994*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
995*e1199815SMickaël Salaün 
996*e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d3. */
997*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
998*e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
999*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1000*e1199815SMickaël Salaün 
1001*e1199815SMickaël Salaün 	/*
1002*e1199815SMickaël Salaün 	 * Now, dir_s1d3 get a new rule tied to it, only allowing
1003*e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_WRITE_FILE.  The (kernel internal) difference is
1004*e1199815SMickaël Salaün 	 * that there was no rule tied to it before.
1005*e1199815SMickaël Salaün 	 */
1006*e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
1007*e1199815SMickaël Salaün 			dir_s1d3);
1008*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1009*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1010*e1199815SMickaël Salaün 
1011*e1199815SMickaël Salaün 	/*
1012*e1199815SMickaël Salaün 	 * Same tests and results as above, except for open(dir_s1d3) which is
1013*e1199815SMickaël Salaün 	 * now denied because the new rule mask the rule previously inherited
1014*e1199815SMickaël Salaün 	 * from dir_s1d2.
1015*e1199815SMickaël Salaün 	 */
1016*e1199815SMickaël Salaün 
1017*e1199815SMickaël Salaün 	/* Same tests and results as above. */
1018*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1019*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1020*e1199815SMickaël Salaün 
1021*e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d2. */
1022*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1023*e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1024*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1025*e1199815SMickaël Salaün 
1026*e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d3. */
1027*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1028*e1199815SMickaël Salaün 	/*
1029*e1199815SMickaël Salaün 	 * Readdir of dir_s1d3 is still allowed because of the OR policy inside
1030*e1199815SMickaël Salaün 	 * the same layer.
1031*e1199815SMickaël Salaün 	 */
1032*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1033*e1199815SMickaël Salaün }
1034*e1199815SMickaël Salaün 
1035*e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_superset)
1036*e1199815SMickaël Salaün {
1037*e1199815SMickaël Salaün 	const struct rule rules[] = {
1038*e1199815SMickaël Salaün 		{
1039*e1199815SMickaël Salaün 			.path = dir_s1d3,
1040*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1041*e1199815SMickaël Salaün 		},
1042*e1199815SMickaël Salaün 		{}
1043*e1199815SMickaël Salaün 	};
1044*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1045*e1199815SMickaël Salaün 
1046*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1047*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1048*e1199815SMickaël Salaün 
1049*e1199815SMickaël Salaün 	/* Readdir access is denied for dir_s1d2. */
1050*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1051*e1199815SMickaël Salaün 	/* Readdir access is allowed for dir_s1d3. */
1052*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1053*e1199815SMickaël Salaün 	/* File access is allowed for file1_s1d3. */
1054*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1055*e1199815SMickaël Salaün 
1056*e1199815SMickaël Salaün 	/* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */
1057*e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_READ_FILE |
1058*e1199815SMickaël Salaün 			LANDLOCK_ACCESS_FS_READ_DIR, dir_s1d2);
1059*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1060*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1061*e1199815SMickaël Salaün 
1062*e1199815SMickaël Salaün 	/* Readdir access is still denied for dir_s1d2. */
1063*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1064*e1199815SMickaël Salaün 	/* Readdir access is still allowed for dir_s1d3. */
1065*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1066*e1199815SMickaël Salaün 	/* File access is still allowed for file1_s1d3. */
1067*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1068*e1199815SMickaël Salaün }
1069*e1199815SMickaël Salaün 
1070*e1199815SMickaël Salaün TEST_F_FORK(layout1, max_layers)
1071*e1199815SMickaël Salaün {
1072*e1199815SMickaël Salaün 	int i, err;
1073*e1199815SMickaël Salaün 	const struct rule rules[] = {
1074*e1199815SMickaël Salaün 		{
1075*e1199815SMickaël Salaün 			.path = dir_s1d2,
1076*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1077*e1199815SMickaël Salaün 		},
1078*e1199815SMickaël Salaün 		{}
1079*e1199815SMickaël Salaün 	};
1080*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1081*e1199815SMickaël Salaün 
1082*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1083*e1199815SMickaël Salaün 	for (i = 0; i < 64; i++)
1084*e1199815SMickaël Salaün 		enforce_ruleset(_metadata, ruleset_fd);
1085*e1199815SMickaël Salaün 
1086*e1199815SMickaël Salaün 	for (i = 0; i < 2; i++) {
1087*e1199815SMickaël Salaün 		err = landlock_restrict_self(ruleset_fd, 0);
1088*e1199815SMickaël Salaün 		ASSERT_EQ(-1, err);
1089*e1199815SMickaël Salaün 		ASSERT_EQ(E2BIG, errno);
1090*e1199815SMickaël Salaün 	}
1091*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1092*e1199815SMickaël Salaün }
1093*e1199815SMickaël Salaün 
1094*e1199815SMickaël Salaün TEST_F_FORK(layout1, empty_or_same_ruleset)
1095*e1199815SMickaël Salaün {
1096*e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {};
1097*e1199815SMickaël Salaün 	int ruleset_fd;
1098*e1199815SMickaël Salaün 
1099*e1199815SMickaël Salaün 	/* Tests empty handled_access_fs. */
1100*e1199815SMickaël Salaün 	ruleset_fd = landlock_create_ruleset(&ruleset_attr,
1101*e1199815SMickaël Salaün 			sizeof(ruleset_attr), 0);
1102*e1199815SMickaël Salaün 	ASSERT_LE(-1, ruleset_fd);
1103*e1199815SMickaël Salaün 	ASSERT_EQ(ENOMSG, errno);
1104*e1199815SMickaël Salaün 
1105*e1199815SMickaël Salaün 	/* Enforces policy which deny read access to all files. */
1106*e1199815SMickaël Salaün 	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE;
1107*e1199815SMickaël Salaün 	ruleset_fd = landlock_create_ruleset(&ruleset_attr,
1108*e1199815SMickaël Salaün 			sizeof(ruleset_attr), 0);
1109*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1110*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1111*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1112*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1113*e1199815SMickaël Salaün 
1114*e1199815SMickaël Salaün 	/* Nests a policy which deny read access to all directories. */
1115*e1199815SMickaël Salaün 	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR;
1116*e1199815SMickaël Salaün 	ruleset_fd = landlock_create_ruleset(&ruleset_attr,
1117*e1199815SMickaël Salaün 			sizeof(ruleset_attr), 0);
1118*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1119*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1120*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1121*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1122*e1199815SMickaël Salaün 
1123*e1199815SMickaël Salaün 	/* Enforces a second time with the same ruleset. */
1124*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1125*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1126*e1199815SMickaël Salaün }
1127*e1199815SMickaël Salaün 
1128*e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_on_mountpoint)
1129*e1199815SMickaël Salaün {
1130*e1199815SMickaël Salaün 	const struct rule rules[] = {
1131*e1199815SMickaël Salaün 		{
1132*e1199815SMickaël Salaün 			.path = dir_s1d1,
1133*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1134*e1199815SMickaël Salaün 		},
1135*e1199815SMickaël Salaün 		{
1136*e1199815SMickaël Salaün 			/* dir_s3d2 is a mount point. */
1137*e1199815SMickaël Salaün 			.path = dir_s3d2,
1138*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1139*e1199815SMickaël Salaün 		},
1140*e1199815SMickaël Salaün 		{}
1141*e1199815SMickaël Salaün 	};
1142*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1143*e1199815SMickaël Salaün 
1144*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1145*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1146*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1147*e1199815SMickaël Salaün 
1148*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1149*e1199815SMickaël Salaün 
1150*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1151*e1199815SMickaël Salaün 
1152*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY));
1153*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1154*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1155*e1199815SMickaël Salaün }
1156*e1199815SMickaël Salaün 
1157*e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_mountpoint)
1158*e1199815SMickaël Salaün {
1159*e1199815SMickaël Salaün 	const struct rule rules[] = {
1160*e1199815SMickaël Salaün 		{
1161*e1199815SMickaël Salaün 			.path = dir_s1d1,
1162*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1163*e1199815SMickaël Salaün 		},
1164*e1199815SMickaël Salaün 		{
1165*e1199815SMickaël Salaün 			/* dir_s3d2 is a mount point. */
1166*e1199815SMickaël Salaün 			.path = dir_s3d1,
1167*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1168*e1199815SMickaël Salaün 		},
1169*e1199815SMickaël Salaün 		{}
1170*e1199815SMickaël Salaün 	};
1171*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1172*e1199815SMickaël Salaün 
1173*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1174*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1175*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1176*e1199815SMickaël Salaün 
1177*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1178*e1199815SMickaël Salaün 
1179*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1180*e1199815SMickaël Salaün 
1181*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
1182*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1183*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1184*e1199815SMickaël Salaün }
1185*e1199815SMickaël Salaün 
1186*e1199815SMickaël Salaün /*
1187*e1199815SMickaël Salaün  * This test verifies that we can apply a landlock rule on the root directory
1188*e1199815SMickaël Salaün  * (which might require special handling).
1189*e1199815SMickaël Salaün  */
1190*e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_allow_then_deny)
1191*e1199815SMickaël Salaün {
1192*e1199815SMickaël Salaün 	struct rule rules[] = {
1193*e1199815SMickaël Salaün 		{
1194*e1199815SMickaël Salaün 			.path = "/",
1195*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1196*e1199815SMickaël Salaün 		},
1197*e1199815SMickaël Salaün 		{}
1198*e1199815SMickaël Salaün 	};
1199*e1199815SMickaël Salaün 	int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1200*e1199815SMickaël Salaün 
1201*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1202*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1203*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1204*e1199815SMickaël Salaün 
1205*e1199815SMickaël Salaün 	/* Checks allowed access. */
1206*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/", O_RDONLY));
1207*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1208*e1199815SMickaël Salaün 
1209*e1199815SMickaël Salaün 	rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE;
1210*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1211*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1212*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1213*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1214*e1199815SMickaël Salaün 
1215*e1199815SMickaël Salaün 	/* Checks denied access (on a directory). */
1216*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1217*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1218*e1199815SMickaël Salaün }
1219*e1199815SMickaël Salaün 
1220*e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_deny)
1221*e1199815SMickaël Salaün {
1222*e1199815SMickaël Salaün 	const struct rule rules[] = {
1223*e1199815SMickaël Salaün 		{
1224*e1199815SMickaël Salaün 			.path = "/",
1225*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1226*e1199815SMickaël Salaün 		},
1227*e1199815SMickaël Salaün 		{}
1228*e1199815SMickaël Salaün 	};
1229*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1230*e1199815SMickaël Salaün 
1231*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1232*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1233*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1234*e1199815SMickaël Salaün 
1235*e1199815SMickaël Salaün 	/* Checks denied access (on a directory). */
1236*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1237*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1238*e1199815SMickaël Salaün }
1239*e1199815SMickaël Salaün 
1240*e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_inside_mount_ns)
1241*e1199815SMickaël Salaün {
1242*e1199815SMickaël Salaün 	const struct rule rules[] = {
1243*e1199815SMickaël Salaün 		{
1244*e1199815SMickaël Salaün 			.path = "s3d3",
1245*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1246*e1199815SMickaël Salaün 		},
1247*e1199815SMickaël Salaün 		{}
1248*e1199815SMickaël Salaün 	};
1249*e1199815SMickaël Salaün 	int ruleset_fd;
1250*e1199815SMickaël Salaün 
1251*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
1252*e1199815SMickaël Salaün 	ASSERT_EQ(0, syscall(SYS_pivot_root, dir_s3d2, dir_s3d3)) {
1253*e1199815SMickaël Salaün 		TH_LOG("Failed to pivot root: %s", strerror(errno));
1254*e1199815SMickaël Salaün 	};
1255*e1199815SMickaël Salaün 	ASSERT_EQ(0, chdir("/"));
1256*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1257*e1199815SMickaël Salaün 
1258*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1259*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1260*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1261*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1262*e1199815SMickaël Salaün 
1263*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("s3d3", O_RDONLY));
1264*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1265*e1199815SMickaël Salaün }
1266*e1199815SMickaël Salaün 
1267*e1199815SMickaël Salaün TEST_F_FORK(layout1, mount_and_pivot)
1268*e1199815SMickaël Salaün {
1269*e1199815SMickaël Salaün 	const struct rule rules[] = {
1270*e1199815SMickaël Salaün 		{
1271*e1199815SMickaël Salaün 			.path = dir_s3d2,
1272*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1273*e1199815SMickaël Salaün 		},
1274*e1199815SMickaël Salaün 		{}
1275*e1199815SMickaël Salaün 	};
1276*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1277*e1199815SMickaël Salaün 
1278*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1279*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1280*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1281*e1199815SMickaël Salaün 
1282*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
1283*e1199815SMickaël Salaün 	ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
1284*e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
1285*e1199815SMickaël Salaün 	ASSERT_EQ(-1, syscall(SYS_pivot_root, dir_s3d2, dir_s3d3));
1286*e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
1287*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1288*e1199815SMickaël Salaün }
1289*e1199815SMickaël Salaün 
1290*e1199815SMickaël Salaün TEST_F_FORK(layout1, move_mount)
1291*e1199815SMickaël Salaün {
1292*e1199815SMickaël Salaün 	const struct rule rules[] = {
1293*e1199815SMickaël Salaün 		{
1294*e1199815SMickaël Salaün 			.path = dir_s3d2,
1295*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1296*e1199815SMickaël Salaün 		},
1297*e1199815SMickaël Salaün 		{}
1298*e1199815SMickaël Salaün 	};
1299*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1300*e1199815SMickaël Salaün 
1301*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1302*e1199815SMickaël Salaün 
1303*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
1304*e1199815SMickaël Salaün 	ASSERT_EQ(0, syscall(SYS_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1305*e1199815SMickaël Salaün 				dir_s1d2, 0)) {
1306*e1199815SMickaël Salaün 		TH_LOG("Failed to move mount: %s", strerror(errno));
1307*e1199815SMickaël Salaün 	}
1308*e1199815SMickaël Salaün 
1309*e1199815SMickaël Salaün 	ASSERT_EQ(0, syscall(SYS_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,
1310*e1199815SMickaël Salaün 				dir_s3d2, 0));
1311*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1312*e1199815SMickaël Salaün 
1313*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1314*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1315*e1199815SMickaël Salaün 
1316*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
1317*e1199815SMickaël Salaün 	ASSERT_EQ(-1, syscall(SYS_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1318*e1199815SMickaël Salaün 				dir_s1d2, 0));
1319*e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
1320*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1321*e1199815SMickaël Salaün }
1322*e1199815SMickaël Salaün 
1323*e1199815SMickaël Salaün TEST_F_FORK(layout1, release_inodes)
1324*e1199815SMickaël Salaün {
1325*e1199815SMickaël Salaün 	const struct rule rules[] = {
1326*e1199815SMickaël Salaün 		{
1327*e1199815SMickaël Salaün 			.path = dir_s1d1,
1328*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1329*e1199815SMickaël Salaün 		},
1330*e1199815SMickaël Salaün 		{
1331*e1199815SMickaël Salaün 			.path = dir_s3d2,
1332*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1333*e1199815SMickaël Salaün 		},
1334*e1199815SMickaël Salaün 		{
1335*e1199815SMickaël Salaün 			.path = dir_s3d3,
1336*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1337*e1199815SMickaël Salaün 		},
1338*e1199815SMickaël Salaün 		{}
1339*e1199815SMickaël Salaün 	};
1340*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1341*e1199815SMickaël Salaün 
1342*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1343*e1199815SMickaël Salaün 	/* Unmount a file hierarchy while it is being used by a ruleset. */
1344*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
1345*e1199815SMickaël Salaün 	ASSERT_EQ(0, umount(dir_s3d2));
1346*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1347*e1199815SMickaël Salaün 
1348*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1349*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1350*e1199815SMickaël Salaün 
1351*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
1352*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));
1353*e1199815SMickaël Salaün 	/* This dir_s3d3 would not be allowed and does not exist anyway. */
1354*e1199815SMickaël Salaün 	ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY));
1355*e1199815SMickaël Salaün }
1356*e1199815SMickaël Salaün 
1357*e1199815SMickaël Salaün enum relative_access {
1358*e1199815SMickaël Salaün 	REL_OPEN,
1359*e1199815SMickaël Salaün 	REL_CHDIR,
1360*e1199815SMickaël Salaün 	REL_CHROOT_ONLY,
1361*e1199815SMickaël Salaün 	REL_CHROOT_CHDIR,
1362*e1199815SMickaël Salaün };
1363*e1199815SMickaël Salaün 
1364*e1199815SMickaël Salaün static void test_relative_path(struct __test_metadata *const _metadata,
1365*e1199815SMickaël Salaün 		const enum relative_access rel)
1366*e1199815SMickaël Salaün {
1367*e1199815SMickaël Salaün 	/*
1368*e1199815SMickaël Salaün 	 * Common layer to check that chroot doesn't ignore it (i.e. a chroot
1369*e1199815SMickaël Salaün 	 * is not a disconnected root directory).
1370*e1199815SMickaël Salaün 	 */
1371*e1199815SMickaël Salaün 	const struct rule layer1_base[] = {
1372*e1199815SMickaël Salaün 		{
1373*e1199815SMickaël Salaün 			.path = TMP_DIR,
1374*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1375*e1199815SMickaël Salaün 		},
1376*e1199815SMickaël Salaün 		{}
1377*e1199815SMickaël Salaün 	};
1378*e1199815SMickaël Salaün 	const struct rule layer2_subs[] = {
1379*e1199815SMickaël Salaün 		{
1380*e1199815SMickaël Salaün 			.path = dir_s1d2,
1381*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1382*e1199815SMickaël Salaün 		},
1383*e1199815SMickaël Salaün 		{
1384*e1199815SMickaël Salaün 			.path = dir_s2d2,
1385*e1199815SMickaël Salaün 			.access = ACCESS_RO,
1386*e1199815SMickaël Salaün 		},
1387*e1199815SMickaël Salaün 		{}
1388*e1199815SMickaël Salaün 	};
1389*e1199815SMickaël Salaün 	int dirfd, ruleset_fd;
1390*e1199815SMickaël Salaün 
1391*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
1392*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1393*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1394*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1395*e1199815SMickaël Salaün 
1396*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs);
1397*e1199815SMickaël Salaün 
1398*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1399*e1199815SMickaël Salaün 	switch (rel) {
1400*e1199815SMickaël Salaün 	case REL_OPEN:
1401*e1199815SMickaël Salaün 	case REL_CHDIR:
1402*e1199815SMickaël Salaün 		break;
1403*e1199815SMickaël Salaün 	case REL_CHROOT_ONLY:
1404*e1199815SMickaël Salaün 		ASSERT_EQ(0, chdir(dir_s2d2));
1405*e1199815SMickaël Salaün 		break;
1406*e1199815SMickaël Salaün 	case REL_CHROOT_CHDIR:
1407*e1199815SMickaël Salaün 		ASSERT_EQ(0, chdir(dir_s1d2));
1408*e1199815SMickaël Salaün 		break;
1409*e1199815SMickaël Salaün 	default:
1410*e1199815SMickaël Salaün 		ASSERT_TRUE(false);
1411*e1199815SMickaël Salaün 		return;
1412*e1199815SMickaël Salaün 	}
1413*e1199815SMickaël Salaün 
1414*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_CHROOT);
1415*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1416*e1199815SMickaël Salaün 
1417*e1199815SMickaël Salaün 	switch (rel) {
1418*e1199815SMickaël Salaün 	case REL_OPEN:
1419*e1199815SMickaël Salaün 		dirfd = open(dir_s1d2, O_DIRECTORY);
1420*e1199815SMickaël Salaün 		ASSERT_LE(0, dirfd);
1421*e1199815SMickaël Salaün 		break;
1422*e1199815SMickaël Salaün 	case REL_CHDIR:
1423*e1199815SMickaël Salaün 		ASSERT_EQ(0, chdir(dir_s1d2));
1424*e1199815SMickaël Salaün 		dirfd = AT_FDCWD;
1425*e1199815SMickaël Salaün 		break;
1426*e1199815SMickaël Salaün 	case REL_CHROOT_ONLY:
1427*e1199815SMickaël Salaün 		/* Do chroot into dir_s1d2 (relative to dir_s2d2). */
1428*e1199815SMickaël Salaün 		ASSERT_EQ(0, chroot("../../s1d1/s1d2")) {
1429*e1199815SMickaël Salaün 			TH_LOG("Failed to chroot: %s", strerror(errno));
1430*e1199815SMickaël Salaün 		}
1431*e1199815SMickaël Salaün 		dirfd = AT_FDCWD;
1432*e1199815SMickaël Salaün 		break;
1433*e1199815SMickaël Salaün 	case REL_CHROOT_CHDIR:
1434*e1199815SMickaël Salaün 		/* Do chroot into dir_s1d2. */
1435*e1199815SMickaël Salaün 		ASSERT_EQ(0, chroot(".")) {
1436*e1199815SMickaël Salaün 			TH_LOG("Failed to chroot: %s", strerror(errno));
1437*e1199815SMickaël Salaün 		}
1438*e1199815SMickaël Salaün 		dirfd = AT_FDCWD;
1439*e1199815SMickaël Salaün 		break;
1440*e1199815SMickaël Salaün 	}
1441*e1199815SMickaël Salaün 
1442*e1199815SMickaël Salaün 	ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES,
1443*e1199815SMickaël Salaün 			test_open_rel(dirfd, "..", O_RDONLY));
1444*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY));
1445*e1199815SMickaël Salaün 
1446*e1199815SMickaël Salaün 	if (rel == REL_CHROOT_ONLY) {
1447*e1199815SMickaël Salaün 		/* The current directory is dir_s2d2. */
1448*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY));
1449*e1199815SMickaël Salaün 	} else {
1450*e1199815SMickaël Salaün 		/* The current directory is dir_s1d2. */
1451*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY));
1452*e1199815SMickaël Salaün 	}
1453*e1199815SMickaël Salaün 
1454*e1199815SMickaël Salaün 	if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) {
1455*e1199815SMickaël Salaün 		/* Checks the root dir_s1d2. */
1456*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY));
1457*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY));
1458*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY));
1459*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY));
1460*e1199815SMickaël Salaün 	}
1461*e1199815SMickaël Salaün 
1462*e1199815SMickaël Salaün 	if (rel != REL_CHROOT_CHDIR) {
1463*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY));
1464*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY));
1465*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", O_RDONLY));
1466*e1199815SMickaël Salaün 
1467*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY));
1468*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY));
1469*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", O_RDONLY));
1470*e1199815SMickaël Salaün 	}
1471*e1199815SMickaël Salaün 
1472*e1199815SMickaël Salaün 	if (rel == REL_OPEN)
1473*e1199815SMickaël Salaün 		ASSERT_EQ(0, close(dirfd));
1474*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1475*e1199815SMickaël Salaün }
1476*e1199815SMickaël Salaün 
1477*e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_open)
1478*e1199815SMickaël Salaün {
1479*e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_OPEN);
1480*e1199815SMickaël Salaün }
1481*e1199815SMickaël Salaün 
1482*e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chdir)
1483*e1199815SMickaël Salaün {
1484*e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_CHDIR);
1485*e1199815SMickaël Salaün }
1486*e1199815SMickaël Salaün 
1487*e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_only)
1488*e1199815SMickaël Salaün {
1489*e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_CHROOT_ONLY);
1490*e1199815SMickaël Salaün }
1491*e1199815SMickaël Salaün 
1492*e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_chdir)
1493*e1199815SMickaël Salaün {
1494*e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_CHROOT_CHDIR);
1495*e1199815SMickaël Salaün }
1496*e1199815SMickaël Salaün 
1497*e1199815SMickaël Salaün static void copy_binary(struct __test_metadata *const _metadata,
1498*e1199815SMickaël Salaün 		const char *const dst_path)
1499*e1199815SMickaël Salaün {
1500*e1199815SMickaël Salaün 	int dst_fd, src_fd;
1501*e1199815SMickaël Salaün 	struct stat statbuf;
1502*e1199815SMickaël Salaün 
1503*e1199815SMickaël Salaün 	dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC);
1504*e1199815SMickaël Salaün 	ASSERT_LE(0, dst_fd) {
1505*e1199815SMickaël Salaün 		TH_LOG("Failed to open \"%s\": %s", dst_path,
1506*e1199815SMickaël Salaün 				strerror(errno));
1507*e1199815SMickaël Salaün 	}
1508*e1199815SMickaël Salaün 	src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC);
1509*e1199815SMickaël Salaün 	ASSERT_LE(0, src_fd) {
1510*e1199815SMickaël Salaün 		TH_LOG("Failed to open \"" BINARY_PATH "\": %s",
1511*e1199815SMickaël Salaün 				strerror(errno));
1512*e1199815SMickaël Salaün 	}
1513*e1199815SMickaël Salaün 	ASSERT_EQ(0, fstat(src_fd, &statbuf));
1514*e1199815SMickaël Salaün 	ASSERT_EQ(statbuf.st_size, sendfile(dst_fd, src_fd, 0,
1515*e1199815SMickaël Salaün 				statbuf.st_size));
1516*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(src_fd));
1517*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(dst_fd));
1518*e1199815SMickaël Salaün }
1519*e1199815SMickaël Salaün 
1520*e1199815SMickaël Salaün static void test_execute(struct __test_metadata *const _metadata,
1521*e1199815SMickaël Salaün 		const int err, const char *const path)
1522*e1199815SMickaël Salaün {
1523*e1199815SMickaël Salaün 	int status;
1524*e1199815SMickaël Salaün 	char *const argv[] = {(char *)path, NULL};
1525*e1199815SMickaël Salaün 	const pid_t child = fork();
1526*e1199815SMickaël Salaün 
1527*e1199815SMickaël Salaün 	ASSERT_LE(0, child);
1528*e1199815SMickaël Salaün 	if (child == 0) {
1529*e1199815SMickaël Salaün 		ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) {
1530*e1199815SMickaël Salaün 			TH_LOG("Failed to execute \"%s\": %s", path,
1531*e1199815SMickaël Salaün 					strerror(errno));
1532*e1199815SMickaël Salaün 		};
1533*e1199815SMickaël Salaün 		ASSERT_EQ(err, errno);
1534*e1199815SMickaël Salaün 		_exit(_metadata->passed ? 2 : 1);
1535*e1199815SMickaël Salaün 		return;
1536*e1199815SMickaël Salaün 	}
1537*e1199815SMickaël Salaün 	ASSERT_EQ(child, waitpid(child, &status, 0));
1538*e1199815SMickaël Salaün 	ASSERT_EQ(1, WIFEXITED(status));
1539*e1199815SMickaël Salaün 	ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) {
1540*e1199815SMickaël Salaün 		TH_LOG("Unexpected return code for \"%s\": %s", path,
1541*e1199815SMickaël Salaün 				strerror(errno));
1542*e1199815SMickaël Salaün 	};
1543*e1199815SMickaël Salaün }
1544*e1199815SMickaël Salaün 
1545*e1199815SMickaël Salaün TEST_F_FORK(layout1, execute)
1546*e1199815SMickaël Salaün {
1547*e1199815SMickaël Salaün 	const struct rule rules[] = {
1548*e1199815SMickaël Salaün 		{
1549*e1199815SMickaël Salaün 			.path = dir_s1d2,
1550*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
1551*e1199815SMickaël Salaün 		},
1552*e1199815SMickaël Salaün 		{}
1553*e1199815SMickaël Salaün 	};
1554*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access,
1555*e1199815SMickaël Salaün 			rules);
1556*e1199815SMickaël Salaün 
1557*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1558*e1199815SMickaël Salaün 	copy_binary(_metadata, file1_s1d1);
1559*e1199815SMickaël Salaün 	copy_binary(_metadata, file1_s1d2);
1560*e1199815SMickaël Salaün 	copy_binary(_metadata, file1_s1d3);
1561*e1199815SMickaël Salaün 
1562*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1563*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1564*e1199815SMickaël Salaün 
1565*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1566*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
1567*e1199815SMickaël Salaün 	test_execute(_metadata, EACCES, file1_s1d1);
1568*e1199815SMickaël Salaün 
1569*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
1570*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
1571*e1199815SMickaël Salaün 	test_execute(_metadata, 0, file1_s1d2);
1572*e1199815SMickaël Salaün 
1573*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
1574*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1575*e1199815SMickaël Salaün 	test_execute(_metadata, 0, file1_s1d3);
1576*e1199815SMickaël Salaün }
1577*e1199815SMickaël Salaün 
1578*e1199815SMickaël Salaün TEST_F_FORK(layout1, link)
1579*e1199815SMickaël Salaün {
1580*e1199815SMickaël Salaün 	const struct rule rules[] = {
1581*e1199815SMickaël Salaün 		{
1582*e1199815SMickaël Salaün 			.path = dir_s1d2,
1583*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
1584*e1199815SMickaël Salaün 		},
1585*e1199815SMickaël Salaün 		{}
1586*e1199815SMickaël Salaün 	};
1587*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access,
1588*e1199815SMickaël Salaün 			rules);
1589*e1199815SMickaël Salaün 
1590*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1591*e1199815SMickaël Salaün 
1592*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
1593*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1594*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1595*e1199815SMickaël Salaün 
1596*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1597*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1598*e1199815SMickaël Salaün 
1599*e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
1600*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1601*e1199815SMickaël Salaün 	/* Denies linking because of reparenting. */
1602*e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
1603*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1604*e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
1605*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1606*e1199815SMickaël Salaün 
1607*e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
1608*e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
1609*e1199815SMickaël Salaün }
1610*e1199815SMickaël Salaün 
1611*e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_file)
1612*e1199815SMickaël Salaün {
1613*e1199815SMickaël Salaün 	const struct rule rules[] = {
1614*e1199815SMickaël Salaün 		{
1615*e1199815SMickaël Salaün 			.path = dir_s1d3,
1616*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1617*e1199815SMickaël Salaün 		},
1618*e1199815SMickaël Salaün 		{
1619*e1199815SMickaël Salaün 			.path = dir_s2d2,
1620*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1621*e1199815SMickaël Salaün 		},
1622*e1199815SMickaël Salaün 		{}
1623*e1199815SMickaël Salaün 	};
1624*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access,
1625*e1199815SMickaël Salaün 			rules);
1626*e1199815SMickaël Salaün 
1627*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1628*e1199815SMickaël Salaün 
1629*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
1630*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1631*e1199815SMickaël Salaün 
1632*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1633*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1634*e1199815SMickaël Salaün 
1635*e1199815SMickaël Salaün 	/*
1636*e1199815SMickaël Salaün 	 * Tries to replace a file, from a directory that allows file removal,
1637*e1199815SMickaël Salaün 	 * but to a different directory (which also allows file removal).
1638*e1199815SMickaël Salaün 	 */
1639*e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3));
1640*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1641*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3,
1642*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1643*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1644*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
1645*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1646*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1647*e1199815SMickaël Salaün 
1648*e1199815SMickaël Salaün 	/*
1649*e1199815SMickaël Salaün 	 * Tries to replace a file, from a directory that denies file removal,
1650*e1199815SMickaël Salaün 	 * to a different directory (which allows file removal).
1651*e1199815SMickaël Salaün 	 */
1652*e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
1653*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1654*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3,
1655*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1656*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1657*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3,
1658*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1659*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1660*e1199815SMickaël Salaün 
1661*e1199815SMickaël Salaün 	/* Exchanges files and directories that partially allow removal. */
1662*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1,
1663*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1664*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1665*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2,
1666*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1667*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1668*e1199815SMickaël Salaün 
1669*e1199815SMickaël Salaün 	/* Renames files with different parents. */
1670*e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
1671*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1672*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1673*e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
1674*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1675*e1199815SMickaël Salaün 
1676*e1199815SMickaël Salaün 	/* Exchanges and renames files with same parent. */
1677*e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3,
1678*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1679*e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3));
1680*e1199815SMickaël Salaün 
1681*e1199815SMickaël Salaün 	/* Exchanges files and directories with same parent, twice. */
1682*e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
1683*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1684*e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
1685*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1686*e1199815SMickaël Salaün }
1687*e1199815SMickaël Salaün 
1688*e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_dir)
1689*e1199815SMickaël Salaün {
1690*e1199815SMickaël Salaün 	const struct rule rules[] = {
1691*e1199815SMickaël Salaün 		{
1692*e1199815SMickaël Salaün 			.path = dir_s1d2,
1693*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
1694*e1199815SMickaël Salaün 		},
1695*e1199815SMickaël Salaün 		{
1696*e1199815SMickaël Salaün 			.path = dir_s2d1,
1697*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
1698*e1199815SMickaël Salaün 		},
1699*e1199815SMickaël Salaün 		{}
1700*e1199815SMickaël Salaün 	};
1701*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access,
1702*e1199815SMickaël Salaün 			rules);
1703*e1199815SMickaël Salaün 
1704*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1705*e1199815SMickaël Salaün 
1706*e1199815SMickaël Salaün 	/* Empties dir_s1d3 to allow renaming. */
1707*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1708*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
1709*e1199815SMickaël Salaün 
1710*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1711*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1712*e1199815SMickaël Salaün 
1713*e1199815SMickaël Salaün 	/* Exchanges and renames directory to a different parent. */
1714*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
1715*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1716*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1717*e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3));
1718*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1719*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
1720*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1721*e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1722*e1199815SMickaël Salaün 
1723*e1199815SMickaël Salaün 	/*
1724*e1199815SMickaël Salaün 	 * Exchanges directory to the same parent, which doesn't allow
1725*e1199815SMickaël Salaün 	 * directory removal.
1726*e1199815SMickaël Salaün 	 */
1727*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1,
1728*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1729*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1730*e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2,
1731*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1732*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1733*e1199815SMickaël Salaün 
1734*e1199815SMickaël Salaün 	/*
1735*e1199815SMickaël Salaün 	 * Exchanges and renames directory to the same parent, which allows
1736*e1199815SMickaël Salaün 	 * directory removal.
1737*e1199815SMickaël Salaün 	 */
1738*e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2,
1739*e1199815SMickaël Salaün 				RENAME_EXCHANGE));
1740*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(dir_s1d3));
1741*e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
1742*e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3));
1743*e1199815SMickaël Salaün 	ASSERT_EQ(0, rmdir(dir_s1d3));
1744*e1199815SMickaël Salaün }
1745*e1199815SMickaël Salaün 
1746*e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_dir)
1747*e1199815SMickaël Salaün {
1748*e1199815SMickaël Salaün 	const struct rule rules[] = {
1749*e1199815SMickaël Salaün 		{
1750*e1199815SMickaël Salaün 			.path = dir_s1d2,
1751*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
1752*e1199815SMickaël Salaün 		},
1753*e1199815SMickaël Salaün 		{}
1754*e1199815SMickaël Salaün 	};
1755*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access,
1756*e1199815SMickaël Salaün 			rules);
1757*e1199815SMickaël Salaün 
1758*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1759*e1199815SMickaël Salaün 
1760*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
1761*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1762*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1763*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
1764*e1199815SMickaël Salaün 
1765*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1766*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1767*e1199815SMickaël Salaün 
1768*e1199815SMickaël Salaün 	ASSERT_EQ(0, rmdir(dir_s1d3));
1769*e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
1770*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));
1771*e1199815SMickaël Salaün 
1772*e1199815SMickaël Salaün 	/* dir_s1d2 itself cannot be removed. */
1773*e1199815SMickaël Salaün 	ASSERT_EQ(-1, rmdir(dir_s1d2));
1774*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1775*e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR));
1776*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1777*e1199815SMickaël Salaün 	ASSERT_EQ(-1, rmdir(dir_s1d1));
1778*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1779*e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR));
1780*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1781*e1199815SMickaël Salaün }
1782*e1199815SMickaël Salaün 
1783*e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_file)
1784*e1199815SMickaël Salaün {
1785*e1199815SMickaël Salaün 	const struct rule rules[] = {
1786*e1199815SMickaël Salaün 		{
1787*e1199815SMickaël Salaün 			.path = dir_s1d2,
1788*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1789*e1199815SMickaël Salaün 		},
1790*e1199815SMickaël Salaün 		{}
1791*e1199815SMickaël Salaün 	};
1792*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access,
1793*e1199815SMickaël Salaün 			rules);
1794*e1199815SMickaël Salaün 
1795*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1796*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1797*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1798*e1199815SMickaël Salaün 
1799*e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlink(file1_s1d1));
1800*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1801*e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0));
1802*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1803*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1804*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0));
1805*e1199815SMickaël Salaün }
1806*e1199815SMickaël Salaün 
1807*e1199815SMickaël Salaün static void test_make_file(struct __test_metadata *const _metadata,
1808*e1199815SMickaël Salaün 		const __u64 access, const mode_t mode, const dev_t dev)
1809*e1199815SMickaël Salaün {
1810*e1199815SMickaël Salaün 	const struct rule rules[] = {
1811*e1199815SMickaël Salaün 		{
1812*e1199815SMickaël Salaün 			.path = dir_s1d2,
1813*e1199815SMickaël Salaün 			.access = access,
1814*e1199815SMickaël Salaün 		},
1815*e1199815SMickaël Salaün 		{}
1816*e1199815SMickaël Salaün 	};
1817*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, access, rules);
1818*e1199815SMickaël Salaün 
1819*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1820*e1199815SMickaël Salaün 
1821*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
1822*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d1));
1823*e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) {
1824*e1199815SMickaël Salaün 		TH_LOG("Failed to make file \"%s\": %s",
1825*e1199815SMickaël Salaün 				file2_s1d1, strerror(errno));
1826*e1199815SMickaël Salaün 	};
1827*e1199815SMickaël Salaün 
1828*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1829*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
1830*e1199815SMickaël Salaün 
1831*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1832*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
1833*e1199815SMickaël Salaün 
1834*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1835*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1836*e1199815SMickaël Salaün 
1837*e1199815SMickaël Salaün 	ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev));
1838*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1839*e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
1840*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1841*e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
1842*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1843*e1199815SMickaël Salaün 
1844*e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) {
1845*e1199815SMickaël Salaün 		TH_LOG("Failed to make file \"%s\": %s",
1846*e1199815SMickaël Salaün 				file1_s1d2, strerror(errno));
1847*e1199815SMickaël Salaün 	};
1848*e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
1849*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
1850*e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
1851*e1199815SMickaël Salaün 
1852*e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev));
1853*e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
1854*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
1855*e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
1856*e1199815SMickaël Salaün }
1857*e1199815SMickaël Salaün 
1858*e1199815SMickaël Salaün TEST_F_FORK(layout1, make_char)
1859*e1199815SMickaël Salaün {
1860*e1199815SMickaël Salaün 	/* Creates a /dev/null device. */
1861*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_MKNOD);
1862*e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR,
1863*e1199815SMickaël Salaün 			makedev(1, 3));
1864*e1199815SMickaël Salaün }
1865*e1199815SMickaël Salaün 
1866*e1199815SMickaël Salaün TEST_F_FORK(layout1, make_block)
1867*e1199815SMickaël Salaün {
1868*e1199815SMickaël Salaün 	/* Creates a /dev/loop0 device. */
1869*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_MKNOD);
1870*e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK,
1871*e1199815SMickaël Salaün 			makedev(7, 0));
1872*e1199815SMickaël Salaün }
1873*e1199815SMickaël Salaün 
1874*e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_1)
1875*e1199815SMickaël Salaün {
1876*e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);
1877*e1199815SMickaël Salaün }
1878*e1199815SMickaël Salaün 
1879*e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_2)
1880*e1199815SMickaël Salaün {
1881*e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);
1882*e1199815SMickaël Salaün }
1883*e1199815SMickaël Salaün 
1884*e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sock)
1885*e1199815SMickaël Salaün {
1886*e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);
1887*e1199815SMickaël Salaün }
1888*e1199815SMickaël Salaün 
1889*e1199815SMickaël Salaün TEST_F_FORK(layout1, make_fifo)
1890*e1199815SMickaël Salaün {
1891*e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);
1892*e1199815SMickaël Salaün }
1893*e1199815SMickaël Salaün 
1894*e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sym)
1895*e1199815SMickaël Salaün {
1896*e1199815SMickaël Salaün 	const struct rule rules[] = {
1897*e1199815SMickaël Salaün 		{
1898*e1199815SMickaël Salaün 			.path = dir_s1d2,
1899*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_SYM,
1900*e1199815SMickaël Salaün 		},
1901*e1199815SMickaël Salaün 		{}
1902*e1199815SMickaël Salaün 	};
1903*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access,
1904*e1199815SMickaël Salaün 			rules);
1905*e1199815SMickaël Salaün 
1906*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1907*e1199815SMickaël Salaün 
1908*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
1909*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d1));
1910*e1199815SMickaël Salaün 	ASSERT_EQ(0, symlink("none", file2_s1d1));
1911*e1199815SMickaël Salaün 
1912*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1913*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
1914*e1199815SMickaël Salaün 
1915*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1916*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
1917*e1199815SMickaël Salaün 
1918*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1919*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1920*e1199815SMickaël Salaün 
1921*e1199815SMickaël Salaün 	ASSERT_EQ(-1, symlink("none", file1_s1d1));
1922*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1923*e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
1924*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1925*e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
1926*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1927*e1199815SMickaël Salaün 
1928*e1199815SMickaël Salaün 	ASSERT_EQ(0, symlink("none", file1_s1d2));
1929*e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
1930*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
1931*e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
1932*e1199815SMickaël Salaün 
1933*e1199815SMickaël Salaün 	ASSERT_EQ(0, symlink("none", file1_s1d3));
1934*e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
1935*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
1936*e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
1937*e1199815SMickaël Salaün }
1938*e1199815SMickaël Salaün 
1939*e1199815SMickaël Salaün TEST_F_FORK(layout1, make_dir)
1940*e1199815SMickaël Salaün {
1941*e1199815SMickaël Salaün 	const struct rule rules[] = {
1942*e1199815SMickaël Salaün 		{
1943*e1199815SMickaël Salaün 			.path = dir_s1d2,
1944*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_DIR,
1945*e1199815SMickaël Salaün 		},
1946*e1199815SMickaël Salaün 		{}
1947*e1199815SMickaël Salaün 	};
1948*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access,
1949*e1199815SMickaël Salaün 			rules);
1950*e1199815SMickaël Salaün 
1951*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1952*e1199815SMickaël Salaün 
1953*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
1954*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1955*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1956*e1199815SMickaël Salaün 
1957*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1958*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1959*e1199815SMickaël Salaün 
1960*e1199815SMickaël Salaün 	/* Uses file_* as directory names. */
1961*e1199815SMickaël Salaün 	ASSERT_EQ(-1, mkdir(file1_s1d1, 0700));
1962*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1963*e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
1964*e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(file1_s1d3, 0700));
1965*e1199815SMickaël Salaün }
1966*e1199815SMickaël Salaün 
1967*e1199815SMickaël Salaün static int open_proc_fd(struct __test_metadata *const _metadata, const int fd,
1968*e1199815SMickaël Salaün 		const int open_flags)
1969*e1199815SMickaël Salaün {
1970*e1199815SMickaël Salaün 	static const char path_template[] = "/proc/self/fd/%d";
1971*e1199815SMickaël Salaün 	char procfd_path[sizeof(path_template) + 10];
1972*e1199815SMickaël Salaün 	const int procfd_path_size = snprintf(procfd_path, sizeof(procfd_path),
1973*e1199815SMickaël Salaün 			path_template, fd);
1974*e1199815SMickaël Salaün 
1975*e1199815SMickaël Salaün 	ASSERT_LT(procfd_path_size, sizeof(procfd_path));
1976*e1199815SMickaël Salaün 	return open(procfd_path, open_flags);
1977*e1199815SMickaël Salaün }
1978*e1199815SMickaël Salaün 
1979*e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_unlinked_file)
1980*e1199815SMickaël Salaün {
1981*e1199815SMickaël Salaün 	const struct rule rules[] = {
1982*e1199815SMickaël Salaün 		{
1983*e1199815SMickaël Salaün 			.path = file1_s1d2,
1984*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1985*e1199815SMickaël Salaün 		},
1986*e1199815SMickaël Salaün 		{}
1987*e1199815SMickaël Salaün 	};
1988*e1199815SMickaël Salaün 	int reg_fd, proc_fd;
1989*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata,
1990*e1199815SMickaël Salaün 			LANDLOCK_ACCESS_FS_READ_FILE |
1991*e1199815SMickaël Salaün 			LANDLOCK_ACCESS_FS_WRITE_FILE, rules);
1992*e1199815SMickaël Salaün 
1993*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1994*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1995*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1996*e1199815SMickaël Salaün 
1997*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
1998*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
1999*e1199815SMickaël Salaün 	reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC);
2000*e1199815SMickaël Salaün 	ASSERT_LE(0, reg_fd);
2001*e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2002*e1199815SMickaël Salaün 
2003*e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC);
2004*e1199815SMickaël Salaün 	ASSERT_LE(0, proc_fd);
2005*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(proc_fd));
2006*e1199815SMickaël Salaün 
2007*e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC);
2008*e1199815SMickaël Salaün 	ASSERT_EQ(-1, proc_fd) {
2009*e1199815SMickaël Salaün 		TH_LOG("Successfully opened /proc/self/fd/%d: %s",
2010*e1199815SMickaël Salaün 				reg_fd, strerror(errno));
2011*e1199815SMickaël Salaün 	}
2012*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2013*e1199815SMickaël Salaün 
2014*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(reg_fd));
2015*e1199815SMickaël Salaün }
2016*e1199815SMickaël Salaün 
2017*e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_pipe)
2018*e1199815SMickaël Salaün {
2019*e1199815SMickaël Salaün 	int proc_fd;
2020*e1199815SMickaël Salaün 	int pipe_fds[2];
2021*e1199815SMickaël Salaün 	char buf = '\0';
2022*e1199815SMickaël Salaün 	const struct rule rules[] = {
2023*e1199815SMickaël Salaün 		{
2024*e1199815SMickaël Salaün 			.path = dir_s1d2,
2025*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
2026*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
2027*e1199815SMickaël Salaün 		},
2028*e1199815SMickaël Salaün 		{}
2029*e1199815SMickaël Salaün 	};
2030*e1199815SMickaël Salaün 	/* Limits read and write access to files tied to the filesystem. */
2031*e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, rules[0].access,
2032*e1199815SMickaël Salaün 			rules);
2033*e1199815SMickaël Salaün 
2034*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2035*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2036*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2037*e1199815SMickaël Salaün 
2038*e1199815SMickaël Salaün 	/* Checks enforcement for normal files. */
2039*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
2040*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
2041*e1199815SMickaël Salaün 
2042*e1199815SMickaël Salaün 	/* Checks access to pipes through FD. */
2043*e1199815SMickaël Salaün 	ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC));
2044*e1199815SMickaël Salaün 	ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) {
2045*e1199815SMickaël Salaün 		TH_LOG("Failed to write in pipe: %s", strerror(errno));
2046*e1199815SMickaël Salaün 	}
2047*e1199815SMickaël Salaün 	ASSERT_EQ(1, read(pipe_fds[0], &buf, 1));
2048*e1199815SMickaël Salaün 	ASSERT_EQ('.', buf);
2049*e1199815SMickaël Salaün 
2050*e1199815SMickaël Salaün 	/* Checks write access to pipe through /proc/self/fd . */
2051*e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC);
2052*e1199815SMickaël Salaün 	ASSERT_LE(0, proc_fd);
2053*e1199815SMickaël Salaün 	ASSERT_EQ(1, write(proc_fd, ".", 1)) {
2054*e1199815SMickaël Salaün 		TH_LOG("Failed to write through /proc/self/fd/%d: %s",
2055*e1199815SMickaël Salaün 				pipe_fds[1], strerror(errno));
2056*e1199815SMickaël Salaün 	}
2057*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(proc_fd));
2058*e1199815SMickaël Salaün 
2059*e1199815SMickaël Salaün 	/* Checks read access to pipe through /proc/self/fd . */
2060*e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC);
2061*e1199815SMickaël Salaün 	ASSERT_LE(0, proc_fd);
2062*e1199815SMickaël Salaün 	buf = '\0';
2063*e1199815SMickaël Salaün 	ASSERT_EQ(1, read(proc_fd, &buf, 1)) {
2064*e1199815SMickaël Salaün 		TH_LOG("Failed to read through /proc/self/fd/%d: %s",
2065*e1199815SMickaël Salaün 				pipe_fds[1], strerror(errno));
2066*e1199815SMickaël Salaün 	}
2067*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(proc_fd));
2068*e1199815SMickaël Salaün 
2069*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(pipe_fds[0]));
2070*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(pipe_fds[1]));
2071*e1199815SMickaël Salaün }
2072*e1199815SMickaël Salaün 
2073*e1199815SMickaël Salaün FIXTURE(layout1_bind) {
2074*e1199815SMickaël Salaün };
2075*e1199815SMickaël Salaün 
2076*e1199815SMickaël Salaün FIXTURE_SETUP(layout1_bind)
2077*e1199815SMickaël Salaün {
2078*e1199815SMickaël Salaün 	prepare_layout(_metadata);
2079*e1199815SMickaël Salaün 
2080*e1199815SMickaël Salaün 	create_layout1(_metadata);
2081*e1199815SMickaël Salaün 
2082*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
2083*e1199815SMickaël Salaün 	ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
2084*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
2085*e1199815SMickaël Salaün }
2086*e1199815SMickaël Salaün 
2087*e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1_bind)
2088*e1199815SMickaël Salaün {
2089*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
2090*e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(dir_s2d2));
2091*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
2092*e1199815SMickaël Salaün 
2093*e1199815SMickaël Salaün 	remove_layout1(_metadata);
2094*e1199815SMickaël Salaün 
2095*e1199815SMickaël Salaün 	cleanup_layout(_metadata);
2096*e1199815SMickaël Salaün }
2097*e1199815SMickaël Salaün 
2098*e1199815SMickaël Salaün static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";
2099*e1199815SMickaël Salaün static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";
2100*e1199815SMickaël Salaün 
2101*e1199815SMickaël Salaün /*
2102*e1199815SMickaël Salaün  * layout1_bind hierarchy:
2103*e1199815SMickaël Salaün  *
2104*e1199815SMickaël Salaün  * tmp
2105*e1199815SMickaël Salaün  * ├── s1d1
2106*e1199815SMickaël Salaün  * │   ├── f1
2107*e1199815SMickaël Salaün  * │   ├── f2
2108*e1199815SMickaël Salaün  * │   └── s1d2
2109*e1199815SMickaël Salaün  * │       ├── f1
2110*e1199815SMickaël Salaün  * │       ├── f2
2111*e1199815SMickaël Salaün  * │       └── s1d3
2112*e1199815SMickaël Salaün  * │           ├── f1
2113*e1199815SMickaël Salaün  * │           └── f2
2114*e1199815SMickaël Salaün  * ├── s2d1
2115*e1199815SMickaël Salaün  * │   ├── f1
2116*e1199815SMickaël Salaün  * │   └── s2d2
2117*e1199815SMickaël Salaün  * │       ├── f1
2118*e1199815SMickaël Salaün  * │       ├── f2
2119*e1199815SMickaël Salaün  * │       └── s1d3
2120*e1199815SMickaël Salaün  * │           ├── f1
2121*e1199815SMickaël Salaün  * │           └── f2
2122*e1199815SMickaël Salaün  * └── s3d1
2123*e1199815SMickaël Salaün  *     └── s3d2
2124*e1199815SMickaël Salaün  *         └── s3d3
2125*e1199815SMickaël Salaün  */
2126*e1199815SMickaël Salaün 
2127*e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, no_restriction)
2128*e1199815SMickaël Salaün {
2129*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
2130*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
2131*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
2132*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
2133*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
2134*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
2135*e1199815SMickaël Salaün 
2136*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
2137*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
2138*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
2139*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
2140*e1199815SMickaël Salaün 	ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY));
2141*e1199815SMickaël Salaün 	ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY));
2142*e1199815SMickaël Salaün 
2143*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY));
2144*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
2145*e1199815SMickaël Salaün 
2146*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
2147*e1199815SMickaël Salaün }
2148*e1199815SMickaël Salaün 
2149*e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, same_content_same_file)
2150*e1199815SMickaël Salaün {
2151*e1199815SMickaël Salaün 	/*
2152*e1199815SMickaël Salaün 	 * Sets access right on parent directories of both source and
2153*e1199815SMickaël Salaün 	 * destination mount points.
2154*e1199815SMickaël Salaün 	 */
2155*e1199815SMickaël Salaün 	const struct rule layer1_parent[] = {
2156*e1199815SMickaël Salaün 		{
2157*e1199815SMickaël Salaün 			.path = dir_s1d1,
2158*e1199815SMickaël Salaün 			.access = ACCESS_RO,
2159*e1199815SMickaël Salaün 		},
2160*e1199815SMickaël Salaün 		{
2161*e1199815SMickaël Salaün 			.path = dir_s2d1,
2162*e1199815SMickaël Salaün 			.access = ACCESS_RW,
2163*e1199815SMickaël Salaün 		},
2164*e1199815SMickaël Salaün 		{}
2165*e1199815SMickaël Salaün 	};
2166*e1199815SMickaël Salaün 	/*
2167*e1199815SMickaël Salaün 	 * Sets access rights on the same bind-mounted directories.  The result
2168*e1199815SMickaël Salaün 	 * should be ACCESS_RW for both directories, but not both hierarchies
2169*e1199815SMickaël Salaün 	 * because of the first layer.
2170*e1199815SMickaël Salaün 	 */
2171*e1199815SMickaël Salaün 	const struct rule layer2_mount_point[] = {
2172*e1199815SMickaël Salaün 		{
2173*e1199815SMickaël Salaün 			.path = dir_s1d2,
2174*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2175*e1199815SMickaël Salaün 		},
2176*e1199815SMickaël Salaün 		{
2177*e1199815SMickaël Salaün 			.path = dir_s2d2,
2178*e1199815SMickaël Salaün 			.access = ACCESS_RW,
2179*e1199815SMickaël Salaün 		},
2180*e1199815SMickaël Salaün 		{}
2181*e1199815SMickaël Salaün 	};
2182*e1199815SMickaël Salaün 	/* Only allow read-access to the s1d3 hierarchies. */
2183*e1199815SMickaël Salaün 	const struct rule layer3_source[] = {
2184*e1199815SMickaël Salaün 		{
2185*e1199815SMickaël Salaün 			.path = dir_s1d3,
2186*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2187*e1199815SMickaël Salaün 		},
2188*e1199815SMickaël Salaün 		{}
2189*e1199815SMickaël Salaün 	};
2190*e1199815SMickaël Salaün 	/* Removes all access rights. */
2191*e1199815SMickaël Salaün 	const struct rule layer4_destination[] = {
2192*e1199815SMickaël Salaün 		{
2193*e1199815SMickaël Salaün 			.path = bind_file1_s1d3,
2194*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
2195*e1199815SMickaël Salaün 		},
2196*e1199815SMickaël Salaün 		{}
2197*e1199815SMickaël Salaün 	};
2198*e1199815SMickaël Salaün 	int ruleset_fd;
2199*e1199815SMickaël Salaün 
2200*e1199815SMickaël Salaün 	/* Sets rules for the parent directories. */
2201*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent);
2202*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2203*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2204*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2205*e1199815SMickaël Salaün 
2206*e1199815SMickaël Salaün 	/* Checks source hierarchy. */
2207*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
2208*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
2209*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
2210*e1199815SMickaël Salaün 
2211*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
2212*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
2213*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
2214*e1199815SMickaël Salaün 
2215*e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
2216*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR));
2217*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
2218*e1199815SMickaël Salaün 
2219*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
2220*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
2221*e1199815SMickaël Salaün 
2222*e1199815SMickaël Salaün 	/* Sets rules for the mount points. */
2223*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point);
2224*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2225*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2226*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2227*e1199815SMickaël Salaün 
2228*e1199815SMickaël Salaün 	/* Checks source hierarchy. */
2229*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
2230*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
2231*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
2232*e1199815SMickaël Salaün 
2233*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
2234*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
2235*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
2236*e1199815SMickaël Salaün 
2237*e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
2238*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY));
2239*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY));
2240*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
2241*e1199815SMickaël Salaün 
2242*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
2243*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
2244*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
2245*e1199815SMickaël Salaün 
2246*e1199815SMickaël Salaün 	/* Sets a (shared) rule only on the source. */
2247*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source);
2248*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2249*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2250*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2251*e1199815SMickaël Salaün 
2252*e1199815SMickaël Salaün 	/* Checks source hierarchy. */
2253*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
2254*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
2255*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
2256*e1199815SMickaël Salaün 
2257*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
2258*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
2259*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
2260*e1199815SMickaël Salaün 
2261*e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
2262*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY));
2263*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY));
2264*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
2265*e1199815SMickaël Salaün 
2266*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
2267*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
2268*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
2269*e1199815SMickaël Salaün 
2270*e1199815SMickaël Salaün 	/* Sets a (shared) rule only on the destination. */
2271*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination);
2272*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2273*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2274*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2275*e1199815SMickaël Salaün 
2276*e1199815SMickaël Salaün 	/* Checks source hierarchy. */
2277*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
2278*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
2279*e1199815SMickaël Salaün 
2280*e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
2281*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY));
2282*e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
2283*e1199815SMickaël Salaün }
2284*e1199815SMickaël Salaün 
2285*e1199815SMickaël Salaün #define LOWER_BASE	TMP_DIR "/lower"
2286*e1199815SMickaël Salaün #define LOWER_DATA	LOWER_BASE "/data"
2287*e1199815SMickaël Salaün static const char lower_fl1[] = LOWER_DATA "/fl1";
2288*e1199815SMickaël Salaün static const char lower_dl1[] = LOWER_DATA "/dl1";
2289*e1199815SMickaël Salaün static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";
2290*e1199815SMickaël Salaün static const char lower_fo1[] = LOWER_DATA "/fo1";
2291*e1199815SMickaël Salaün static const char lower_do1[] = LOWER_DATA "/do1";
2292*e1199815SMickaël Salaün static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";
2293*e1199815SMickaël Salaün static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";
2294*e1199815SMickaël Salaün 
2295*e1199815SMickaël Salaün static const char (*lower_base_files[])[] = {
2296*e1199815SMickaël Salaün 	&lower_fl1,
2297*e1199815SMickaël Salaün 	&lower_fo1,
2298*e1199815SMickaël Salaün 	NULL
2299*e1199815SMickaël Salaün };
2300*e1199815SMickaël Salaün static const char (*lower_base_directories[])[] = {
2301*e1199815SMickaël Salaün 	&lower_dl1,
2302*e1199815SMickaël Salaün 	&lower_do1,
2303*e1199815SMickaël Salaün 	NULL
2304*e1199815SMickaël Salaün };
2305*e1199815SMickaël Salaün static const char (*lower_sub_files[])[] = {
2306*e1199815SMickaël Salaün 	&lower_dl1_fl2,
2307*e1199815SMickaël Salaün 	&lower_do1_fo2,
2308*e1199815SMickaël Salaün 	&lower_do1_fl3,
2309*e1199815SMickaël Salaün 	NULL
2310*e1199815SMickaël Salaün };
2311*e1199815SMickaël Salaün 
2312*e1199815SMickaël Salaün #define UPPER_BASE	TMP_DIR "/upper"
2313*e1199815SMickaël Salaün #define UPPER_DATA	UPPER_BASE "/data"
2314*e1199815SMickaël Salaün #define UPPER_WORK	UPPER_BASE "/work"
2315*e1199815SMickaël Salaün static const char upper_fu1[] = UPPER_DATA "/fu1";
2316*e1199815SMickaël Salaün static const char upper_du1[] = UPPER_DATA "/du1";
2317*e1199815SMickaël Salaün static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";
2318*e1199815SMickaël Salaün static const char upper_fo1[] = UPPER_DATA "/fo1";
2319*e1199815SMickaël Salaün static const char upper_do1[] = UPPER_DATA "/do1";
2320*e1199815SMickaël Salaün static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";
2321*e1199815SMickaël Salaün static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";
2322*e1199815SMickaël Salaün 
2323*e1199815SMickaël Salaün static const char (*upper_base_files[])[] = {
2324*e1199815SMickaël Salaün 	&upper_fu1,
2325*e1199815SMickaël Salaün 	&upper_fo1,
2326*e1199815SMickaël Salaün 	NULL
2327*e1199815SMickaël Salaün };
2328*e1199815SMickaël Salaün static const char (*upper_base_directories[])[] = {
2329*e1199815SMickaël Salaün 	&upper_du1,
2330*e1199815SMickaël Salaün 	&upper_do1,
2331*e1199815SMickaël Salaün 	NULL
2332*e1199815SMickaël Salaün };
2333*e1199815SMickaël Salaün static const char (*upper_sub_files[])[] = {
2334*e1199815SMickaël Salaün 	&upper_du1_fu2,
2335*e1199815SMickaël Salaün 	&upper_do1_fo2,
2336*e1199815SMickaël Salaün 	&upper_do1_fu3,
2337*e1199815SMickaël Salaün 	NULL
2338*e1199815SMickaël Salaün };
2339*e1199815SMickaël Salaün 
2340*e1199815SMickaël Salaün #define MERGE_BASE	TMP_DIR "/merge"
2341*e1199815SMickaël Salaün #define MERGE_DATA	MERGE_BASE "/data"
2342*e1199815SMickaël Salaün static const char merge_fl1[] = MERGE_DATA "/fl1";
2343*e1199815SMickaël Salaün static const char merge_dl1[] = MERGE_DATA "/dl1";
2344*e1199815SMickaël Salaün static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";
2345*e1199815SMickaël Salaün static const char merge_fu1[] = MERGE_DATA "/fu1";
2346*e1199815SMickaël Salaün static const char merge_du1[] = MERGE_DATA "/du1";
2347*e1199815SMickaël Salaün static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";
2348*e1199815SMickaël Salaün static const char merge_fo1[] = MERGE_DATA "/fo1";
2349*e1199815SMickaël Salaün static const char merge_do1[] = MERGE_DATA "/do1";
2350*e1199815SMickaël Salaün static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";
2351*e1199815SMickaël Salaün static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";
2352*e1199815SMickaël Salaün static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";
2353*e1199815SMickaël Salaün 
2354*e1199815SMickaël Salaün static const char (*merge_base_files[])[] = {
2355*e1199815SMickaël Salaün 	&merge_fl1,
2356*e1199815SMickaël Salaün 	&merge_fu1,
2357*e1199815SMickaël Salaün 	&merge_fo1,
2358*e1199815SMickaël Salaün 	NULL
2359*e1199815SMickaël Salaün };
2360*e1199815SMickaël Salaün static const char (*merge_base_directories[])[] = {
2361*e1199815SMickaël Salaün 	&merge_dl1,
2362*e1199815SMickaël Salaün 	&merge_du1,
2363*e1199815SMickaël Salaün 	&merge_do1,
2364*e1199815SMickaël Salaün 	NULL
2365*e1199815SMickaël Salaün };
2366*e1199815SMickaël Salaün static const char (*merge_sub_files[])[] = {
2367*e1199815SMickaël Salaün 	&merge_dl1_fl2,
2368*e1199815SMickaël Salaün 	&merge_du1_fu2,
2369*e1199815SMickaël Salaün 	&merge_do1_fo2,
2370*e1199815SMickaël Salaün 	&merge_do1_fl3,
2371*e1199815SMickaël Salaün 	&merge_do1_fu3,
2372*e1199815SMickaël Salaün 	NULL
2373*e1199815SMickaël Salaün };
2374*e1199815SMickaël Salaün 
2375*e1199815SMickaël Salaün /*
2376*e1199815SMickaël Salaün  * layout2_overlay hierarchy:
2377*e1199815SMickaël Salaün  *
2378*e1199815SMickaël Salaün  * tmp
2379*e1199815SMickaël Salaün  * ├── lower
2380*e1199815SMickaël Salaün  * │   └── data
2381*e1199815SMickaël Salaün  * │       ├── dl1
2382*e1199815SMickaël Salaün  * │       │   └── fl2
2383*e1199815SMickaël Salaün  * │       ├── do1
2384*e1199815SMickaël Salaün  * │       │   ├── fl3
2385*e1199815SMickaël Salaün  * │       │   └── fo2
2386*e1199815SMickaël Salaün  * │       ├── fl1
2387*e1199815SMickaël Salaün  * │       └── fo1
2388*e1199815SMickaël Salaün  * ├── merge
2389*e1199815SMickaël Salaün  * │   └── data
2390*e1199815SMickaël Salaün  * │       ├── dl1
2391*e1199815SMickaël Salaün  * │       │   └── fl2
2392*e1199815SMickaël Salaün  * │       ├── do1
2393*e1199815SMickaël Salaün  * │       │   ├── fl3
2394*e1199815SMickaël Salaün  * │       │   ├── fo2
2395*e1199815SMickaël Salaün  * │       │   └── fu3
2396*e1199815SMickaël Salaün  * │       ├── du1
2397*e1199815SMickaël Salaün  * │       │   └── fu2
2398*e1199815SMickaël Salaün  * │       ├── fl1
2399*e1199815SMickaël Salaün  * │       ├── fo1
2400*e1199815SMickaël Salaün  * │       └── fu1
2401*e1199815SMickaël Salaün  * └── upper
2402*e1199815SMickaël Salaün  *     ├── data
2403*e1199815SMickaël Salaün  *     │   ├── do1
2404*e1199815SMickaël Salaün  *     │   │   ├── fo2
2405*e1199815SMickaël Salaün  *     │   │   └── fu3
2406*e1199815SMickaël Salaün  *     │   ├── du1
2407*e1199815SMickaël Salaün  *     │   │   └── fu2
2408*e1199815SMickaël Salaün  *     │   ├── fo1
2409*e1199815SMickaël Salaün  *     │   └── fu1
2410*e1199815SMickaël Salaün  *     └── work
2411*e1199815SMickaël Salaün  *         └── work
2412*e1199815SMickaël Salaün  */
2413*e1199815SMickaël Salaün 
2414*e1199815SMickaël Salaün FIXTURE(layout2_overlay) {
2415*e1199815SMickaël Salaün };
2416*e1199815SMickaël Salaün 
2417*e1199815SMickaël Salaün FIXTURE_SETUP(layout2_overlay)
2418*e1199815SMickaël Salaün {
2419*e1199815SMickaël Salaün 	prepare_layout(_metadata);
2420*e1199815SMickaël Salaün 
2421*e1199815SMickaël Salaün 	create_directory(_metadata, LOWER_BASE);
2422*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
2423*e1199815SMickaël Salaün 	/* Creates tmpfs mount points to get deterministic overlayfs. */
2424*e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("tmp", LOWER_BASE, "tmpfs", 0, "size=4m,mode=700"));
2425*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
2426*e1199815SMickaël Salaün 	create_file(_metadata, lower_fl1);
2427*e1199815SMickaël Salaün 	create_file(_metadata, lower_dl1_fl2);
2428*e1199815SMickaël Salaün 	create_file(_metadata, lower_fo1);
2429*e1199815SMickaël Salaün 	create_file(_metadata, lower_do1_fo2);
2430*e1199815SMickaël Salaün 	create_file(_metadata, lower_do1_fl3);
2431*e1199815SMickaël Salaün 
2432*e1199815SMickaël Salaün 	create_directory(_metadata, UPPER_BASE);
2433*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
2434*e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("tmp", UPPER_BASE, "tmpfs", 0, "size=4m,mode=700"));
2435*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
2436*e1199815SMickaël Salaün 	create_file(_metadata, upper_fu1);
2437*e1199815SMickaël Salaün 	create_file(_metadata, upper_du1_fu2);
2438*e1199815SMickaël Salaün 	create_file(_metadata, upper_fo1);
2439*e1199815SMickaël Salaün 	create_file(_metadata, upper_do1_fo2);
2440*e1199815SMickaël Salaün 	create_file(_metadata, upper_do1_fu3);
2441*e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(UPPER_WORK, 0700));
2442*e1199815SMickaël Salaün 
2443*e1199815SMickaël Salaün 	create_directory(_metadata, MERGE_DATA);
2444*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
2445*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_DAC_OVERRIDE);
2446*e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0,
2447*e1199815SMickaël Salaün 				"lowerdir=" LOWER_DATA
2448*e1199815SMickaël Salaün 				",upperdir=" UPPER_DATA
2449*e1199815SMickaël Salaün 				",workdir=" UPPER_WORK));
2450*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_DAC_OVERRIDE);
2451*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
2452*e1199815SMickaël Salaün }
2453*e1199815SMickaël Salaün 
2454*e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout2_overlay)
2455*e1199815SMickaël Salaün {
2456*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_do1_fl3));
2457*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_dl1_fl2));
2458*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_fl1));
2459*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_do1_fo2));
2460*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_fo1));
2461*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
2462*e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(LOWER_BASE));
2463*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
2464*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(LOWER_BASE));
2465*e1199815SMickaël Salaün 
2466*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_do1_fu3));
2467*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_du1_fu2));
2468*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_fu1));
2469*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_do1_fo2));
2470*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_fo1));
2471*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(UPPER_WORK "/work"));
2472*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
2473*e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(UPPER_BASE));
2474*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
2475*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(UPPER_BASE));
2476*e1199815SMickaël Salaün 
2477*e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
2478*e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(MERGE_DATA));
2479*e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
2480*e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(MERGE_DATA));
2481*e1199815SMickaël Salaün 
2482*e1199815SMickaël Salaün 	cleanup_layout(_metadata);
2483*e1199815SMickaël Salaün }
2484*e1199815SMickaël Salaün 
2485*e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, no_restriction)
2486*e1199815SMickaël Salaün {
2487*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
2488*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
2489*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));
2490*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY));
2491*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_do1, O_RDONLY));
2492*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY));
2493*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY));
2494*e1199815SMickaël Salaün 
2495*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY));
2496*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_du1, O_RDONLY));
2497*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY));
2498*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY));
2499*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_do1, O_RDONLY));
2500*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY));
2501*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY));
2502*e1199815SMickaël Salaün 
2503*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY));
2504*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY));
2505*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY));
2506*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY));
2507*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_du1, O_RDONLY));
2508*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY));
2509*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY));
2510*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1, O_RDONLY));
2511*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY));
2512*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY));
2513*e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY));
2514*e1199815SMickaël Salaün }
2515*e1199815SMickaël Salaün 
2516*e1199815SMickaël Salaün #define for_each_path(path_list, path_entry, i)			\
2517*e1199815SMickaël Salaün 	for (i = 0, path_entry = *path_list[i]; path_list[i];	\
2518*e1199815SMickaël Salaün 			path_entry = *path_list[++i])
2519*e1199815SMickaël Salaün 
2520*e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, same_content_different_file)
2521*e1199815SMickaël Salaün {
2522*e1199815SMickaël Salaün 	/* Sets access right on parent directories of both layers. */
2523*e1199815SMickaël Salaün 	const struct rule layer1_base[] = {
2524*e1199815SMickaël Salaün 		{
2525*e1199815SMickaël Salaün 			.path = LOWER_BASE,
2526*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2527*e1199815SMickaël Salaün 		},
2528*e1199815SMickaël Salaün 		{
2529*e1199815SMickaël Salaün 			.path = UPPER_BASE,
2530*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2531*e1199815SMickaël Salaün 		},
2532*e1199815SMickaël Salaün 		{
2533*e1199815SMickaël Salaün 			.path = MERGE_BASE,
2534*e1199815SMickaël Salaün 			.access = ACCESS_RW,
2535*e1199815SMickaël Salaün 		},
2536*e1199815SMickaël Salaün 		{}
2537*e1199815SMickaël Salaün 	};
2538*e1199815SMickaël Salaün 	const struct rule layer2_data[] = {
2539*e1199815SMickaël Salaün 		{
2540*e1199815SMickaël Salaün 			.path = LOWER_DATA,
2541*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2542*e1199815SMickaël Salaün 		},
2543*e1199815SMickaël Salaün 		{
2544*e1199815SMickaël Salaün 			.path = UPPER_DATA,
2545*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2546*e1199815SMickaël Salaün 		},
2547*e1199815SMickaël Salaün 		{
2548*e1199815SMickaël Salaün 			.path = MERGE_DATA,
2549*e1199815SMickaël Salaün 			.access = ACCESS_RW,
2550*e1199815SMickaël Salaün 		},
2551*e1199815SMickaël Salaün 		{}
2552*e1199815SMickaël Salaün 	};
2553*e1199815SMickaël Salaün 	/* Sets access right on directories inside both layers. */
2554*e1199815SMickaël Salaün 	const struct rule layer3_subdirs[] = {
2555*e1199815SMickaël Salaün 		{
2556*e1199815SMickaël Salaün 			.path = lower_dl1,
2557*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2558*e1199815SMickaël Salaün 		},
2559*e1199815SMickaël Salaün 		{
2560*e1199815SMickaël Salaün 			.path = lower_do1,
2561*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2562*e1199815SMickaël Salaün 		},
2563*e1199815SMickaël Salaün 		{
2564*e1199815SMickaël Salaün 			.path = upper_du1,
2565*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2566*e1199815SMickaël Salaün 		},
2567*e1199815SMickaël Salaün 		{
2568*e1199815SMickaël Salaün 			.path = upper_do1,
2569*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2570*e1199815SMickaël Salaün 		},
2571*e1199815SMickaël Salaün 		{
2572*e1199815SMickaël Salaün 			.path = merge_dl1,
2573*e1199815SMickaël Salaün 			.access = ACCESS_RW,
2574*e1199815SMickaël Salaün 		},
2575*e1199815SMickaël Salaün 		{
2576*e1199815SMickaël Salaün 			.path = merge_du1,
2577*e1199815SMickaël Salaün 			.access = ACCESS_RW,
2578*e1199815SMickaël Salaün 		},
2579*e1199815SMickaël Salaün 		{
2580*e1199815SMickaël Salaün 			.path = merge_do1,
2581*e1199815SMickaël Salaün 			.access = ACCESS_RW,
2582*e1199815SMickaël Salaün 		},
2583*e1199815SMickaël Salaün 		{}
2584*e1199815SMickaël Salaün 	};
2585*e1199815SMickaël Salaün 	/* Tighten access rights to the files. */
2586*e1199815SMickaël Salaün 	const struct rule layer4_files[] = {
2587*e1199815SMickaël Salaün 		{
2588*e1199815SMickaël Salaün 			.path = lower_dl1_fl2,
2589*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2590*e1199815SMickaël Salaün 		},
2591*e1199815SMickaël Salaün 		{
2592*e1199815SMickaël Salaün 			.path = lower_do1_fo2,
2593*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2594*e1199815SMickaël Salaün 		},
2595*e1199815SMickaël Salaün 		{
2596*e1199815SMickaël Salaün 			.path = lower_do1_fl3,
2597*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2598*e1199815SMickaël Salaün 		},
2599*e1199815SMickaël Salaün 		{
2600*e1199815SMickaël Salaün 			.path = upper_du1_fu2,
2601*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2602*e1199815SMickaël Salaün 		},
2603*e1199815SMickaël Salaün 		{
2604*e1199815SMickaël Salaün 			.path = upper_do1_fo2,
2605*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2606*e1199815SMickaël Salaün 		},
2607*e1199815SMickaël Salaün 		{
2608*e1199815SMickaël Salaün 			.path = upper_do1_fu3,
2609*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
2610*e1199815SMickaël Salaün 		},
2611*e1199815SMickaël Salaün 		{
2612*e1199815SMickaël Salaün 			.path = merge_dl1_fl2,
2613*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
2614*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
2615*e1199815SMickaël Salaün 		},
2616*e1199815SMickaël Salaün 		{
2617*e1199815SMickaël Salaün 			.path = merge_du1_fu2,
2618*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
2619*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
2620*e1199815SMickaël Salaün 		},
2621*e1199815SMickaël Salaün 		{
2622*e1199815SMickaël Salaün 			.path = merge_do1_fo2,
2623*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
2624*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
2625*e1199815SMickaël Salaün 		},
2626*e1199815SMickaël Salaün 		{
2627*e1199815SMickaël Salaün 			.path = merge_do1_fl3,
2628*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
2629*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
2630*e1199815SMickaël Salaün 		},
2631*e1199815SMickaël Salaün 		{
2632*e1199815SMickaël Salaün 			.path = merge_do1_fu3,
2633*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
2634*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
2635*e1199815SMickaël Salaün 		},
2636*e1199815SMickaël Salaün 		{}
2637*e1199815SMickaël Salaün 	};
2638*e1199815SMickaël Salaün 	const struct rule layer5_merge_only[] = {
2639*e1199815SMickaël Salaün 		{
2640*e1199815SMickaël Salaün 			.path = MERGE_DATA,
2641*e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
2642*e1199815SMickaël Salaün 				LANDLOCK_ACCESS_FS_WRITE_FILE,
2643*e1199815SMickaël Salaün 		},
2644*e1199815SMickaël Salaün 		{}
2645*e1199815SMickaël Salaün 	};
2646*e1199815SMickaël Salaün 	int ruleset_fd;
2647*e1199815SMickaël Salaün 	size_t i;
2648*e1199815SMickaël Salaün 	const char *path_entry;
2649*e1199815SMickaël Salaün 
2650*e1199815SMickaël Salaün 	/* Sets rules on base directories (i.e. outside overlay scope). */
2651*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
2652*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2653*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2654*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2655*e1199815SMickaël Salaün 
2656*e1199815SMickaël Salaün 	/* Checks lower layer. */
2657*e1199815SMickaël Salaün 	for_each_path(lower_base_files, path_entry, i) {
2658*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
2659*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
2660*e1199815SMickaël Salaün 	}
2661*e1199815SMickaël Salaün 	for_each_path(lower_base_directories, path_entry, i) {
2662*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY));
2663*e1199815SMickaël Salaün 	}
2664*e1199815SMickaël Salaün 	for_each_path(lower_sub_files, path_entry, i) {
2665*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
2666*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
2667*e1199815SMickaël Salaün 	}
2668*e1199815SMickaël Salaün 	/* Checks upper layer. */
2669*e1199815SMickaël Salaün 	for_each_path(upper_base_files, path_entry, i) {
2670*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
2671*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
2672*e1199815SMickaël Salaün 	}
2673*e1199815SMickaël Salaün 	for_each_path(upper_base_directories, path_entry, i) {
2674*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY));
2675*e1199815SMickaël Salaün 	}
2676*e1199815SMickaël Salaün 	for_each_path(upper_sub_files, path_entry, i) {
2677*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
2678*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
2679*e1199815SMickaël Salaün 	}
2680*e1199815SMickaël Salaün 	/*
2681*e1199815SMickaël Salaün 	 * Checks that access rights are independent from the lower and upper
2682*e1199815SMickaël Salaün 	 * layers: write access to upper files viewed through the merge point
2683*e1199815SMickaël Salaün 	 * is still allowed, and write access to lower file viewed (and copied)
2684*e1199815SMickaël Salaün 	 * through the merge point is still allowed.
2685*e1199815SMickaël Salaün 	 */
2686*e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
2687*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
2688*e1199815SMickaël Salaün 	}
2689*e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
2690*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
2691*e1199815SMickaël Salaün 	}
2692*e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
2693*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
2694*e1199815SMickaël Salaün 	}
2695*e1199815SMickaël Salaün 
2696*e1199815SMickaël Salaün 	/* Sets rules on data directories (i.e. inside overlay scope). */
2697*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data);
2698*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2699*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2700*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2701*e1199815SMickaël Salaün 
2702*e1199815SMickaël Salaün 	/* Checks merge. */
2703*e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
2704*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
2705*e1199815SMickaël Salaün 	}
2706*e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
2707*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
2708*e1199815SMickaël Salaün 	}
2709*e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
2710*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
2711*e1199815SMickaël Salaün 	}
2712*e1199815SMickaël Salaün 
2713*e1199815SMickaël Salaün 	/* Same checks with tighter rules. */
2714*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs);
2715*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2716*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2717*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2718*e1199815SMickaël Salaün 
2719*e1199815SMickaël Salaün 	/* Checks changes for lower layer. */
2720*e1199815SMickaël Salaün 	for_each_path(lower_base_files, path_entry, i) {
2721*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
2722*e1199815SMickaël Salaün 	}
2723*e1199815SMickaël Salaün 	/* Checks changes for upper layer. */
2724*e1199815SMickaël Salaün 	for_each_path(upper_base_files, path_entry, i) {
2725*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
2726*e1199815SMickaël Salaün 	}
2727*e1199815SMickaël Salaün 	/* Checks all merge accesses. */
2728*e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
2729*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
2730*e1199815SMickaël Salaün 	}
2731*e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
2732*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
2733*e1199815SMickaël Salaün 	}
2734*e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
2735*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
2736*e1199815SMickaël Salaün 	}
2737*e1199815SMickaël Salaün 
2738*e1199815SMickaël Salaün 	/* Sets rules directly on overlayed files. */
2739*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files);
2740*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2741*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2742*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2743*e1199815SMickaël Salaün 
2744*e1199815SMickaël Salaün 	/* Checks unchanged accesses on lower layer. */
2745*e1199815SMickaël Salaün 	for_each_path(lower_sub_files, path_entry, i) {
2746*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
2747*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
2748*e1199815SMickaël Salaün 	}
2749*e1199815SMickaël Salaün 	/* Checks unchanged accesses on upper layer. */
2750*e1199815SMickaël Salaün 	for_each_path(upper_sub_files, path_entry, i) {
2751*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
2752*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
2753*e1199815SMickaël Salaün 	}
2754*e1199815SMickaël Salaün 	/* Checks all merge accesses. */
2755*e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
2756*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
2757*e1199815SMickaël Salaün 	}
2758*e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
2759*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY));
2760*e1199815SMickaël Salaün 	}
2761*e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
2762*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
2763*e1199815SMickaël Salaün 	}
2764*e1199815SMickaël Salaün 
2765*e1199815SMickaël Salaün 	/* Only allowes access to the merge hierarchy. */
2766*e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only);
2767*e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2768*e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2769*e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2770*e1199815SMickaël Salaün 
2771*e1199815SMickaël Salaün 	/* Checks new accesses on lower layer. */
2772*e1199815SMickaël Salaün 	for_each_path(lower_sub_files, path_entry, i) {
2773*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
2774*e1199815SMickaël Salaün 	}
2775*e1199815SMickaël Salaün 	/* Checks new accesses on upper layer. */
2776*e1199815SMickaël Salaün 	for_each_path(upper_sub_files, path_entry, i) {
2777*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
2778*e1199815SMickaël Salaün 	}
2779*e1199815SMickaël Salaün 	/* Checks all merge accesses. */
2780*e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
2781*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
2782*e1199815SMickaël Salaün 	}
2783*e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
2784*e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY));
2785*e1199815SMickaël Salaün 	}
2786*e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
2787*e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
2788*e1199815SMickaël Salaün 	}
2789*e1199815SMickaël Salaün }
2790*e1199815SMickaël Salaün 
2791*e1199815SMickaël Salaün TEST_HARNESS_MAIN
2792