1e1199815SMickaël Salaün // SPDX-License-Identifier: GPL-2.0
2e1199815SMickaël Salaün /*
3e1199815SMickaël Salaün  * Landlock tests - Filesystem
4e1199815SMickaël Salaün  *
5e1199815SMickaël Salaün  * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6e1199815SMickaël Salaün  * Copyright © 2020 ANSSI
755e55920SMickaël Salaün  * Copyright © 2020-2022 Microsoft Corporation
8e1199815SMickaël Salaün  */
9e1199815SMickaël Salaün 
10e1199815SMickaël Salaün #define _GNU_SOURCE
11e1199815SMickaël Salaün #include <fcntl.h>
12e1199815SMickaël Salaün #include <linux/landlock.h>
1335ca4239SMickaël Salaün #include <linux/magic.h>
14e1199815SMickaël Salaün #include <sched.h>
15366617a6SJeff Xu #include <stdio.h>
16e1199815SMickaël Salaün #include <string.h>
17e1199815SMickaël Salaün #include <sys/capability.h>
18e1199815SMickaël Salaün #include <sys/mount.h>
19e1199815SMickaël Salaün #include <sys/prctl.h>
20e1199815SMickaël Salaün #include <sys/sendfile.h>
21e1199815SMickaël Salaün #include <sys/stat.h>
22e1199815SMickaël Salaün #include <sys/sysmacros.h>
2335ca4239SMickaël Salaün #include <sys/vfs.h>
24e1199815SMickaël Salaün #include <unistd.h>
25e1199815SMickaël Salaün 
26e1199815SMickaël Salaün #include "common.h"
27e1199815SMickaël Salaün 
2887129ef1SMickaël Salaün #ifndef renameat2
renameat2(int olddirfd,const char * oldpath,int newdirfd,const char * newpath,unsigned int flags)2987129ef1SMickaël Salaün int renameat2(int olddirfd, const char *oldpath, int newdirfd,
3087129ef1SMickaël Salaün 	      const char *newpath, unsigned int flags)
3187129ef1SMickaël Salaün {
3287129ef1SMickaël Salaün 	return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath,
3387129ef1SMickaël Salaün 		       flags);
3487129ef1SMickaël Salaün }
3587129ef1SMickaël Salaün #endif
3687129ef1SMickaël Salaün 
3787129ef1SMickaël Salaün #ifndef RENAME_EXCHANGE
3887129ef1SMickaël Salaün #define RENAME_EXCHANGE (1 << 1)
3987129ef1SMickaël Salaün #endif
4087129ef1SMickaël Salaün 
41e1199815SMickaël Salaün #define TMP_DIR "tmp"
42e1199815SMickaël Salaün #define BINARY_PATH "./true"
43e1199815SMickaël Salaün 
44e1199815SMickaël Salaün /* Paths (sibling number and depth) */
45e1199815SMickaël Salaün static const char dir_s1d1[] = TMP_DIR "/s1d1";
46e1199815SMickaël Salaün static const char file1_s1d1[] = TMP_DIR "/s1d1/f1";
47e1199815SMickaël Salaün static const char file2_s1d1[] = TMP_DIR "/s1d1/f2";
48e1199815SMickaël Salaün static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2";
49e1199815SMickaël Salaün static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1";
50e1199815SMickaël Salaün static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2";
51e1199815SMickaël Salaün static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3";
52e1199815SMickaël Salaün static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1";
53e1199815SMickaël Salaün static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2";
54e1199815SMickaël Salaün 
55e1199815SMickaël Salaün static const char dir_s2d1[] = TMP_DIR "/s2d1";
56e1199815SMickaël Salaün static const char file1_s2d1[] = TMP_DIR "/s2d1/f1";
57e1199815SMickaël Salaün static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2";
58e1199815SMickaël Salaün static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1";
59e1199815SMickaël Salaün static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3";
60e1199815SMickaël Salaün static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1";
61e1199815SMickaël Salaün static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2";
62e1199815SMickaël Salaün 
63e1199815SMickaël Salaün static const char dir_s3d1[] = TMP_DIR "/s3d1";
64225351abSGünther Noack static const char file1_s3d1[] = TMP_DIR "/s3d1/f1";
65e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */
66e1199815SMickaël Salaün static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2";
67e1199815SMickaël Salaün static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3";
68e1199815SMickaël Salaün 
69e1199815SMickaël Salaün /*
70e1199815SMickaël Salaün  * layout1 hierarchy:
71e1199815SMickaël Salaün  *
72e1199815SMickaël Salaün  * tmp
73e1199815SMickaël Salaün  * ├── s1d1
74e1199815SMickaël Salaün  * │   ├── f1
75e1199815SMickaël Salaün  * │   ├── f2
76e1199815SMickaël Salaün  * │   └── s1d2
77e1199815SMickaël Salaün  * │       ├── f1
78e1199815SMickaël Salaün  * │       ├── f2
79e1199815SMickaël Salaün  * │       └── s1d3
80e1199815SMickaël Salaün  * │           ├── f1
81e1199815SMickaël Salaün  * │           └── f2
82e1199815SMickaël Salaün  * ├── s2d1
83e1199815SMickaël Salaün  * │   ├── f1
84e1199815SMickaël Salaün  * │   └── s2d2
85e1199815SMickaël Salaün  * │       ├── f1
86e1199815SMickaël Salaün  * │       └── s2d3
87e1199815SMickaël Salaün  * │           ├── f1
88e1199815SMickaël Salaün  * │           └── f2
89e1199815SMickaël Salaün  * └── s3d1
90225351abSGünther Noack  *     ├── f1
91e1199815SMickaël Salaün  *     └── s3d2
92e1199815SMickaël Salaün  *         └── s3d3
93e1199815SMickaël Salaün  */
94e1199815SMickaël Salaün 
fgrep(FILE * const inf,const char * const str)95366617a6SJeff Xu static bool fgrep(FILE *const inf, const char *const str)
96366617a6SJeff Xu {
97366617a6SJeff Xu 	char line[32];
98366617a6SJeff Xu 	const int slen = strlen(str);
99366617a6SJeff Xu 
100366617a6SJeff Xu 	while (!feof(inf)) {
101366617a6SJeff Xu 		if (!fgets(line, sizeof(line), inf))
102366617a6SJeff Xu 			break;
103366617a6SJeff Xu 		if (strncmp(line, str, slen))
104366617a6SJeff Xu 			continue;
105366617a6SJeff Xu 
106366617a6SJeff Xu 		return true;
107366617a6SJeff Xu 	}
108366617a6SJeff Xu 
109366617a6SJeff Xu 	return false;
110366617a6SJeff Xu }
111366617a6SJeff Xu 
supports_filesystem(const char * const filesystem)1123de64b65SMickaël Salaün static bool supports_filesystem(const char *const filesystem)
113366617a6SJeff Xu {
1143de64b65SMickaël Salaün 	char str[32];
1153de64b65SMickaël Salaün 	int len;
1162a201549SDing Xiang 	bool res = true;
117366617a6SJeff Xu 	FILE *const inf = fopen("/proc/filesystems", "r");
118366617a6SJeff Xu 
119366617a6SJeff Xu 	/*
120366617a6SJeff Xu 	 * Consider that the filesystem is supported if we cannot get the
121366617a6SJeff Xu 	 * supported ones.
122366617a6SJeff Xu 	 */
123366617a6SJeff Xu 	if (!inf)
124366617a6SJeff Xu 		return true;
125366617a6SJeff Xu 
12604f9070eSMickaël Salaün 	/* filesystem can be null for bind mounts. */
12704f9070eSMickaël Salaün 	if (!filesystem)
1282a201549SDing Xiang 		goto out;
12904f9070eSMickaël Salaün 
1303de64b65SMickaël Salaün 	len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem);
1313de64b65SMickaël Salaün 	if (len >= sizeof(str))
1323de64b65SMickaël Salaün 		/* Ignores too-long filesystem names. */
1332a201549SDing Xiang 		goto out;
1343de64b65SMickaël Salaün 
1353de64b65SMickaël Salaün 	res = fgrep(inf, str);
1362a201549SDing Xiang 
1372a201549SDing Xiang out:
138366617a6SJeff Xu 	fclose(inf);
139366617a6SJeff Xu 	return res;
140366617a6SJeff Xu }
141366617a6SJeff Xu 
cwd_matches_fs(unsigned int fs_magic)14235ca4239SMickaël Salaün static bool cwd_matches_fs(unsigned int fs_magic)
14335ca4239SMickaël Salaün {
14435ca4239SMickaël Salaün 	struct statfs statfs_buf;
14535ca4239SMickaël Salaün 
14635ca4239SMickaël Salaün 	if (!fs_magic)
14735ca4239SMickaël Salaün 		return true;
14835ca4239SMickaël Salaün 
14935ca4239SMickaël Salaün 	if (statfs(".", &statfs_buf))
15035ca4239SMickaël Salaün 		return true;
15135ca4239SMickaël Salaün 
15235ca4239SMickaël Salaün 	return statfs_buf.f_type == fs_magic;
15335ca4239SMickaël Salaün }
15435ca4239SMickaël Salaün 
mkdir_parents(struct __test_metadata * const _metadata,const char * const path)155e1199815SMickaël Salaün static void mkdir_parents(struct __test_metadata *const _metadata,
156e1199815SMickaël Salaün 			  const char *const path)
157e1199815SMickaël Salaün {
158e1199815SMickaël Salaün 	char *walker;
159e1199815SMickaël Salaün 	const char *parent;
160e1199815SMickaël Salaün 	int i, err;
161e1199815SMickaël Salaün 
162e1199815SMickaël Salaün 	ASSERT_NE(path[0], '\0');
163e1199815SMickaël Salaün 	walker = strdup(path);
164e1199815SMickaël Salaün 	ASSERT_NE(NULL, walker);
165e1199815SMickaël Salaün 	parent = walker;
166e1199815SMickaël Salaün 	for (i = 1; walker[i]; i++) {
167e1199815SMickaël Salaün 		if (walker[i] != '/')
168e1199815SMickaël Salaün 			continue;
169e1199815SMickaël Salaün 		walker[i] = '\0';
170e1199815SMickaël Salaün 		err = mkdir(parent, 0700);
171371183faSMickaël Salaün 		ASSERT_FALSE(err && errno != EEXIST)
172371183faSMickaël Salaün 		{
173371183faSMickaël Salaün 			TH_LOG("Failed to create directory \"%s\": %s", parent,
174371183faSMickaël Salaün 			       strerror(errno));
175e1199815SMickaël Salaün 		}
176e1199815SMickaël Salaün 		walker[i] = '/';
177e1199815SMickaël Salaün 	}
178e1199815SMickaël Salaün 	free(walker);
179e1199815SMickaël Salaün }
180e1199815SMickaël Salaün 
create_directory(struct __test_metadata * const _metadata,const char * const path)181e1199815SMickaël Salaün static void create_directory(struct __test_metadata *const _metadata,
182e1199815SMickaël Salaün 			     const char *const path)
183e1199815SMickaël Salaün {
184e1199815SMickaël Salaün 	mkdir_parents(_metadata, path);
185371183faSMickaël Salaün 	ASSERT_EQ(0, mkdir(path, 0700))
186371183faSMickaël Salaün 	{
187e1199815SMickaël Salaün 		TH_LOG("Failed to create directory \"%s\": %s", path,
188e1199815SMickaël Salaün 		       strerror(errno));
189e1199815SMickaël Salaün 	}
190e1199815SMickaël Salaün }
191e1199815SMickaël Salaün 
create_file(struct __test_metadata * const _metadata,const char * const path)192e1199815SMickaël Salaün static void create_file(struct __test_metadata *const _metadata,
193e1199815SMickaël Salaün 			const char *const path)
194e1199815SMickaël Salaün {
195e1199815SMickaël Salaün 	mkdir_parents(_metadata, path);
196371183faSMickaël Salaün 	ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0))
197371183faSMickaël Salaün 	{
198e1199815SMickaël Salaün 		TH_LOG("Failed to create file \"%s\": %s", path,
199e1199815SMickaël Salaün 		       strerror(errno));
200e1199815SMickaël Salaün 	}
201e1199815SMickaël Salaün }
202e1199815SMickaël Salaün 
remove_path(const char * const path)203e1199815SMickaël Salaün static int remove_path(const char *const path)
204e1199815SMickaël Salaün {
205e1199815SMickaël Salaün 	char *walker;
206e1199815SMickaël Salaün 	int i, ret, err = 0;
207e1199815SMickaël Salaün 
208e1199815SMickaël Salaün 	walker = strdup(path);
209e1199815SMickaël Salaün 	if (!walker) {
210e1199815SMickaël Salaün 		err = ENOMEM;
211e1199815SMickaël Salaün 		goto out;
212e1199815SMickaël Salaün 	}
213e1199815SMickaël Salaün 	if (unlink(path) && rmdir(path)) {
214f4056b92SMickaël Salaün 		if (errno != ENOENT && errno != ENOTDIR)
215e1199815SMickaël Salaün 			err = errno;
216e1199815SMickaël Salaün 		goto out;
217e1199815SMickaël Salaün 	}
218e1199815SMickaël Salaün 	for (i = strlen(walker); i > 0; i--) {
219e1199815SMickaël Salaün 		if (walker[i] != '/')
220e1199815SMickaël Salaün 			continue;
221e1199815SMickaël Salaün 		walker[i] = '\0';
222e1199815SMickaël Salaün 		ret = rmdir(walker);
223e1199815SMickaël Salaün 		if (ret) {
224e1199815SMickaël Salaün 			if (errno != ENOTEMPTY && errno != EBUSY)
225e1199815SMickaël Salaün 				err = errno;
226e1199815SMickaël Salaün 			goto out;
227e1199815SMickaël Salaün 		}
228e1199815SMickaël Salaün 		if (strcmp(walker, TMP_DIR) == 0)
229e1199815SMickaël Salaün 			goto out;
230e1199815SMickaël Salaün 	}
231e1199815SMickaël Salaün 
232e1199815SMickaël Salaün out:
233e1199815SMickaël Salaün 	free(walker);
234e1199815SMickaël Salaün 	return err;
235e1199815SMickaël Salaün }
236e1199815SMickaël Salaün 
23755ab3fbeSMickaël Salaün struct mnt_opt {
23855ab3fbeSMickaël Salaün 	const char *const source;
23955ab3fbeSMickaël Salaün 	const char *const type;
24055ab3fbeSMickaël Salaün 	const unsigned long flags;
24155ab3fbeSMickaël Salaün 	const char *const data;
24255ab3fbeSMickaël Salaün };
24355ab3fbeSMickaël Salaün 
244*854357dbSHu Yadi #define MNT_TMP_DATA "size=4m,mode=700"
245*854357dbSHu Yadi 
246*854357dbSHu Yadi static const struct mnt_opt mnt_tmp = {
24755ab3fbeSMickaël Salaün 	.type = "tmpfs",
248*854357dbSHu Yadi 	.data = MNT_TMP_DATA,
24955ab3fbeSMickaël Salaün };
25055ab3fbeSMickaël Salaün 
mount_opt(const struct mnt_opt * const mnt,const char * const target)25155ab3fbeSMickaël Salaün static int mount_opt(const struct mnt_opt *const mnt, const char *const target)
25255ab3fbeSMickaël Salaün {
25355ab3fbeSMickaël Salaün 	return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags,
25455ab3fbeSMickaël Salaün 		     mnt->data);
25555ab3fbeSMickaël Salaün }
25655ab3fbeSMickaël Salaün 
prepare_layout_opt(struct __test_metadata * const _metadata,const struct mnt_opt * const mnt)25755ab3fbeSMickaël Salaün static void prepare_layout_opt(struct __test_metadata *const _metadata,
25855ab3fbeSMickaël Salaün 			       const struct mnt_opt *const mnt)
259e1199815SMickaël Salaün {
260e1199815SMickaël Salaün 	disable_caps(_metadata);
261e1199815SMickaël Salaün 	umask(0077);
262e1199815SMickaël Salaün 	create_directory(_metadata, TMP_DIR);
263e1199815SMickaël Salaün 
264e1199815SMickaël Salaün 	/*
265e1199815SMickaël Salaün 	 * Do not pollute the rest of the system: creates a private mount point
266e1199815SMickaël Salaün 	 * for tests relying on pivot_root(2) and move_mount(2).
267e1199815SMickaël Salaün 	 */
268e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
26904f9070eSMickaël Salaün 	ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP));
27055ab3fbeSMickaël Salaün 	ASSERT_EQ(0, mount_opt(mnt, TMP_DIR))
27155ab3fbeSMickaël Salaün 	{
27255ab3fbeSMickaël Salaün 		TH_LOG("Failed to mount the %s filesystem: %s", mnt->type,
27355ab3fbeSMickaël Salaün 		       strerror(errno));
27455ab3fbeSMickaël Salaün 		/*
27555ab3fbeSMickaël Salaün 		 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP()
27655ab3fbeSMickaël Salaün 		 * failed, so we need to explicitly do a minimal cleanup to
27755ab3fbeSMickaël Salaün 		 * avoid cascading errors with other tests that don't depend on
27855ab3fbeSMickaël Salaün 		 * the same filesystem.
27955ab3fbeSMickaël Salaün 		 */
28055ab3fbeSMickaël Salaün 		remove_path(TMP_DIR);
28155ab3fbeSMickaël Salaün 	}
282e1199815SMickaël Salaün 	ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));
283e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
284e1199815SMickaël Salaün }
285e1199815SMickaël Salaün 
prepare_layout(struct __test_metadata * const _metadata)28655ab3fbeSMickaël Salaün static void prepare_layout(struct __test_metadata *const _metadata)
28755ab3fbeSMickaël Salaün {
28855ab3fbeSMickaël Salaün 	prepare_layout_opt(_metadata, &mnt_tmp);
28955ab3fbeSMickaël Salaün }
29055ab3fbeSMickaël Salaün 
cleanup_layout(struct __test_metadata * const _metadata)291e1199815SMickaël Salaün static void cleanup_layout(struct __test_metadata *const _metadata)
292e1199815SMickaël Salaün {
293e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
294e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(TMP_DIR));
295e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
296e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(TMP_DIR));
297e1199815SMickaël Salaün }
298e1199815SMickaël Salaün 
299592efeb4SMickaël Salaün /* clang-format off */
FIXTURE(layout0)300592efeb4SMickaël Salaün FIXTURE(layout0) {};
301592efeb4SMickaël Salaün /* clang-format on */
302592efeb4SMickaël Salaün 
FIXTURE_SETUP(layout0)303592efeb4SMickaël Salaün FIXTURE_SETUP(layout0)
304592efeb4SMickaël Salaün {
305592efeb4SMickaël Salaün 	prepare_layout(_metadata);
306592efeb4SMickaël Salaün }
307592efeb4SMickaël Salaün 
FIXTURE_TEARDOWN(layout0)308592efeb4SMickaël Salaün FIXTURE_TEARDOWN(layout0)
309592efeb4SMickaël Salaün {
310592efeb4SMickaël Salaün 	cleanup_layout(_metadata);
311592efeb4SMickaël Salaün }
312592efeb4SMickaël Salaün 
create_layout1(struct __test_metadata * const _metadata)313e1199815SMickaël Salaün static void create_layout1(struct __test_metadata *const _metadata)
314e1199815SMickaël Salaün {
315e1199815SMickaël Salaün 	create_file(_metadata, file1_s1d1);
316e1199815SMickaël Salaün 	create_file(_metadata, file1_s1d2);
317e1199815SMickaël Salaün 	create_file(_metadata, file1_s1d3);
318e1199815SMickaël Salaün 	create_file(_metadata, file2_s1d1);
319e1199815SMickaël Salaün 	create_file(_metadata, file2_s1d2);
320e1199815SMickaël Salaün 	create_file(_metadata, file2_s1d3);
321e1199815SMickaël Salaün 
322e1199815SMickaël Salaün 	create_file(_metadata, file1_s2d1);
323e1199815SMickaël Salaün 	create_file(_metadata, file1_s2d2);
324e1199815SMickaël Salaün 	create_file(_metadata, file1_s2d3);
325e1199815SMickaël Salaün 	create_file(_metadata, file2_s2d3);
326e1199815SMickaël Salaün 
327225351abSGünther Noack 	create_file(_metadata, file1_s3d1);
328e1199815SMickaël Salaün 	create_directory(_metadata, dir_s3d2);
329e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
33055ab3fbeSMickaël Salaün 	ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2));
331e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
332e1199815SMickaël Salaün 
333e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s3d3, 0700));
334e1199815SMickaël Salaün }
335e1199815SMickaël Salaün 
remove_layout1(struct __test_metadata * const _metadata)336e1199815SMickaël Salaün static void remove_layout1(struct __test_metadata *const _metadata)
337e1199815SMickaël Salaün {
338e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s1d3));
339e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s1d2));
340e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s1d1));
341e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s1d3));
342e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s1d2));
343e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s1d1));
34404f9070eSMickaël Salaün 	EXPECT_EQ(0, remove_path(dir_s1d3));
345e1199815SMickaël Salaün 
346e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file2_s2d3));
347e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s2d3));
348e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s2d2));
349e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(file1_s2d1));
35004f9070eSMickaël Salaün 	EXPECT_EQ(0, remove_path(dir_s2d2));
351e1199815SMickaël Salaün 
352225351abSGünther Noack 	EXPECT_EQ(0, remove_path(file1_s3d1));
353e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(dir_s3d3));
354e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
355e1199815SMickaël Salaün 	umount(dir_s3d2);
356e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
357e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(dir_s3d2));
358e1199815SMickaël Salaün }
359e1199815SMickaël Salaün 
3604598d9abSMickaël Salaün /* clang-format off */
FIXTURE(layout1)3614598d9abSMickaël Salaün FIXTURE(layout1) {};
3624598d9abSMickaël Salaün /* clang-format on */
363e1199815SMickaël Salaün 
FIXTURE_SETUP(layout1)364e1199815SMickaël Salaün FIXTURE_SETUP(layout1)
365e1199815SMickaël Salaün {
366e1199815SMickaël Salaün 	prepare_layout(_metadata);
367e1199815SMickaël Salaün 
368e1199815SMickaël Salaün 	create_layout1(_metadata);
369e1199815SMickaël Salaün }
370e1199815SMickaël Salaün 
FIXTURE_TEARDOWN(layout1)371e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1)
372e1199815SMickaël Salaün {
373e1199815SMickaël Salaün 	remove_layout1(_metadata);
374e1199815SMickaël Salaün 
375e1199815SMickaël Salaün 	cleanup_layout(_metadata);
376e1199815SMickaël Salaün }
377e1199815SMickaël Salaün 
378e1199815SMickaël Salaün /*
379e1199815SMickaël Salaün  * This helper enables to use the ASSERT_* macros and print the line number
380e1199815SMickaël Salaün  * pointing to the test caller.
381e1199815SMickaël Salaün  */
test_open_rel(const int dirfd,const char * const path,const int flags)382371183faSMickaël Salaün static int test_open_rel(const int dirfd, const char *const path,
383371183faSMickaël Salaün 			 const int flags)
384e1199815SMickaël Salaün {
385e1199815SMickaël Salaün 	int fd;
386e1199815SMickaël Salaün 
387e1199815SMickaël Salaün 	/* Works with file and directories. */
388e1199815SMickaël Salaün 	fd = openat(dirfd, path, flags | O_CLOEXEC);
389e1199815SMickaël Salaün 	if (fd < 0)
390e1199815SMickaël Salaün 		return errno;
391e1199815SMickaël Salaün 	/*
392e1199815SMickaël Salaün 	 * Mixing error codes from close(2) and open(2) should not lead to any
393e1199815SMickaël Salaün 	 * (access type) confusion for this test.
394e1199815SMickaël Salaün 	 */
395e1199815SMickaël Salaün 	if (close(fd) != 0)
396e1199815SMickaël Salaün 		return errno;
397e1199815SMickaël Salaün 	return 0;
398e1199815SMickaël Salaün }
399e1199815SMickaël Salaün 
test_open(const char * const path,const int flags)400e1199815SMickaël Salaün static int test_open(const char *const path, const int flags)
401e1199815SMickaël Salaün {
402e1199815SMickaël Salaün 	return test_open_rel(AT_FDCWD, path, flags);
403e1199815SMickaël Salaün }
404e1199815SMickaël Salaün 
TEST_F_FORK(layout1,no_restriction)405e1199815SMickaël Salaün TEST_F_FORK(layout1, no_restriction)
406e1199815SMickaël Salaün {
407e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
408e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
409e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY));
410e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
411e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
412e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY));
413e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
414e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
415e1199815SMickaël Salaün 
416e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
417e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
418e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
419e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
420e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY));
421e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY));
422e1199815SMickaël Salaün 
423e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
424e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
425e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
426e1199815SMickaël Salaün }
427e1199815SMickaël Salaün 
TEST_F_FORK(layout1,inval)428e1199815SMickaël Salaün TEST_F_FORK(layout1, inval)
429e1199815SMickaël Salaün {
430e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath = {
431e1199815SMickaël Salaün 		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
432e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
433e1199815SMickaël Salaün 		.parent_fd = -1,
434e1199815SMickaël Salaün 	};
435e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {
436e1199815SMickaël Salaün 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |
437e1199815SMickaël Salaün 				     LANDLOCK_ACCESS_FS_WRITE_FILE,
438e1199815SMickaël Salaün 	};
439e1199815SMickaël Salaün 	int ruleset_fd;
440e1199815SMickaël Salaün 
441371183faSMickaël Salaün 	path_beneath.parent_fd =
442371183faSMickaël Salaün 		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
443e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
444e1199815SMickaël Salaün 
445e1199815SMickaël Salaün 	ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);
446e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
447e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
448e1199815SMickaël Salaün 					&path_beneath, 0));
449e1199815SMickaël Salaün 	/* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */
450e1199815SMickaël Salaün 	ASSERT_EQ(EBADF, errno);
451e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
452e1199815SMickaël Salaün 
453e1199815SMickaël Salaün 	ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC);
454e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
455e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
456e1199815SMickaël Salaün 					&path_beneath, 0));
457e1199815SMickaël Salaün 	/* Returns EBADFD because ruleset_fd is not a valid ruleset. */
458e1199815SMickaël Salaün 	ASSERT_EQ(EBADFD, errno);
459e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
460e1199815SMickaël Salaün 
461e1199815SMickaël Salaün 	/* Gets a real ruleset. */
462371183faSMickaël Salaün 	ruleset_fd =
463371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
464e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
465e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
466e1199815SMickaël Salaün 				       &path_beneath, 0));
467e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
468e1199815SMickaël Salaün 
469e1199815SMickaël Salaün 	/* Tests without O_PATH. */
470e1199815SMickaël Salaün 	path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC);
471e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
472e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
473e1199815SMickaël Salaün 				       &path_beneath, 0));
474e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
475e1199815SMickaël Salaün 
476e1199815SMickaël Salaün 	/* Tests with a ruleset FD. */
477e1199815SMickaël Salaün 	path_beneath.parent_fd = ruleset_fd;
478e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
479e1199815SMickaël Salaün 					&path_beneath, 0));
480e1199815SMickaël Salaün 	ASSERT_EQ(EBADFD, errno);
481e1199815SMickaël Salaün 
482e1199815SMickaël Salaün 	/* Checks unhandled allowed_access. */
483371183faSMickaël Salaün 	path_beneath.parent_fd =
484371183faSMickaël Salaün 		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
485e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
486e1199815SMickaël Salaün 
487e1199815SMickaël Salaün 	/* Test with legitimate values. */
488e1199815SMickaël Salaün 	path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE;
489e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
490e1199815SMickaël Salaün 					&path_beneath, 0));
491e1199815SMickaël Salaün 	ASSERT_EQ(EINVAL, errno);
492e1199815SMickaël Salaün 	path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE;
493e1199815SMickaël Salaün 
49455e55920SMickaël Salaün 	/* Tests with denied-by-default access right. */
49555e55920SMickaël Salaün 	path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER;
49655e55920SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
49755e55920SMickaël Salaün 					&path_beneath, 0));
49855e55920SMickaël Salaün 	ASSERT_EQ(EINVAL, errno);
49955e55920SMickaël Salaün 	path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER;
50055e55920SMickaël Salaün 
501e1199815SMickaël Salaün 	/* Test with unknown (64-bits) value. */
502e1199815SMickaël Salaün 	path_beneath.allowed_access |= (1ULL << 60);
503e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
504e1199815SMickaël Salaün 					&path_beneath, 0));
505e1199815SMickaël Salaün 	ASSERT_EQ(EINVAL, errno);
506e1199815SMickaël Salaün 	path_beneath.allowed_access &= ~(1ULL << 60);
507e1199815SMickaël Salaün 
508e1199815SMickaël Salaün 	/* Test with no access. */
509e1199815SMickaël Salaün 	path_beneath.allowed_access = 0;
510e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
511e1199815SMickaël Salaün 					&path_beneath, 0));
512e1199815SMickaël Salaün 	ASSERT_EQ(ENOMSG, errno);
513e1199815SMickaël Salaün 	path_beneath.allowed_access &= ~(1ULL << 60);
514e1199815SMickaël Salaün 
515e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
516e1199815SMickaël Salaün 
517e1199815SMickaël Salaün 	/* Enforces the ruleset. */
518e1199815SMickaël Salaün 	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
519e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
520e1199815SMickaël Salaün 
521e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
522e1199815SMickaël Salaün }
523e1199815SMickaël Salaün 
5244598d9abSMickaël Salaün /* clang-format off */
5254598d9abSMickaël Salaün 
526e1199815SMickaël Salaün #define ACCESS_FILE ( \
527e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_EXECUTE | \
528e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_WRITE_FILE | \
529b9f5ce27SGünther Noack 	LANDLOCK_ACCESS_FS_READ_FILE | \
530b9f5ce27SGünther Noack 	LANDLOCK_ACCESS_FS_TRUNCATE)
531e1199815SMickaël Salaün 
532b9f5ce27SGünther Noack #define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE
533e1199815SMickaël Salaün 
534e1199815SMickaël Salaün #define ACCESS_ALL ( \
535e1199815SMickaël Salaün 	ACCESS_FILE | \
536e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_DIR | \
537e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_REMOVE_DIR | \
538e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_REMOVE_FILE | \
539e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_CHAR | \
540e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_DIR | \
541e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_REG | \
542e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_SOCK | \
543e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_FIFO | \
544e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
545b91c3e4eSMickaël Salaün 	LANDLOCK_ACCESS_FS_MAKE_SYM | \
546b9f5ce27SGünther Noack 	LANDLOCK_ACCESS_FS_REFER)
547e1199815SMickaël Salaün 
5484598d9abSMickaël Salaün /* clang-format on */
5494598d9abSMickaël Salaün 
TEST_F_FORK(layout1,file_and_dir_access_rights)550d18955d0SMickaël Salaün TEST_F_FORK(layout1, file_and_dir_access_rights)
551e1199815SMickaël Salaün {
552e1199815SMickaël Salaün 	__u64 access;
553e1199815SMickaël Salaün 	int err;
554d18955d0SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath_file = {},
555d18955d0SMickaël Salaün 					  path_beneath_dir = {};
556e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {
557e1199815SMickaël Salaün 		.handled_access_fs = ACCESS_ALL,
558e1199815SMickaël Salaün 	};
559371183faSMickaël Salaün 	const int ruleset_fd =
560371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
561e1199815SMickaël Salaün 
562e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
563e1199815SMickaël Salaün 
564e1199815SMickaël Salaün 	/* Tests access rights for files. */
565d18955d0SMickaël Salaün 	path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);
566d18955d0SMickaël Salaün 	ASSERT_LE(0, path_beneath_file.parent_fd);
567d18955d0SMickaël Salaün 
568d18955d0SMickaël Salaün 	/* Tests access rights for directories. */
569d18955d0SMickaël Salaün 	path_beneath_dir.parent_fd =
570d18955d0SMickaël Salaün 		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
571d18955d0SMickaël Salaün 	ASSERT_LE(0, path_beneath_dir.parent_fd);
572d18955d0SMickaël Salaün 
573e1199815SMickaël Salaün 	for (access = 1; access <= ACCESS_LAST; access <<= 1) {
574d18955d0SMickaël Salaün 		path_beneath_dir.allowed_access = access;
575d18955d0SMickaël Salaün 		ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
576d18955d0SMickaël Salaün 					       LANDLOCK_RULE_PATH_BENEATH,
577d18955d0SMickaël Salaün 					       &path_beneath_dir, 0));
578d18955d0SMickaël Salaün 
579d18955d0SMickaël Salaün 		path_beneath_file.allowed_access = access;
580e1199815SMickaël Salaün 		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
581d18955d0SMickaël Salaün 					&path_beneath_file, 0);
582d18955d0SMickaël Salaün 		if (access & ACCESS_FILE) {
583e1199815SMickaël Salaün 			ASSERT_EQ(0, err);
584e1199815SMickaël Salaün 		} else {
585e1199815SMickaël Salaün 			ASSERT_EQ(-1, err);
586e1199815SMickaël Salaün 			ASSERT_EQ(EINVAL, errno);
587e1199815SMickaël Salaün 		}
588e1199815SMickaël Salaün 	}
589d18955d0SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath_file.parent_fd));
590d18955d0SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath_dir.parent_fd));
591d18955d0SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
592e1199815SMickaël Salaün }
593e1199815SMickaël Salaün 
TEST_F_FORK(layout0,unknown_access_rights)594592efeb4SMickaël Salaün TEST_F_FORK(layout0, unknown_access_rights)
595c56b3bf5SMickaël Salaün {
596c56b3bf5SMickaël Salaün 	__u64 access_mask;
597c56b3bf5SMickaël Salaün 
598c56b3bf5SMickaël Salaün 	for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST;
599c56b3bf5SMickaël Salaün 	     access_mask >>= 1) {
600c56b3bf5SMickaël Salaün 		struct landlock_ruleset_attr ruleset_attr = {
601c56b3bf5SMickaël Salaün 			.handled_access_fs = access_mask,
602c56b3bf5SMickaël Salaün 		};
603c56b3bf5SMickaël Salaün 
604c56b3bf5SMickaël Salaün 		ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
605c56b3bf5SMickaël Salaün 						      sizeof(ruleset_attr), 0));
606c56b3bf5SMickaël Salaün 		ASSERT_EQ(EINVAL, errno);
607c56b3bf5SMickaël Salaün 	}
608c56b3bf5SMickaël Salaün }
609c56b3bf5SMickaël Salaün 
add_path_beneath(struct __test_metadata * const _metadata,const int ruleset_fd,const __u64 allowed_access,const char * const path)610e1199815SMickaël Salaün static void add_path_beneath(struct __test_metadata *const _metadata,
611e1199815SMickaël Salaün 			     const int ruleset_fd, const __u64 allowed_access,
612e1199815SMickaël Salaün 			     const char *const path)
613e1199815SMickaël Salaün {
614e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath = {
615e1199815SMickaël Salaün 		.allowed_access = allowed_access,
616e1199815SMickaël Salaün 	};
617e1199815SMickaël Salaün 
618e1199815SMickaël Salaün 	path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC);
619371183faSMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd)
620371183faSMickaël Salaün 	{
621e1199815SMickaël Salaün 		TH_LOG("Failed to open directory \"%s\": %s", path,
622e1199815SMickaël Salaün 		       strerror(errno));
623e1199815SMickaël Salaün 	}
624e1199815SMickaël Salaün 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
625371183faSMickaël Salaün 				       &path_beneath, 0))
626371183faSMickaël Salaün 	{
627e1199815SMickaël Salaün 		TH_LOG("Failed to update the ruleset with \"%s\": %s", path,
628e1199815SMickaël Salaün 		       strerror(errno));
629e1199815SMickaël Salaün 	}
630e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
631e1199815SMickaël Salaün }
632e1199815SMickaël Salaün 
633e1199815SMickaël Salaün struct rule {
634e1199815SMickaël Salaün 	const char *path;
635e1199815SMickaël Salaün 	__u64 access;
636e1199815SMickaël Salaün };
637e1199815SMickaël Salaün 
6384598d9abSMickaël Salaün /* clang-format off */
6394598d9abSMickaël Salaün 
640e1199815SMickaël Salaün #define ACCESS_RO ( \
641e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_FILE | \
642e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_READ_DIR)
643e1199815SMickaël Salaün 
644e1199815SMickaël Salaün #define ACCESS_RW ( \
645e1199815SMickaël Salaün 	ACCESS_RO | \
646e1199815SMickaël Salaün 	LANDLOCK_ACCESS_FS_WRITE_FILE)
647e1199815SMickaël Salaün 
6484598d9abSMickaël Salaün /* clang-format on */
6494598d9abSMickaël Salaün 
create_ruleset(struct __test_metadata * const _metadata,const __u64 handled_access_fs,const struct rule rules[])650e1199815SMickaël Salaün static int create_ruleset(struct __test_metadata *const _metadata,
651371183faSMickaël Salaün 			  const __u64 handled_access_fs,
652371183faSMickaël Salaün 			  const struct rule rules[])
653e1199815SMickaël Salaün {
654e1199815SMickaël Salaün 	int ruleset_fd, i;
655e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {
656e1199815SMickaël Salaün 		.handled_access_fs = handled_access_fs,
657e1199815SMickaël Salaün 	};
658e1199815SMickaël Salaün 
659371183faSMickaël Salaün 	ASSERT_NE(NULL, rules)
660371183faSMickaël Salaün 	{
661e1199815SMickaël Salaün 		TH_LOG("No rule list");
662e1199815SMickaël Salaün 	}
663371183faSMickaël Salaün 	ASSERT_NE(NULL, rules[0].path)
664371183faSMickaël Salaün 	{
665e1199815SMickaël Salaün 		TH_LOG("Empty rule list");
666e1199815SMickaël Salaün 	}
667e1199815SMickaël Salaün 
668371183faSMickaël Salaün 	ruleset_fd =
669371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
670371183faSMickaël Salaün 	ASSERT_LE(0, ruleset_fd)
671371183faSMickaël Salaün 	{
672e1199815SMickaël Salaün 		TH_LOG("Failed to create a ruleset: %s", strerror(errno));
673e1199815SMickaël Salaün 	}
674e1199815SMickaël Salaün 
675e1199815SMickaël Salaün 	for (i = 0; rules[i].path; i++) {
676e1199815SMickaël Salaün 		add_path_beneath(_metadata, ruleset_fd, rules[i].access,
677e1199815SMickaël Salaün 				 rules[i].path);
678e1199815SMickaël Salaün 	}
679e1199815SMickaël Salaün 	return ruleset_fd;
680e1199815SMickaël Salaün }
681e1199815SMickaël Salaün 
enforce_ruleset(struct __test_metadata * const _metadata,const int ruleset_fd)682e1199815SMickaël Salaün static void enforce_ruleset(struct __test_metadata *const _metadata,
683e1199815SMickaël Salaün 			    const int ruleset_fd)
684e1199815SMickaël Salaün {
685e1199815SMickaël Salaün 	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
686371183faSMickaël Salaün 	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0))
687371183faSMickaël Salaün 	{
688e1199815SMickaël Salaün 		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
689e1199815SMickaël Salaün 	}
690e1199815SMickaël Salaün }
691e1199815SMickaël Salaün 
TEST_F_FORK(layout0,proc_nsfs)692592efeb4SMickaël Salaün TEST_F_FORK(layout0, proc_nsfs)
693e1199815SMickaël Salaün {
694e1199815SMickaël Salaün 	const struct rule rules[] = {
695e1199815SMickaël Salaün 		{
696e1199815SMickaël Salaün 			.path = "/dev/null",
697e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
698e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
699e1199815SMickaël Salaün 		},
700135464f9SMickaël Salaün 		{},
701e1199815SMickaël Salaün 	};
702e1199815SMickaël Salaün 	struct landlock_path_beneath_attr path_beneath;
703371183faSMickaël Salaün 	const int ruleset_fd = create_ruleset(
704371183faSMickaël Salaün 		_metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR,
705371183faSMickaël Salaün 		rules);
706e1199815SMickaël Salaün 
707e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
708e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
709e1199815SMickaël Salaün 
710e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
711e1199815SMickaël Salaün 
712e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
713e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY));
714e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/dev/null", O_RDONLY));
715e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY));
716e1199815SMickaël Salaün 
717e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY));
718e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY));
719e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY));
720e1199815SMickaël Salaün 	/*
721e1199815SMickaël Salaün 	 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a
722e1199815SMickaël Salaün 	 * disconnected path.  Such path cannot be identified and must then be
723e1199815SMickaël Salaün 	 * allowed.
724e1199815SMickaël Salaün 	 */
725e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
726e1199815SMickaël Salaün 
727e1199815SMickaël Salaün 	/*
728e1199815SMickaël Salaün 	 * Checks that it is not possible to add nsfs-like filesystem
729e1199815SMickaël Salaün 	 * references to a ruleset.
730e1199815SMickaël Salaün 	 */
731e1199815SMickaël Salaün 	path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
732e1199815SMickaël Salaün 				      LANDLOCK_ACCESS_FS_WRITE_FILE,
733e1199815SMickaël Salaün 	path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC);
734e1199815SMickaël Salaün 	ASSERT_LE(0, path_beneath.parent_fd);
735e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
736e1199815SMickaël Salaün 					&path_beneath, 0));
737e1199815SMickaël Salaün 	ASSERT_EQ(EBADFD, errno);
738e1199815SMickaël Salaün 	ASSERT_EQ(0, close(path_beneath.parent_fd));
739e1199815SMickaël Salaün }
740e1199815SMickaël Salaün 
TEST_F_FORK(layout0,unpriv)741592efeb4SMickaël Salaün TEST_F_FORK(layout0, unpriv)
742371183faSMickaël Salaün {
743e1199815SMickaël Salaün 	const struct rule rules[] = {
744e1199815SMickaël Salaün 		{
745592efeb4SMickaël Salaün 			.path = TMP_DIR,
746e1199815SMickaël Salaün 			.access = ACCESS_RO,
747e1199815SMickaël Salaün 		},
748135464f9SMickaël Salaün 		{},
749e1199815SMickaël Salaün 	};
750e1199815SMickaël Salaün 	int ruleset_fd;
751e1199815SMickaël Salaün 
752e1199815SMickaël Salaün 	drop_caps(_metadata);
753e1199815SMickaël Salaün 
754e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
755e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
756e1199815SMickaël Salaün 	ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));
757e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
758e1199815SMickaël Salaün 
759e1199815SMickaël Salaün 	/* enforce_ruleset() calls prctl(no_new_privs). */
760e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
761e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
762e1199815SMickaël Salaün }
763e1199815SMickaël Salaün 
TEST_F_FORK(layout1,effective_access)764e1199815SMickaël Salaün TEST_F_FORK(layout1, effective_access)
765e1199815SMickaël Salaün {
766e1199815SMickaël Salaün 	const struct rule rules[] = {
767e1199815SMickaël Salaün 		{
768e1199815SMickaël Salaün 			.path = dir_s1d2,
769e1199815SMickaël Salaün 			.access = ACCESS_RO,
770e1199815SMickaël Salaün 		},
771e1199815SMickaël Salaün 		{
772e1199815SMickaël Salaün 			.path = file1_s2d2,
773e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
774e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
775e1199815SMickaël Salaün 		},
776135464f9SMickaël Salaün 		{},
777e1199815SMickaël Salaün 	};
778e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
779e1199815SMickaël Salaün 	char buf;
780e1199815SMickaël Salaün 	int reg_fd;
781e1199815SMickaël Salaün 
782e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
783e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
784e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
785e1199815SMickaël Salaün 
786d1788ad9SMickaël Salaün 	/* Tests on a directory (with or without O_PATH). */
787e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
788d1788ad9SMickaël Salaün 	ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH));
789e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
790d1788ad9SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH));
791e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
792d1788ad9SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH));
793d1788ad9SMickaël Salaün 
794e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
795e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
796e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
797e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
798e1199815SMickaël Salaün 
799d1788ad9SMickaël Salaün 	/* Tests on a file (with or without O_PATH). */
800e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY));
801d1788ad9SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH));
802d1788ad9SMickaël Salaün 
803e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
804e1199815SMickaël Salaün 
805e1199815SMickaël Salaün 	/* Checks effective read and write actions. */
806e1199815SMickaël Salaün 	reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC);
807e1199815SMickaël Salaün 	ASSERT_LE(0, reg_fd);
808e1199815SMickaël Salaün 	ASSERT_EQ(1, write(reg_fd, ".", 1));
809e1199815SMickaël Salaün 	ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET));
810e1199815SMickaël Salaün 	ASSERT_EQ(1, read(reg_fd, &buf, 1));
811e1199815SMickaël Salaün 	ASSERT_EQ('.', buf);
812e1199815SMickaël Salaün 	ASSERT_EQ(0, close(reg_fd));
813e1199815SMickaël Salaün 
814e1199815SMickaël Salaün 	/* Just in case, double-checks effective actions. */
815e1199815SMickaël Salaün 	reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC);
816e1199815SMickaël Salaün 	ASSERT_LE(0, reg_fd);
817e1199815SMickaël Salaün 	ASSERT_EQ(-1, write(reg_fd, &buf, 1));
818e1199815SMickaël Salaün 	ASSERT_EQ(EBADF, errno);
819e1199815SMickaël Salaün 	ASSERT_EQ(0, close(reg_fd));
820e1199815SMickaël Salaün }
821e1199815SMickaël Salaün 
TEST_F_FORK(layout1,unhandled_access)822e1199815SMickaël Salaün TEST_F_FORK(layout1, unhandled_access)
823e1199815SMickaël Salaün {
824e1199815SMickaël Salaün 	const struct rule rules[] = {
825e1199815SMickaël Salaün 		{
826e1199815SMickaël Salaün 			.path = dir_s1d2,
827e1199815SMickaël Salaün 			.access = ACCESS_RO,
828e1199815SMickaël Salaün 		},
829135464f9SMickaël Salaün 		{},
830e1199815SMickaël Salaün 	};
831e1199815SMickaël Salaün 	/* Here, we only handle read accesses, not write accesses. */
832e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
833e1199815SMickaël Salaün 
834e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
835e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
836e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
837e1199815SMickaël Salaün 
838e1199815SMickaël Salaün 	/*
839e1199815SMickaël Salaün 	 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE,
840e1199815SMickaël Salaün 	 * opening for write-only should be allowed, but not read-write.
841e1199815SMickaël Salaün 	 */
842e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY));
843e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
844e1199815SMickaël Salaün 
845e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
846e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
847e1199815SMickaël Salaün }
848e1199815SMickaël Salaün 
TEST_F_FORK(layout1,ruleset_overlap)849e1199815SMickaël Salaün TEST_F_FORK(layout1, ruleset_overlap)
850e1199815SMickaël Salaün {
851e1199815SMickaël Salaün 	const struct rule rules[] = {
852e1199815SMickaël Salaün 		/* These rules should be ORed among them. */
853e1199815SMickaël Salaün 		{
854e1199815SMickaël Salaün 			.path = dir_s1d2,
855e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
856e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
857e1199815SMickaël Salaün 		},
858e1199815SMickaël Salaün 		{
859e1199815SMickaël Salaün 			.path = dir_s1d2,
860e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
861e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_READ_DIR,
862e1199815SMickaël Salaün 		},
863135464f9SMickaël Salaün 		{},
864e1199815SMickaël Salaün 	};
865e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
866e1199815SMickaël Salaün 
867e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
868e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
869e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
870e1199815SMickaël Salaün 
871e1199815SMickaël Salaün 	/* Checks s1d1 hierarchy. */
872e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
873e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
874e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
875e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
876e1199815SMickaël Salaün 
877e1199815SMickaël Salaün 	/* Checks s1d2 hierarchy. */
878e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
879e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
880e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
881e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
882e1199815SMickaël Salaün 
883e1199815SMickaël Salaün 	/* Checks s1d3 hierarchy. */
884e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
885e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
886e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
887e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
888e1199815SMickaël Salaün }
889e1199815SMickaël Salaün 
TEST_F_FORK(layout1,layer_rule_unions)8908ba0005fSMickaël Salaün TEST_F_FORK(layout1, layer_rule_unions)
8918ba0005fSMickaël Salaün {
8928ba0005fSMickaël Salaün 	const struct rule layer1[] = {
8938ba0005fSMickaël Salaün 		{
8948ba0005fSMickaël Salaün 			.path = dir_s1d2,
8958ba0005fSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
8968ba0005fSMickaël Salaün 		},
8978ba0005fSMickaël Salaün 		/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
8988ba0005fSMickaël Salaün 		{
8998ba0005fSMickaël Salaün 			.path = dir_s1d3,
9008ba0005fSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
9018ba0005fSMickaël Salaün 		},
9028ba0005fSMickaël Salaün 		{},
9038ba0005fSMickaël Salaün 	};
9048ba0005fSMickaël Salaün 	const struct rule layer2[] = {
9058ba0005fSMickaël Salaün 		/* Doesn't change anything from layer1. */
9068ba0005fSMickaël Salaün 		{
9078ba0005fSMickaël Salaün 			.path = dir_s1d2,
9088ba0005fSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
9098ba0005fSMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
9108ba0005fSMickaël Salaün 		},
9118ba0005fSMickaël Salaün 		{},
9128ba0005fSMickaël Salaün 	};
9138ba0005fSMickaël Salaün 	const struct rule layer3[] = {
9148ba0005fSMickaël Salaün 		/* Only allows write (but not read) to dir_s1d3. */
9158ba0005fSMickaël Salaün 		{
9168ba0005fSMickaël Salaün 			.path = dir_s1d2,
9178ba0005fSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
9188ba0005fSMickaël Salaün 		},
9198ba0005fSMickaël Salaün 		{},
9208ba0005fSMickaël Salaün 	};
9218ba0005fSMickaël Salaün 	int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1);
9228ba0005fSMickaël Salaün 
9238ba0005fSMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
9248ba0005fSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
9258ba0005fSMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
9268ba0005fSMickaël Salaün 
9278ba0005fSMickaël Salaün 	/* Checks s1d1 hierarchy with layer1. */
9288ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
9298ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
9308ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
9318ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9328ba0005fSMickaël Salaün 
9338ba0005fSMickaël Salaün 	/* Checks s1d2 hierarchy with layer1. */
9348ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
9358ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
9368ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
9378ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9388ba0005fSMickaël Salaün 
9398ba0005fSMickaël Salaün 	/* Checks s1d3 hierarchy with layer1. */
9408ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
9418ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
9428ba0005fSMickaël Salaün 	/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
9438ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
9448ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9458ba0005fSMickaël Salaün 
9468ba0005fSMickaël Salaün 	/* Doesn't change anything from layer1. */
9478ba0005fSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2);
9488ba0005fSMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
9498ba0005fSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
9508ba0005fSMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
9518ba0005fSMickaël Salaün 
9528ba0005fSMickaël Salaün 	/* Checks s1d1 hierarchy with layer2. */
9538ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
9548ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
9558ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
9568ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9578ba0005fSMickaël Salaün 
9588ba0005fSMickaël Salaün 	/* Checks s1d2 hierarchy with layer2. */
9598ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
9608ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
9618ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
9628ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9638ba0005fSMickaël Salaün 
9648ba0005fSMickaël Salaün 	/* Checks s1d3 hierarchy with layer2. */
9658ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
9668ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
9678ba0005fSMickaël Salaün 	/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
9688ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
9698ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9708ba0005fSMickaël Salaün 
9718ba0005fSMickaël Salaün 	/* Only allows write (but not read) to dir_s1d3. */
9728ba0005fSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3);
9738ba0005fSMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
9748ba0005fSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
9758ba0005fSMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
9768ba0005fSMickaël Salaün 
9778ba0005fSMickaël Salaün 	/* Checks s1d1 hierarchy with layer3. */
9788ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
9798ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
9808ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
9818ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9828ba0005fSMickaël Salaün 
9838ba0005fSMickaël Salaün 	/* Checks s1d2 hierarchy with layer3. */
9848ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
9858ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
9868ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
9878ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9888ba0005fSMickaël Salaün 
9898ba0005fSMickaël Salaün 	/* Checks s1d3 hierarchy with layer3. */
9908ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
9918ba0005fSMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
9928ba0005fSMickaël Salaün 	/* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */
9938ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR));
9948ba0005fSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
9958ba0005fSMickaël Salaün }
9968ba0005fSMickaël Salaün 
TEST_F_FORK(layout1,non_overlapping_accesses)997e1199815SMickaël Salaün TEST_F_FORK(layout1, non_overlapping_accesses)
998e1199815SMickaël Salaün {
999e1199815SMickaël Salaün 	const struct rule layer1[] = {
1000e1199815SMickaël Salaün 		{
1001e1199815SMickaël Salaün 			.path = dir_s1d2,
1002e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
1003e1199815SMickaël Salaün 		},
1004135464f9SMickaël Salaün 		{},
1005e1199815SMickaël Salaün 	};
1006e1199815SMickaël Salaün 	const struct rule layer2[] = {
1007e1199815SMickaël Salaün 		{
1008e1199815SMickaël Salaün 			.path = dir_s1d3,
1009e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1010e1199815SMickaël Salaün 		},
1011135464f9SMickaël Salaün 		{},
1012e1199815SMickaël Salaün 	};
1013e1199815SMickaël Salaün 	int ruleset_fd;
1014e1199815SMickaël Salaün 
1015e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
1016e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1017e1199815SMickaël Salaün 
1018371183faSMickaël Salaün 	ruleset_fd =
1019371183faSMickaël Salaün 		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1);
1020e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1021e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1022e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1023e1199815SMickaël Salaün 
1024e1199815SMickaël Salaün 	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
1025e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1026e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
1027e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1028e1199815SMickaël Salaün 
1029e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE,
1030e1199815SMickaël Salaün 				    layer2);
1031e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1032e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1033e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1034e1199815SMickaël Salaün 
1035e1199815SMickaël Salaün 	/* Unchanged accesses for file creation. */
1036e1199815SMickaël Salaün 	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
1037e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1038e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
1039e1199815SMickaël Salaün 
1040e1199815SMickaël Salaün 	/* Checks file removing. */
1041e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlink(file1_s1d2));
1042e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
1043e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1044e1199815SMickaël Salaün }
1045e1199815SMickaël Salaün 
TEST_F_FORK(layout1,interleaved_masked_accesses)1046e1199815SMickaël Salaün TEST_F_FORK(layout1, interleaved_masked_accesses)
1047e1199815SMickaël Salaün {
1048e1199815SMickaël Salaün 	/*
1049e1199815SMickaël Salaün 	 * Checks overly restrictive rules:
1050e1199815SMickaël Salaün 	 * layer 1: allows R   s1d1/s1d2/s1d3/file1
1051e1199815SMickaël Salaün 	 * layer 2: allows RW  s1d1/s1d2/s1d3
1052e1199815SMickaël Salaün 	 *          allows  W  s1d1/s1d2
1053e1199815SMickaël Salaün 	 *          denies R   s1d1/s1d2
1054e1199815SMickaël Salaün 	 * layer 3: allows R   s1d1
1055e1199815SMickaël Salaün 	 * layer 4: allows R   s1d1/s1d2
1056e1199815SMickaël Salaün 	 *          denies  W  s1d1/s1d2
1057e1199815SMickaël Salaün 	 * layer 5: allows R   s1d1/s1d2
1058e1199815SMickaël Salaün 	 * layer 6: allows   X ----
1059e1199815SMickaël Salaün 	 * layer 7: allows  W  s1d1/s1d2
1060e1199815SMickaël Salaün 	 *          denies R   s1d1/s1d2
1061e1199815SMickaël Salaün 	 */
1062e1199815SMickaël Salaün 	const struct rule layer1_read[] = {
1063e1199815SMickaël Salaün 		/* Allows read access to file1_s1d3 with the first layer. */
1064e1199815SMickaël Salaün 		{
1065e1199815SMickaël Salaün 			.path = file1_s1d3,
1066e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1067e1199815SMickaël Salaün 		},
1068135464f9SMickaël Salaün 		{},
1069e1199815SMickaël Salaün 	};
1070e1199815SMickaël Salaün 	/* First rule with write restrictions. */
1071e1199815SMickaël Salaün 	const struct rule layer2_read_write[] = {
1072e1199815SMickaël Salaün 		/* Start by granting read-write access via its parent directory... */
1073e1199815SMickaël Salaün 		{
1074e1199815SMickaël Salaün 			.path = dir_s1d3,
1075e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
1076e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
1077e1199815SMickaël Salaün 		},
1078e1199815SMickaël Salaün 		/* ...but also denies read access via its grandparent directory. */
1079e1199815SMickaël Salaün 		{
1080e1199815SMickaël Salaün 			.path = dir_s1d2,
1081e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1082e1199815SMickaël Salaün 		},
1083135464f9SMickaël Salaün 		{},
1084e1199815SMickaël Salaün 	};
1085e1199815SMickaël Salaün 	const struct rule layer3_read[] = {
1086e1199815SMickaël Salaün 		/* Allows read access via its great-grandparent directory. */
1087e1199815SMickaël Salaün 		{
1088e1199815SMickaël Salaün 			.path = dir_s1d1,
1089e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1090e1199815SMickaël Salaün 		},
1091135464f9SMickaël Salaün 		{},
1092e1199815SMickaël Salaün 	};
1093e1199815SMickaël Salaün 	const struct rule layer4_read_write[] = {
1094e1199815SMickaël Salaün 		/*
1095e1199815SMickaël Salaün 		 * Try to confuse the deny access by denying write (but not
1096e1199815SMickaël Salaün 		 * read) access via its grandparent directory.
1097e1199815SMickaël Salaün 		 */
1098e1199815SMickaël Salaün 		{
1099e1199815SMickaël Salaün 			.path = dir_s1d2,
1100e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1101e1199815SMickaël Salaün 		},
1102135464f9SMickaël Salaün 		{},
1103e1199815SMickaël Salaün 	};
1104e1199815SMickaël Salaün 	const struct rule layer5_read[] = {
1105e1199815SMickaël Salaün 		/*
1106e1199815SMickaël Salaün 		 * Try to override layer2's deny read access by explicitly
1107e1199815SMickaël Salaün 		 * allowing read access via file1_s1d3's grandparent.
1108e1199815SMickaël Salaün 		 */
1109e1199815SMickaël Salaün 		{
1110e1199815SMickaël Salaün 			.path = dir_s1d2,
1111e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1112e1199815SMickaël Salaün 		},
1113135464f9SMickaël Salaün 		{},
1114e1199815SMickaël Salaün 	};
1115e1199815SMickaël Salaün 	const struct rule layer6_execute[] = {
1116e1199815SMickaël Salaün 		/*
1117e1199815SMickaël Salaün 		 * Restricts an unrelated file hierarchy with a new access
1118e1199815SMickaël Salaün 		 * (non-overlapping) type.
1119e1199815SMickaël Salaün 		 */
1120e1199815SMickaël Salaün 		{
1121e1199815SMickaël Salaün 			.path = dir_s2d1,
1122e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
1123e1199815SMickaël Salaün 		},
1124135464f9SMickaël Salaün 		{},
1125e1199815SMickaël Salaün 	};
1126e1199815SMickaël Salaün 	const struct rule layer7_read_write[] = {
1127e1199815SMickaël Salaün 		/*
1128e1199815SMickaël Salaün 		 * Finally, denies read access to file1_s1d3 via its
1129e1199815SMickaël Salaün 		 * grandparent.
1130e1199815SMickaël Salaün 		 */
1131e1199815SMickaël Salaün 		{
1132e1199815SMickaël Salaün 			.path = dir_s1d2,
1133e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1134e1199815SMickaël Salaün 		},
1135135464f9SMickaël Salaün 		{},
1136e1199815SMickaël Salaün 	};
1137e1199815SMickaël Salaün 	int ruleset_fd;
1138e1199815SMickaël Salaün 
1139e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
1140e1199815SMickaël Salaün 				    layer1_read);
1141e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1142e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1143e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1144e1199815SMickaël Salaün 
1145e1199815SMickaël Salaün 	/* Checks that read access is granted for file1_s1d3 with layer 1. */
1146e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1147e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1148e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1149e1199815SMickaël Salaün 
1150371183faSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata,
1151371183faSMickaël Salaün 				    LANDLOCK_ACCESS_FS_READ_FILE |
1152371183faSMickaël Salaün 					    LANDLOCK_ACCESS_FS_WRITE_FILE,
1153371183faSMickaël Salaün 				    layer2_read_write);
1154e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1155e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1156e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1157e1199815SMickaël Salaün 
1158e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 2. */
1159e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1160e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1161e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1162e1199815SMickaël Salaün 
1163e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
1164e1199815SMickaël Salaün 				    layer3_read);
1165e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1166e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1167e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1168e1199815SMickaël Salaün 
1169e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 3. */
1170e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1171e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1172e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1173e1199815SMickaël Salaün 
1174e1199815SMickaël Salaün 	/* This time, denies write access for the file hierarchy. */
1175371183faSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata,
1176371183faSMickaël Salaün 				    LANDLOCK_ACCESS_FS_READ_FILE |
1177371183faSMickaël Salaün 					    LANDLOCK_ACCESS_FS_WRITE_FILE,
1178371183faSMickaël Salaün 				    layer4_read_write);
1179e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1180e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1181e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1182e1199815SMickaël Salaün 
1183e1199815SMickaël Salaün 	/*
1184e1199815SMickaël Salaün 	 * Checks that the only change with layer 4 is that write access is
1185e1199815SMickaël Salaün 	 * denied.
1186e1199815SMickaël Salaün 	 */
1187e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1188e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1189e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1190e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1191e1199815SMickaël Salaün 
1192e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
1193e1199815SMickaël Salaün 				    layer5_read);
1194e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1195e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1196e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1197e1199815SMickaël Salaün 
1198e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 5. */
1199e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1200e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1201e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1202e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1203e1199815SMickaël Salaün 
1204e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE,
1205e1199815SMickaël Salaün 				    layer6_execute);
1206e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1207e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1208e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1209e1199815SMickaël Salaün 
1210e1199815SMickaël Salaün 	/* Checks that previous access rights are unchanged with layer 6. */
1211e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1212e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1213e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1214e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1215e1199815SMickaël Salaün 
1216371183faSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata,
1217371183faSMickaël Salaün 				    LANDLOCK_ACCESS_FS_READ_FILE |
1218371183faSMickaël Salaün 					    LANDLOCK_ACCESS_FS_WRITE_FILE,
1219371183faSMickaël Salaün 				    layer7_read_write);
1220e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1221e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1222e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1223e1199815SMickaël Salaün 
1224e1199815SMickaël Salaün 	/* Checks read access is now denied with layer 7. */
1225e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
1226e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1227e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1228e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1229e1199815SMickaël Salaün }
1230e1199815SMickaël Salaün 
TEST_F_FORK(layout1,inherit_subset)1231e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_subset)
1232e1199815SMickaël Salaün {
1233e1199815SMickaël Salaün 	const struct rule rules[] = {
1234e1199815SMickaël Salaün 		{
1235e1199815SMickaël Salaün 			.path = dir_s1d2,
1236e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
1237e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_READ_DIR,
1238e1199815SMickaël Salaün 		},
1239135464f9SMickaël Salaün 		{},
1240e1199815SMickaël Salaün 	};
1241e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1242e1199815SMickaël Salaün 
1243e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1244e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1245e1199815SMickaël Salaün 
1246e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1247e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1248e1199815SMickaël Salaün 
1249e1199815SMickaël Salaün 	/* Write access is forbidden. */
1250e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1251e1199815SMickaël Salaün 	/* Readdir access is allowed. */
1252e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1253e1199815SMickaël Salaün 
1254e1199815SMickaël Salaün 	/* Write access is forbidden. */
1255e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1256e1199815SMickaël Salaün 	/* Readdir access is allowed. */
1257e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1258e1199815SMickaël Salaün 
1259e1199815SMickaël Salaün 	/*
1260e1199815SMickaël Salaün 	 * Tests shared rule extension: the following rules should not grant
1261e1199815SMickaël Salaün 	 * any new access, only remove some.  Once enforced, these rules are
1262e1199815SMickaël Salaün 	 * ANDed with the previous ones.
1263e1199815SMickaël Salaün 	 */
1264e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
1265e1199815SMickaël Salaün 			 dir_s1d2);
1266e1199815SMickaël Salaün 	/*
1267e1199815SMickaël Salaün 	 * According to ruleset_fd, dir_s1d2 should now have the
1268e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE
1269e1199815SMickaël Salaün 	 * access rights (even if this directory is opened a second time).
1270e1199815SMickaël Salaün 	 * However, when enforcing this updated ruleset, the ruleset tied to
1271e1199815SMickaël Salaün 	 * the current process (i.e. its domain) will still only have the
1272e1199815SMickaël Salaün 	 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and
1273e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but
1274e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would
1275e1199815SMickaël Salaün 	 * be a privilege escalation.
1276e1199815SMickaël Salaün 	 */
1277e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1278e1199815SMickaël Salaün 
1279e1199815SMickaël Salaün 	/* Same tests and results as above. */
1280e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1281e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1282e1199815SMickaël Salaün 
1283e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d2. */
1284e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1285e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1286e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1287e1199815SMickaël Salaün 
1288e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d3. */
1289e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1290e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1291e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1292e1199815SMickaël Salaün 
1293e1199815SMickaël Salaün 	/*
1294e1199815SMickaël Salaün 	 * Try to get more privileges by adding new access rights to the parent
1295e1199815SMickaël Salaün 	 * directory: dir_s1d1.
1296e1199815SMickaël Salaün 	 */
1297e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1);
1298e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1299e1199815SMickaël Salaün 
1300e1199815SMickaël Salaün 	/* Same tests and results as above. */
1301e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1302e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1303e1199815SMickaël Salaün 
1304e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d2. */
1305e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1306e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1307e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1308e1199815SMickaël Salaün 
1309e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d3. */
1310e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1311e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1312e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1313e1199815SMickaël Salaün 
1314e1199815SMickaël Salaün 	/*
1315e1199815SMickaël Salaün 	 * Now, dir_s1d3 get a new rule tied to it, only allowing
1316e1199815SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_WRITE_FILE.  The (kernel internal) difference is
1317e1199815SMickaël Salaün 	 * that there was no rule tied to it before.
1318e1199815SMickaël Salaün 	 */
1319e1199815SMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
1320e1199815SMickaël Salaün 			 dir_s1d3);
1321e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1322e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1323e1199815SMickaël Salaün 
1324e1199815SMickaël Salaün 	/*
1325e1199815SMickaël Salaün 	 * Same tests and results as above, except for open(dir_s1d3) which is
1326e1199815SMickaël Salaün 	 * now denied because the new rule mask the rule previously inherited
1327e1199815SMickaël Salaün 	 * from dir_s1d2.
1328e1199815SMickaël Salaün 	 */
1329e1199815SMickaël Salaün 
1330e1199815SMickaël Salaün 	/* Same tests and results as above. */
1331e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1332e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1333e1199815SMickaël Salaün 
1334e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d2. */
1335e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1336e1199815SMickaël Salaün 	/* Readdir access is still allowed. */
1337e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1338e1199815SMickaël Salaün 
1339e1199815SMickaël Salaün 	/* It is still forbidden to write in file1_s1d3. */
1340e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1341e1199815SMickaël Salaün 	/*
1342e1199815SMickaël Salaün 	 * Readdir of dir_s1d3 is still allowed because of the OR policy inside
1343e1199815SMickaël Salaün 	 * the same layer.
1344e1199815SMickaël Salaün 	 */
1345e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1346e1199815SMickaël Salaün }
1347e1199815SMickaël Salaün 
TEST_F_FORK(layout1,inherit_superset)1348e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_superset)
1349e1199815SMickaël Salaün {
1350e1199815SMickaël Salaün 	const struct rule rules[] = {
1351e1199815SMickaël Salaün 		{
1352e1199815SMickaël Salaün 			.path = dir_s1d3,
1353e1199815SMickaël Salaün 			.access = ACCESS_RO,
1354e1199815SMickaël Salaün 		},
1355135464f9SMickaël Salaün 		{},
1356e1199815SMickaël Salaün 	};
1357e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1358e1199815SMickaël Salaün 
1359e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1360e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1361e1199815SMickaël Salaün 
1362e1199815SMickaël Salaün 	/* Readdir access is denied for dir_s1d2. */
1363e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1364e1199815SMickaël Salaün 	/* Readdir access is allowed for dir_s1d3. */
1365e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1366e1199815SMickaël Salaün 	/* File access is allowed for file1_s1d3. */
1367e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1368e1199815SMickaël Salaün 
1369e1199815SMickaël Salaün 	/* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */
1370371183faSMickaël Salaün 	add_path_beneath(_metadata, ruleset_fd,
1371371183faSMickaël Salaün 			 LANDLOCK_ACCESS_FS_READ_FILE |
1372371183faSMickaël Salaün 				 LANDLOCK_ACCESS_FS_READ_DIR,
1373371183faSMickaël Salaün 			 dir_s1d2);
1374e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1375e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1376e1199815SMickaël Salaün 
1377e1199815SMickaël Salaün 	/* Readdir access is still denied for dir_s1d2. */
1378e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1379e1199815SMickaël Salaün 	/* Readdir access is still allowed for dir_s1d3. */
1380e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1381e1199815SMickaël Salaün 	/* File access is still allowed for file1_s1d3. */
1382e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1383e1199815SMickaël Salaün }
1384e1199815SMickaël Salaün 
TEST_F_FORK(layout0,max_layers)1385592efeb4SMickaël Salaün TEST_F_FORK(layout0, max_layers)
1386e1199815SMickaël Salaün {
1387e1199815SMickaël Salaün 	int i, err;
1388e1199815SMickaël Salaün 	const struct rule rules[] = {
1389e1199815SMickaël Salaün 		{
1390592efeb4SMickaël Salaün 			.path = TMP_DIR,
1391e1199815SMickaël Salaün 			.access = ACCESS_RO,
1392e1199815SMickaël Salaün 		},
1393135464f9SMickaël Salaün 		{},
1394e1199815SMickaël Salaün 	};
1395e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1396e1199815SMickaël Salaün 
1397e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
139875c542d6SMickaël Salaün 	for (i = 0; i < 16; i++)
1399e1199815SMickaël Salaün 		enforce_ruleset(_metadata, ruleset_fd);
1400e1199815SMickaël Salaün 
1401e1199815SMickaël Salaün 	for (i = 0; i < 2; i++) {
1402e1199815SMickaël Salaün 		err = landlock_restrict_self(ruleset_fd, 0);
1403e1199815SMickaël Salaün 		ASSERT_EQ(-1, err);
1404e1199815SMickaël Salaün 		ASSERT_EQ(E2BIG, errno);
1405e1199815SMickaël Salaün 	}
1406e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1407e1199815SMickaël Salaün }
1408e1199815SMickaël Salaün 
TEST_F_FORK(layout1,empty_or_same_ruleset)1409e1199815SMickaël Salaün TEST_F_FORK(layout1, empty_or_same_ruleset)
1410e1199815SMickaël Salaün {
1411e1199815SMickaël Salaün 	struct landlock_ruleset_attr ruleset_attr = {};
1412e1199815SMickaël Salaün 	int ruleset_fd;
1413e1199815SMickaël Salaün 
1414e1199815SMickaël Salaün 	/* Tests empty handled_access_fs. */
1415371183faSMickaël Salaün 	ruleset_fd =
1416371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1417e1199815SMickaël Salaün 	ASSERT_LE(-1, ruleset_fd);
1418e1199815SMickaël Salaün 	ASSERT_EQ(ENOMSG, errno);
1419e1199815SMickaël Salaün 
1420e1199815SMickaël Salaün 	/* Enforces policy which deny read access to all files. */
1421e1199815SMickaël Salaün 	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE;
1422371183faSMickaël Salaün 	ruleset_fd =
1423371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1424e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1425e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1426e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1427e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1428e1199815SMickaël Salaün 
1429e1199815SMickaël Salaün 	/* Nests a policy which deny read access to all directories. */
1430e1199815SMickaël Salaün 	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR;
1431371183faSMickaël Salaün 	ruleset_fd =
1432371183faSMickaël Salaün 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1433e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1434e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1435e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1436e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1437e1199815SMickaël Salaün 
1438e1199815SMickaël Salaün 	/* Enforces a second time with the same ruleset. */
1439e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1440e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1441e1199815SMickaël Salaün }
1442e1199815SMickaël Salaün 
TEST_F_FORK(layout1,rule_on_mountpoint)1443e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_on_mountpoint)
1444e1199815SMickaël Salaün {
1445e1199815SMickaël Salaün 	const struct rule rules[] = {
1446e1199815SMickaël Salaün 		{
1447e1199815SMickaël Salaün 			.path = dir_s1d1,
1448e1199815SMickaël Salaün 			.access = ACCESS_RO,
1449e1199815SMickaël Salaün 		},
1450e1199815SMickaël Salaün 		{
1451e1199815SMickaël Salaün 			/* dir_s3d2 is a mount point. */
1452e1199815SMickaël Salaün 			.path = dir_s3d2,
1453e1199815SMickaël Salaün 			.access = ACCESS_RO,
1454e1199815SMickaël Salaün 		},
1455135464f9SMickaël Salaün 		{},
1456e1199815SMickaël Salaün 	};
1457e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1458e1199815SMickaël Salaün 
1459e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1460e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1461e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1462e1199815SMickaël Salaün 
1463e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1464e1199815SMickaël Salaün 
1465e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1466e1199815SMickaël Salaün 
1467e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY));
1468e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1469e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1470e1199815SMickaël Salaün }
1471e1199815SMickaël Salaün 
TEST_F_FORK(layout1,rule_over_mountpoint)1472e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_mountpoint)
1473e1199815SMickaël Salaün {
1474e1199815SMickaël Salaün 	const struct rule rules[] = {
1475e1199815SMickaël Salaün 		{
1476e1199815SMickaël Salaün 			.path = dir_s1d1,
1477e1199815SMickaël Salaün 			.access = ACCESS_RO,
1478e1199815SMickaël Salaün 		},
1479e1199815SMickaël Salaün 		{
1480e1199815SMickaël Salaün 			/* dir_s3d2 is a mount point. */
1481e1199815SMickaël Salaün 			.path = dir_s3d1,
1482e1199815SMickaël Salaün 			.access = ACCESS_RO,
1483e1199815SMickaël Salaün 		},
1484135464f9SMickaël Salaün 		{},
1485e1199815SMickaël Salaün 	};
1486e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1487e1199815SMickaël Salaün 
1488e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1489e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1490e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1491e1199815SMickaël Salaün 
1492e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1493e1199815SMickaël Salaün 
1494e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1495e1199815SMickaël Salaün 
1496e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
1497e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1498e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1499e1199815SMickaël Salaün }
1500e1199815SMickaël Salaün 
1501e1199815SMickaël Salaün /*
1502e1199815SMickaël Salaün  * This test verifies that we can apply a landlock rule on the root directory
1503e1199815SMickaël Salaün  * (which might require special handling).
1504e1199815SMickaël Salaün  */
TEST_F_FORK(layout1,rule_over_root_allow_then_deny)1505e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_allow_then_deny)
1506e1199815SMickaël Salaün {
1507e1199815SMickaël Salaün 	struct rule rules[] = {
1508e1199815SMickaël Salaün 		{
1509e1199815SMickaël Salaün 			.path = "/",
1510e1199815SMickaël Salaün 			.access = ACCESS_RO,
1511e1199815SMickaël Salaün 		},
1512135464f9SMickaël Salaün 		{},
1513e1199815SMickaël Salaün 	};
1514e1199815SMickaël Salaün 	int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1515e1199815SMickaël Salaün 
1516e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1517e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1518e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1519e1199815SMickaël Salaün 
1520e1199815SMickaël Salaün 	/* Checks allowed access. */
1521e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("/", O_RDONLY));
1522e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1523e1199815SMickaël Salaün 
1524e1199815SMickaël Salaün 	rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE;
1525e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1526e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1527e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1528e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1529e1199815SMickaël Salaün 
1530e1199815SMickaël Salaün 	/* Checks denied access (on a directory). */
1531e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1532e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1533e1199815SMickaël Salaün }
1534e1199815SMickaël Salaün 
TEST_F_FORK(layout1,rule_over_root_deny)1535e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_deny)
1536e1199815SMickaël Salaün {
1537e1199815SMickaël Salaün 	const struct rule rules[] = {
1538e1199815SMickaël Salaün 		{
1539e1199815SMickaël Salaün 			.path = "/",
1540e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
1541e1199815SMickaël Salaün 		},
1542135464f9SMickaël Salaün 		{},
1543e1199815SMickaël Salaün 	};
1544e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1545e1199815SMickaël Salaün 
1546e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1547e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1548e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1549e1199815SMickaël Salaün 
1550e1199815SMickaël Salaün 	/* Checks denied access (on a directory). */
1551e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1552e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1553e1199815SMickaël Salaün }
1554e1199815SMickaël Salaün 
TEST_F_FORK(layout1,rule_inside_mount_ns)1555e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_inside_mount_ns)
1556e1199815SMickaël Salaün {
1557e1199815SMickaël Salaün 	const struct rule rules[] = {
1558e1199815SMickaël Salaün 		{
1559e1199815SMickaël Salaün 			.path = "s3d3",
1560e1199815SMickaël Salaün 			.access = ACCESS_RO,
1561e1199815SMickaël Salaün 		},
1562135464f9SMickaël Salaün 		{},
1563e1199815SMickaël Salaün 	};
1564e1199815SMickaël Salaün 	int ruleset_fd;
1565e1199815SMickaël Salaün 
1566e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
156787129ef1SMickaël Salaün 	ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3))
1568371183faSMickaël Salaün 	{
1569e1199815SMickaël Salaün 		TH_LOG("Failed to pivot root: %s", strerror(errno));
1570e1199815SMickaël Salaün 	};
1571e1199815SMickaël Salaün 	ASSERT_EQ(0, chdir("/"));
1572e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1573e1199815SMickaël Salaün 
1574e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1575e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1576e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1577e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1578e1199815SMickaël Salaün 
1579e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open("s3d3", O_RDONLY));
1580e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1581e1199815SMickaël Salaün }
1582e1199815SMickaël Salaün 
TEST_F_FORK(layout1,mount_and_pivot)1583e1199815SMickaël Salaün TEST_F_FORK(layout1, mount_and_pivot)
1584e1199815SMickaël Salaün {
1585e1199815SMickaël Salaün 	const struct rule rules[] = {
1586e1199815SMickaël Salaün 		{
1587e1199815SMickaël Salaün 			.path = dir_s3d2,
1588e1199815SMickaël Salaün 			.access = ACCESS_RO,
1589e1199815SMickaël Salaün 		},
1590135464f9SMickaël Salaün 		{},
1591e1199815SMickaël Salaün 	};
1592e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1593e1199815SMickaël Salaün 
1594e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1595e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1596e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1597e1199815SMickaël Salaün 
1598e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
1599e1199815SMickaël Salaün 	ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
1600e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
160187129ef1SMickaël Salaün 	ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));
1602e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
1603e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1604e1199815SMickaël Salaün }
1605e1199815SMickaël Salaün 
TEST_F_FORK(layout1,move_mount)1606e1199815SMickaël Salaün TEST_F_FORK(layout1, move_mount)
1607e1199815SMickaël Salaün {
1608e1199815SMickaël Salaün 	const struct rule rules[] = {
1609e1199815SMickaël Salaün 		{
1610e1199815SMickaël Salaün 			.path = dir_s3d2,
1611e1199815SMickaël Salaün 			.access = ACCESS_RO,
1612e1199815SMickaël Salaün 		},
1613135464f9SMickaël Salaün 		{},
1614e1199815SMickaël Salaün 	};
1615e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1616e1199815SMickaël Salaün 
1617e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1618e1199815SMickaël Salaün 
1619e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
162087129ef1SMickaël Salaün 	ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1621371183faSMickaël Salaün 			     dir_s1d2, 0))
1622371183faSMickaël Salaün 	{
1623e1199815SMickaël Salaün 		TH_LOG("Failed to move mount: %s", strerror(errno));
1624e1199815SMickaël Salaün 	}
1625e1199815SMickaël Salaün 
162687129ef1SMickaël Salaün 	ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,
1627e1199815SMickaël Salaün 			     dir_s3d2, 0));
1628e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1629e1199815SMickaël Salaün 
1630e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1631e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1632e1199815SMickaël Salaün 
1633e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
163487129ef1SMickaël Salaün 	ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1635e1199815SMickaël Salaün 			      dir_s1d2, 0));
1636e1199815SMickaël Salaün 	ASSERT_EQ(EPERM, errno);
1637e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1638e1199815SMickaël Salaün }
1639e1199815SMickaël Salaün 
TEST_F_FORK(layout1,release_inodes)1640e1199815SMickaël Salaün TEST_F_FORK(layout1, release_inodes)
1641e1199815SMickaël Salaün {
1642e1199815SMickaël Salaün 	const struct rule rules[] = {
1643e1199815SMickaël Salaün 		{
1644e1199815SMickaël Salaün 			.path = dir_s1d1,
1645e1199815SMickaël Salaün 			.access = ACCESS_RO,
1646e1199815SMickaël Salaün 		},
1647e1199815SMickaël Salaün 		{
1648e1199815SMickaël Salaün 			.path = dir_s3d2,
1649e1199815SMickaël Salaün 			.access = ACCESS_RO,
1650e1199815SMickaël Salaün 		},
1651e1199815SMickaël Salaün 		{
1652e1199815SMickaël Salaün 			.path = dir_s3d3,
1653e1199815SMickaël Salaün 			.access = ACCESS_RO,
1654e1199815SMickaël Salaün 		},
1655135464f9SMickaël Salaün 		{},
1656e1199815SMickaël Salaün 	};
1657e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1658e1199815SMickaël Salaün 
1659e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1660e1199815SMickaël Salaün 	/* Unmount a file hierarchy while it is being used by a ruleset. */
1661e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
1662e1199815SMickaël Salaün 	ASSERT_EQ(0, umount(dir_s3d2));
1663e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
1664e1199815SMickaël Salaün 
1665e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1666e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1667e1199815SMickaël Salaün 
1668e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
1669e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));
1670e1199815SMickaël Salaün 	/* This dir_s3d3 would not be allowed and does not exist anyway. */
1671e1199815SMickaël Salaün 	ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY));
1672e1199815SMickaël Salaün }
1673e1199815SMickaël Salaün 
1674e1199815SMickaël Salaün enum relative_access {
1675e1199815SMickaël Salaün 	REL_OPEN,
1676e1199815SMickaël Salaün 	REL_CHDIR,
1677e1199815SMickaël Salaün 	REL_CHROOT_ONLY,
1678e1199815SMickaël Salaün 	REL_CHROOT_CHDIR,
1679e1199815SMickaël Salaün };
1680e1199815SMickaël Salaün 
test_relative_path(struct __test_metadata * const _metadata,const enum relative_access rel)1681e1199815SMickaël Salaün static void test_relative_path(struct __test_metadata *const _metadata,
1682e1199815SMickaël Salaün 			       const enum relative_access rel)
1683e1199815SMickaël Salaün {
1684e1199815SMickaël Salaün 	/*
1685e1199815SMickaël Salaün 	 * Common layer to check that chroot doesn't ignore it (i.e. a chroot
1686e1199815SMickaël Salaün 	 * is not a disconnected root directory).
1687e1199815SMickaël Salaün 	 */
1688e1199815SMickaël Salaün 	const struct rule layer1_base[] = {
1689e1199815SMickaël Salaün 		{
1690e1199815SMickaël Salaün 			.path = TMP_DIR,
1691e1199815SMickaël Salaün 			.access = ACCESS_RO,
1692e1199815SMickaël Salaün 		},
1693135464f9SMickaël Salaün 		{},
1694e1199815SMickaël Salaün 	};
1695e1199815SMickaël Salaün 	const struct rule layer2_subs[] = {
1696e1199815SMickaël Salaün 		{
1697e1199815SMickaël Salaün 			.path = dir_s1d2,
1698e1199815SMickaël Salaün 			.access = ACCESS_RO,
1699e1199815SMickaël Salaün 		},
1700e1199815SMickaël Salaün 		{
1701e1199815SMickaël Salaün 			.path = dir_s2d2,
1702e1199815SMickaël Salaün 			.access = ACCESS_RO,
1703e1199815SMickaël Salaün 		},
1704135464f9SMickaël Salaün 		{},
1705e1199815SMickaël Salaün 	};
1706e1199815SMickaël Salaün 	int dirfd, ruleset_fd;
1707e1199815SMickaël Salaün 
1708e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
1709e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1710e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1711e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1712e1199815SMickaël Salaün 
1713e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs);
1714e1199815SMickaël Salaün 
1715e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1716e1199815SMickaël Salaün 	switch (rel) {
1717e1199815SMickaël Salaün 	case REL_OPEN:
1718e1199815SMickaël Salaün 	case REL_CHDIR:
1719e1199815SMickaël Salaün 		break;
1720e1199815SMickaël Salaün 	case REL_CHROOT_ONLY:
1721e1199815SMickaël Salaün 		ASSERT_EQ(0, chdir(dir_s2d2));
1722e1199815SMickaël Salaün 		break;
1723e1199815SMickaël Salaün 	case REL_CHROOT_CHDIR:
1724e1199815SMickaël Salaün 		ASSERT_EQ(0, chdir(dir_s1d2));
1725e1199815SMickaël Salaün 		break;
1726e1199815SMickaël Salaün 	default:
1727e1199815SMickaël Salaün 		ASSERT_TRUE(false);
1728e1199815SMickaël Salaün 		return;
1729e1199815SMickaël Salaün 	}
1730e1199815SMickaël Salaün 
1731e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_CHROOT);
1732e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1733e1199815SMickaël Salaün 
1734e1199815SMickaël Salaün 	switch (rel) {
1735e1199815SMickaël Salaün 	case REL_OPEN:
1736e1199815SMickaël Salaün 		dirfd = open(dir_s1d2, O_DIRECTORY);
1737e1199815SMickaël Salaün 		ASSERT_LE(0, dirfd);
1738e1199815SMickaël Salaün 		break;
1739e1199815SMickaël Salaün 	case REL_CHDIR:
1740e1199815SMickaël Salaün 		ASSERT_EQ(0, chdir(dir_s1d2));
1741e1199815SMickaël Salaün 		dirfd = AT_FDCWD;
1742e1199815SMickaël Salaün 		break;
1743e1199815SMickaël Salaün 	case REL_CHROOT_ONLY:
1744e1199815SMickaël Salaün 		/* Do chroot into dir_s1d2 (relative to dir_s2d2). */
1745371183faSMickaël Salaün 		ASSERT_EQ(0, chroot("../../s1d1/s1d2"))
1746371183faSMickaël Salaün 		{
1747e1199815SMickaël Salaün 			TH_LOG("Failed to chroot: %s", strerror(errno));
1748e1199815SMickaël Salaün 		}
1749e1199815SMickaël Salaün 		dirfd = AT_FDCWD;
1750e1199815SMickaël Salaün 		break;
1751e1199815SMickaël Salaün 	case REL_CHROOT_CHDIR:
1752e1199815SMickaël Salaün 		/* Do chroot into dir_s1d2. */
1753371183faSMickaël Salaün 		ASSERT_EQ(0, chroot("."))
1754371183faSMickaël Salaün 		{
1755e1199815SMickaël Salaün 			TH_LOG("Failed to chroot: %s", strerror(errno));
1756e1199815SMickaël Salaün 		}
1757e1199815SMickaël Salaün 		dirfd = AT_FDCWD;
1758e1199815SMickaël Salaün 		break;
1759e1199815SMickaël Salaün 	}
1760e1199815SMickaël Salaün 
1761e1199815SMickaël Salaün 	ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES,
1762e1199815SMickaël Salaün 		  test_open_rel(dirfd, "..", O_RDONLY));
1763e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY));
1764e1199815SMickaël Salaün 
1765e1199815SMickaël Salaün 	if (rel == REL_CHROOT_ONLY) {
1766e1199815SMickaël Salaün 		/* The current directory is dir_s2d2. */
1767e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY));
1768e1199815SMickaël Salaün 	} else {
1769e1199815SMickaël Salaün 		/* The current directory is dir_s1d2. */
1770e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY));
1771e1199815SMickaël Salaün 	}
1772e1199815SMickaël Salaün 
1773e1199815SMickaël Salaün 	if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) {
1774e1199815SMickaël Salaün 		/* Checks the root dir_s1d2. */
1775e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY));
1776e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY));
1777e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY));
1778e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY));
1779e1199815SMickaël Salaün 	}
1780e1199815SMickaël Salaün 
1781e1199815SMickaël Salaün 	if (rel != REL_CHROOT_CHDIR) {
1782e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY));
1783e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY));
1784371183faSMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3",
1785371183faSMickaël Salaün 					   O_RDONLY));
1786e1199815SMickaël Salaün 
1787e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY));
1788e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY));
1789371183faSMickaël Salaün 		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3",
1790371183faSMickaël Salaün 					   O_RDONLY));
1791e1199815SMickaël Salaün 	}
1792e1199815SMickaël Salaün 
1793e1199815SMickaël Salaün 	if (rel == REL_OPEN)
1794e1199815SMickaël Salaün 		ASSERT_EQ(0, close(dirfd));
1795e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1796e1199815SMickaël Salaün }
1797e1199815SMickaël Salaün 
TEST_F_FORK(layout1,relative_open)1798e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_open)
1799e1199815SMickaël Salaün {
1800e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_OPEN);
1801e1199815SMickaël Salaün }
1802e1199815SMickaël Salaün 
TEST_F_FORK(layout1,relative_chdir)1803e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chdir)
1804e1199815SMickaël Salaün {
1805e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_CHDIR);
1806e1199815SMickaël Salaün }
1807e1199815SMickaël Salaün 
TEST_F_FORK(layout1,relative_chroot_only)1808e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_only)
1809e1199815SMickaël Salaün {
1810e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_CHROOT_ONLY);
1811e1199815SMickaël Salaün }
1812e1199815SMickaël Salaün 
TEST_F_FORK(layout1,relative_chroot_chdir)1813e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_chdir)
1814e1199815SMickaël Salaün {
1815e1199815SMickaël Salaün 	test_relative_path(_metadata, REL_CHROOT_CHDIR);
1816e1199815SMickaël Salaün }
1817e1199815SMickaël Salaün 
copy_binary(struct __test_metadata * const _metadata,const char * const dst_path)1818e1199815SMickaël Salaün static void copy_binary(struct __test_metadata *const _metadata,
1819e1199815SMickaël Salaün 			const char *const dst_path)
1820e1199815SMickaël Salaün {
1821e1199815SMickaël Salaün 	int dst_fd, src_fd;
1822e1199815SMickaël Salaün 	struct stat statbuf;
1823e1199815SMickaël Salaün 
1824e1199815SMickaël Salaün 	dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC);
1825371183faSMickaël Salaün 	ASSERT_LE(0, dst_fd)
1826371183faSMickaël Salaün 	{
1827371183faSMickaël Salaün 		TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno));
1828e1199815SMickaël Salaün 	}
1829e1199815SMickaël Salaün 	src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC);
1830371183faSMickaël Salaün 	ASSERT_LE(0, src_fd)
1831371183faSMickaël Salaün 	{
1832e1199815SMickaël Salaün 		TH_LOG("Failed to open \"" BINARY_PATH "\": %s",
1833e1199815SMickaël Salaün 		       strerror(errno));
1834e1199815SMickaël Salaün 	}
1835e1199815SMickaël Salaün 	ASSERT_EQ(0, fstat(src_fd, &statbuf));
1836371183faSMickaël Salaün 	ASSERT_EQ(statbuf.st_size,
1837371183faSMickaël Salaün 		  sendfile(dst_fd, src_fd, 0, statbuf.st_size));
1838e1199815SMickaël Salaün 	ASSERT_EQ(0, close(src_fd));
1839e1199815SMickaël Salaün 	ASSERT_EQ(0, close(dst_fd));
1840e1199815SMickaël Salaün }
1841e1199815SMickaël Salaün 
test_execute(struct __test_metadata * const _metadata,const int err,const char * const path)1842371183faSMickaël Salaün static void test_execute(struct __test_metadata *const _metadata, const int err,
1843371183faSMickaël Salaün 			 const char *const path)
1844e1199815SMickaël Salaün {
1845e1199815SMickaël Salaün 	int status;
1846e1199815SMickaël Salaün 	char *const argv[] = { (char *)path, NULL };
1847e1199815SMickaël Salaün 	const pid_t child = fork();
1848e1199815SMickaël Salaün 
1849e1199815SMickaël Salaün 	ASSERT_LE(0, child);
1850e1199815SMickaël Salaün 	if (child == 0) {
1851371183faSMickaël Salaün 		ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL))
1852371183faSMickaël Salaün 		{
1853e1199815SMickaël Salaün 			TH_LOG("Failed to execute \"%s\": %s", path,
1854e1199815SMickaël Salaün 			       strerror(errno));
1855e1199815SMickaël Salaün 		};
1856e1199815SMickaël Salaün 		ASSERT_EQ(err, errno);
1857e1199815SMickaël Salaün 		_exit(_metadata->passed ? 2 : 1);
1858e1199815SMickaël Salaün 		return;
1859e1199815SMickaël Salaün 	}
1860e1199815SMickaël Salaün 	ASSERT_EQ(child, waitpid(child, &status, 0));
1861e1199815SMickaël Salaün 	ASSERT_EQ(1, WIFEXITED(status));
1862371183faSMickaël Salaün 	ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status))
1863371183faSMickaël Salaün 	{
1864e1199815SMickaël Salaün 		TH_LOG("Unexpected return code for \"%s\": %s", path,
1865e1199815SMickaël Salaün 		       strerror(errno));
1866e1199815SMickaël Salaün 	};
1867e1199815SMickaël Salaün }
1868e1199815SMickaël Salaün 
TEST_F_FORK(layout1,execute)1869e1199815SMickaël Salaün TEST_F_FORK(layout1, execute)
1870e1199815SMickaël Salaün {
1871e1199815SMickaël Salaün 	const struct rule rules[] = {
1872e1199815SMickaël Salaün 		{
1873e1199815SMickaël Salaün 			.path = dir_s1d2,
1874e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
1875e1199815SMickaël Salaün 		},
1876135464f9SMickaël Salaün 		{},
1877e1199815SMickaël Salaün 	};
1878371183faSMickaël Salaün 	const int ruleset_fd =
1879371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
1880e1199815SMickaël Salaün 
1881e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1882e1199815SMickaël Salaün 	copy_binary(_metadata, file1_s1d1);
1883e1199815SMickaël Salaün 	copy_binary(_metadata, file1_s1d2);
1884e1199815SMickaël Salaün 	copy_binary(_metadata, file1_s1d3);
1885e1199815SMickaël Salaün 
1886e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1887e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1888e1199815SMickaël Salaün 
1889e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1890e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
1891e1199815SMickaël Salaün 	test_execute(_metadata, EACCES, file1_s1d1);
1892e1199815SMickaël Salaün 
1893e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
1894e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
1895e1199815SMickaël Salaün 	test_execute(_metadata, 0, file1_s1d2);
1896e1199815SMickaël Salaün 
1897e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
1898e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1899e1199815SMickaël Salaün 	test_execute(_metadata, 0, file1_s1d3);
1900e1199815SMickaël Salaün }
1901e1199815SMickaël Salaün 
TEST_F_FORK(layout1,link)1902e1199815SMickaël Salaün TEST_F_FORK(layout1, link)
1903e1199815SMickaël Salaün {
19046a1bdd4aSMickaël Salaün 	const struct rule layer1[] = {
1905e1199815SMickaël Salaün 		{
1906e1199815SMickaël Salaün 			.path = dir_s1d2,
1907e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
1908e1199815SMickaël Salaün 		},
1909135464f9SMickaël Salaün 		{},
1910e1199815SMickaël Salaün 	};
19116a1bdd4aSMickaël Salaün 	const struct rule layer2[] = {
19126a1bdd4aSMickaël Salaün 		{
19136a1bdd4aSMickaël Salaün 			.path = dir_s1d3,
19146a1bdd4aSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
19156a1bdd4aSMickaël Salaün 		},
19166a1bdd4aSMickaël Salaün 		{},
19176a1bdd4aSMickaël Salaün 	};
19186a1bdd4aSMickaël Salaün 	int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
1919e1199815SMickaël Salaün 
1920e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1921e1199815SMickaël Salaün 
1922e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
1923e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1924e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
1925e1199815SMickaël Salaün 
1926e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1927e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1928e1199815SMickaël Salaün 
1929e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
1930e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
19316a1bdd4aSMickaël Salaün 
1932e1199815SMickaël Salaün 	/* Denies linking because of reparenting. */
1933e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
1934e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1935e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
1936e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
19376a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
19386a1bdd4aSMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
1939e1199815SMickaël Salaün 
1940e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
1941e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
19426a1bdd4aSMickaël Salaün 
19436a1bdd4aSMickaël Salaün 	/* Prepares for next unlinks. */
19446a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
19456a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
19466a1bdd4aSMickaël Salaün 
19476a1bdd4aSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
19486a1bdd4aSMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
19496a1bdd4aSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
19506a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
19516a1bdd4aSMickaël Salaün 
19526a1bdd4aSMickaël Salaün 	/* Checks that linkind doesn't require the ability to delete a file. */
19536a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
19546a1bdd4aSMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
1955e1199815SMickaël Salaün }
1956e1199815SMickaël Salaün 
test_rename(const char * const oldpath,const char * const newpath)195755e55920SMickaël Salaün static int test_rename(const char *const oldpath, const char *const newpath)
195855e55920SMickaël Salaün {
195955e55920SMickaël Salaün 	if (rename(oldpath, newpath))
196055e55920SMickaël Salaün 		return errno;
196155e55920SMickaël Salaün 	return 0;
196255e55920SMickaël Salaün }
196355e55920SMickaël Salaün 
test_exchange(const char * const oldpath,const char * const newpath)196455e55920SMickaël Salaün static int test_exchange(const char *const oldpath, const char *const newpath)
196555e55920SMickaël Salaün {
196655e55920SMickaël Salaün 	if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE))
196755e55920SMickaël Salaün 		return errno;
196855e55920SMickaël Salaün 	return 0;
196955e55920SMickaël Salaün }
197055e55920SMickaël Salaün 
TEST_F_FORK(layout1,rename_file)1971e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_file)
1972e1199815SMickaël Salaün {
1973e1199815SMickaël Salaün 	const struct rule rules[] = {
1974e1199815SMickaël Salaün 		{
1975e1199815SMickaël Salaün 			.path = dir_s1d3,
1976e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1977e1199815SMickaël Salaün 		},
1978e1199815SMickaël Salaün 		{
1979e1199815SMickaël Salaün 			.path = dir_s2d2,
1980e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1981e1199815SMickaël Salaün 		},
1982135464f9SMickaël Salaün 		{},
1983e1199815SMickaël Salaün 	};
1984371183faSMickaël Salaün 	const int ruleset_fd =
1985371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
1986e1199815SMickaël Salaün 
1987e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
1988e1199815SMickaël Salaün 
1989e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
1990e1199815SMickaël Salaün 
1991e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
1992e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
1993e1199815SMickaël Salaün 
1994e1199815SMickaël Salaün 	/*
1995e1199815SMickaël Salaün 	 * Tries to replace a file, from a directory that allows file removal,
1996e1199815SMickaël Salaün 	 * but to a different directory (which also allows file removal).
1997e1199815SMickaël Salaün 	 */
1998e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3));
1999e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2000e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3,
2001e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2002e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2003e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
2004e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2005e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2006e1199815SMickaël Salaün 
2007e1199815SMickaël Salaün 	/*
2008e1199815SMickaël Salaün 	 * Tries to replace a file, from a directory that denies file removal,
2009e1199815SMickaël Salaün 	 * to a different directory (which allows file removal).
2010e1199815SMickaël Salaün 	 */
2011e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
201255e55920SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2013e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3,
2014e1199815SMickaël Salaün 				RENAME_EXCHANGE));
201555e55920SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2016e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3,
2017e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2018e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2019e1199815SMickaël Salaün 
2020e1199815SMickaël Salaün 	/* Exchanges files and directories that partially allow removal. */
2021e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1,
2022e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2023e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
20246a1bdd4aSMickaël Salaün 	/* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */
20256a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1));
20266a1bdd4aSMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2027e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2,
2028e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2029e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
20306a1bdd4aSMickaël Salaün 	/* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */
20316a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
20326a1bdd4aSMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2033e1199815SMickaël Salaün 
2034e1199815SMickaël Salaün 	/* Renames files with different parents. */
2035e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
2036e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2037e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2038e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
203955e55920SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2040e1199815SMickaël Salaün 
2041e1199815SMickaël Salaün 	/* Exchanges and renames files with same parent. */
2042e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3,
2043e1199815SMickaël Salaün 			       RENAME_EXCHANGE));
2044e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3));
2045e1199815SMickaël Salaün 
2046e1199815SMickaël Salaün 	/* Exchanges files and directories with same parent, twice. */
2047e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
2048e1199815SMickaël Salaün 			       RENAME_EXCHANGE));
2049e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
2050e1199815SMickaël Salaün 			       RENAME_EXCHANGE));
2051e1199815SMickaël Salaün }
2052e1199815SMickaël Salaün 
TEST_F_FORK(layout1,rename_dir)2053e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_dir)
2054e1199815SMickaël Salaün {
2055e1199815SMickaël Salaün 	const struct rule rules[] = {
2056e1199815SMickaël Salaün 		{
2057e1199815SMickaël Salaün 			.path = dir_s1d2,
2058e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2059e1199815SMickaël Salaün 		},
2060e1199815SMickaël Salaün 		{
2061e1199815SMickaël Salaün 			.path = dir_s2d1,
2062e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2063e1199815SMickaël Salaün 		},
2064135464f9SMickaël Salaün 		{},
2065e1199815SMickaël Salaün 	};
2066371183faSMickaël Salaün 	const int ruleset_fd =
2067371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
2068e1199815SMickaël Salaün 
2069e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2070e1199815SMickaël Salaün 
2071e1199815SMickaël Salaün 	/* Empties dir_s1d3 to allow renaming. */
2072e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2073e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
2074e1199815SMickaël Salaün 
2075e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2076e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2077e1199815SMickaël Salaün 
2078e1199815SMickaël Salaün 	/* Exchanges and renames directory to a different parent. */
2079e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2080e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2081e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2082e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3));
2083e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2084e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
2085e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2086e1199815SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2087e1199815SMickaël Salaün 
2088e1199815SMickaël Salaün 	/*
2089e1199815SMickaël Salaün 	 * Exchanges directory to the same parent, which doesn't allow
2090e1199815SMickaël Salaün 	 * directory removal.
2091e1199815SMickaël Salaün 	 */
2092e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1,
2093e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2094e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
20956a1bdd4aSMickaël Salaün 	/* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */
20966a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1));
20976a1bdd4aSMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2098e1199815SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2,
2099e1199815SMickaël Salaün 				RENAME_EXCHANGE));
2100e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
21016a1bdd4aSMickaël Salaün 	/* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */
21026a1bdd4aSMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
21036a1bdd4aSMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2104e1199815SMickaël Salaün 
2105e1199815SMickaël Salaün 	/*
2106e1199815SMickaël Salaün 	 * Exchanges and renames directory to the same parent, which allows
2107e1199815SMickaël Salaün 	 * directory removal.
2108e1199815SMickaël Salaün 	 */
2109e1199815SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2,
2110e1199815SMickaël Salaün 			       RENAME_EXCHANGE));
2111e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(dir_s1d3));
2112e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
2113e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3));
2114e1199815SMickaël Salaün 	ASSERT_EQ(0, rmdir(dir_s1d3));
2115e1199815SMickaël Salaün }
2116e1199815SMickaël Salaün 
TEST_F_FORK(layout1,reparent_refer)2117f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_refer)
2118f4056b92SMickaël Salaün {
2119f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2120f4056b92SMickaël Salaün 		{
2121f4056b92SMickaël Salaün 			.path = dir_s1d2,
2122f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2123f4056b92SMickaël Salaün 		},
2124f4056b92SMickaël Salaün 		{
2125f4056b92SMickaël Salaün 			.path = dir_s2d2,
2126f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2127f4056b92SMickaël Salaün 		},
2128f4056b92SMickaël Salaün 		{},
2129f4056b92SMickaël Salaün 	};
2130f4056b92SMickaël Salaün 	int ruleset_fd =
2131f4056b92SMickaël Salaün 		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);
2132f4056b92SMickaël Salaün 
2133f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2134f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2135f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2136f4056b92SMickaël Salaün 
2137f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1));
2138f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2139f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2));
2140f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2141f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));
2142f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2143f4056b92SMickaël Salaün 
2144f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1));
2145f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2146f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2));
2147f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2148f4056b92SMickaël Salaün 	/*
2149f4056b92SMickaël Salaün 	 * Moving should only be allowed when the source and the destination
2150f4056b92SMickaël Salaün 	 * parent directory have REFER.
2151f4056b92SMickaël Salaün 	 */
2152f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3));
2153f4056b92SMickaël Salaün 	ASSERT_EQ(ENOTEMPTY, errno);
2154f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d3));
2155f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2156f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3));
2157f4056b92SMickaël Salaün }
2158f4056b92SMickaël Salaün 
215955e55920SMickaël Salaün /* Checks renames beneath dir_s1d1. */
refer_denied_by_default(struct __test_metadata * const _metadata,const struct rule layer1[],const int layer1_err,const struct rule layer2[])216055e55920SMickaël Salaün static void refer_denied_by_default(struct __test_metadata *const _metadata,
216155e55920SMickaël Salaün 				    const struct rule layer1[],
216255e55920SMickaël Salaün 				    const int layer1_err,
216355e55920SMickaël Salaün 				    const struct rule layer2[])
216455e55920SMickaël Salaün {
216555e55920SMickaël Salaün 	int ruleset_fd;
216655e55920SMickaël Salaün 
216755e55920SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
216855e55920SMickaël Salaün 
216955e55920SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
217055e55920SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
217155e55920SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
217255e55920SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
217355e55920SMickaël Salaün 
217455e55920SMickaël Salaün 	/*
217555e55920SMickaël Salaün 	 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to
217655e55920SMickaël Salaün 	 * layer1_err), then it allows some different-parent renames and links.
217755e55920SMickaël Salaün 	 */
217855e55920SMickaël Salaün 	ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2));
217955e55920SMickaël Salaün 	if (layer1_err == 0)
218055e55920SMickaël Salaün 		ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1));
218155e55920SMickaël Salaün 	ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2));
218255e55920SMickaël Salaün 	ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1));
218355e55920SMickaël Salaün 
218455e55920SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
218555e55920SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
218655e55920SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
218755e55920SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
218855e55920SMickaël Salaün 
218955e55920SMickaël Salaün 	/*
219055e55920SMickaël Salaün 	 * Now, either the first or the second layer does not handle
219155e55920SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent
219255e55920SMickaël Salaün 	 * renames and links are denied, thus making the layer handling
219355e55920SMickaël Salaün 	 * LANDLOCK_ACCESS_FS_REFER null and void.
219455e55920SMickaël Salaün 	 */
219555e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2));
219655e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2));
219755e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1));
219855e55920SMickaël Salaün }
219955e55920SMickaël Salaün 
220055e55920SMickaël Salaün const struct rule layer_dir_s1d1_refer[] = {
220155e55920SMickaël Salaün 	{
220255e55920SMickaël Salaün 		.path = dir_s1d1,
220355e55920SMickaël Salaün 		.access = LANDLOCK_ACCESS_FS_REFER,
220455e55920SMickaël Salaün 	},
220555e55920SMickaël Salaün 	{},
220655e55920SMickaël Salaün };
220755e55920SMickaël Salaün 
220855e55920SMickaël Salaün const struct rule layer_dir_s1d1_execute[] = {
220955e55920SMickaël Salaün 	{
221055e55920SMickaël Salaün 		/* Matches a parent directory. */
221155e55920SMickaël Salaün 		.path = dir_s1d1,
221255e55920SMickaël Salaün 		.access = LANDLOCK_ACCESS_FS_EXECUTE,
221355e55920SMickaël Salaün 	},
221455e55920SMickaël Salaün 	{},
221555e55920SMickaël Salaün };
221655e55920SMickaël Salaün 
221755e55920SMickaël Salaün const struct rule layer_dir_s2d1_execute[] = {
221855e55920SMickaël Salaün 	{
221955e55920SMickaël Salaün 		/* Does not match a parent directory. */
222055e55920SMickaël Salaün 		.path = dir_s2d1,
222155e55920SMickaël Salaün 		.access = LANDLOCK_ACCESS_FS_EXECUTE,
222255e55920SMickaël Salaün 	},
222355e55920SMickaël Salaün 	{},
222455e55920SMickaël Salaün };
222555e55920SMickaël Salaün 
222655e55920SMickaël Salaün /*
222755e55920SMickaël Salaün  * Tests precedence over renames: denied by default for different parent
222855e55920SMickaël Salaün  * directories, *with* a rule matching a parent directory, but not directly
222955e55920SMickaël Salaün  * denying access (with MAKE_REG nor REMOVE).
223055e55920SMickaël Salaün  */
TEST_F_FORK(layout1,refer_denied_by_default1)223155e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default1)
223255e55920SMickaël Salaün {
223355e55920SMickaël Salaün 	refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
223455e55920SMickaël Salaün 				layer_dir_s1d1_execute);
223555e55920SMickaël Salaün }
223655e55920SMickaël Salaün 
223755e55920SMickaël Salaün /*
223855e55920SMickaël Salaün  * Same test but this time turning around the ABI version order: the first
223955e55920SMickaël Salaün  * layer does not handle LANDLOCK_ACCESS_FS_REFER.
224055e55920SMickaël Salaün  */
TEST_F_FORK(layout1,refer_denied_by_default2)224155e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default2)
224255e55920SMickaël Salaün {
224355e55920SMickaël Salaün 	refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV,
224455e55920SMickaël Salaün 				layer_dir_s1d1_refer);
224555e55920SMickaël Salaün }
224655e55920SMickaël Salaün 
224755e55920SMickaël Salaün /*
224855e55920SMickaël Salaün  * Tests precedence over renames: denied by default for different parent
224955e55920SMickaël Salaün  * directories, *without* a rule matching a parent directory, but not directly
225055e55920SMickaël Salaün  * denying access (with MAKE_REG nor REMOVE).
225155e55920SMickaël Salaün  */
TEST_F_FORK(layout1,refer_denied_by_default3)225255e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default3)
225355e55920SMickaël Salaün {
225455e55920SMickaël Salaün 	refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
225555e55920SMickaël Salaün 				layer_dir_s2d1_execute);
225655e55920SMickaël Salaün }
225755e55920SMickaël Salaün 
225855e55920SMickaël Salaün /*
225955e55920SMickaël Salaün  * Same test but this time turning around the ABI version order: the first
226055e55920SMickaël Salaün  * layer does not handle LANDLOCK_ACCESS_FS_REFER.
226155e55920SMickaël Salaün  */
TEST_F_FORK(layout1,refer_denied_by_default4)226255e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default4)
226355e55920SMickaël Salaün {
226455e55920SMickaël Salaün 	refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV,
226555e55920SMickaël Salaün 				layer_dir_s1d1_refer);
226655e55920SMickaël Salaün }
226755e55920SMickaël Salaün 
TEST_F_FORK(layout1,reparent_link)2268f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_link)
2269f4056b92SMickaël Salaün {
2270f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2271f4056b92SMickaël Salaün 		{
2272f4056b92SMickaël Salaün 			.path = dir_s1d2,
2273f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2274f4056b92SMickaël Salaün 		},
2275f4056b92SMickaël Salaün 		{
2276f4056b92SMickaël Salaün 			.path = dir_s1d3,
2277f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2278f4056b92SMickaël Salaün 		},
2279f4056b92SMickaël Salaün 		{
2280f4056b92SMickaël Salaün 			.path = dir_s2d2,
2281f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2282f4056b92SMickaël Salaün 		},
2283f4056b92SMickaël Salaün 		{
2284f4056b92SMickaël Salaün 			.path = dir_s2d3,
2285f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2286f4056b92SMickaël Salaün 		},
2287f4056b92SMickaël Salaün 		{},
2288f4056b92SMickaël Salaün 	};
2289f4056b92SMickaël Salaün 	const int ruleset_fd = create_ruleset(
2290f4056b92SMickaël Salaün 		_metadata,
2291f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2292f4056b92SMickaël Salaün 
2293f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2294f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2295f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2296f4056b92SMickaël Salaün 
2297f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
2298f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2299f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2300f4056b92SMickaël Salaün 
2301f4056b92SMickaël Salaün 	/* Denies linking because of missing MAKE_REG. */
2302f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
2303f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2304f4056b92SMickaël Salaün 	/* Denies linking because of missing source and destination REFER. */
2305f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
2306f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2307f4056b92SMickaël Salaün 	/* Denies linking because of missing source REFER. */
2308f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3));
2309f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2310f4056b92SMickaël Salaün 
2311f4056b92SMickaël Salaün 	/* Denies linking because of missing MAKE_REG. */
2312f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1));
2313f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2314f4056b92SMickaël Salaün 	/* Denies linking because of missing destination REFER. */
2315f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2));
2316f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2317f4056b92SMickaël Salaün 
2318f4056b92SMickaël Salaün 	/* Allows linking because of REFER and MAKE_REG. */
2319f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file1_s2d2, file1_s1d3));
2320f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d2));
2321f4056b92SMickaël Salaün 	/* Reverse linking denied because of missing MAKE_REG. */
2322f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2));
2323f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2324f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d3));
2325f4056b92SMickaël Salaün 	/* Checks reverse linking. */
2326f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file1_s2d3));
2327f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2328f4056b92SMickaël Salaün 
2329f4056b92SMickaël Salaün 	/*
2330f4056b92SMickaël Salaün 	 * This is OK for a file link, but it should not be allowed for a
2331f4056b92SMickaël Salaün 	 * directory rename (because of the superset of access rights.
2332f4056b92SMickaël Salaün 	 */
2333f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file1_s2d3, file1_s1d3));
2334f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2335f4056b92SMickaël Salaün 
2336f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
2337f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2338f4056b92SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
2339f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2340f4056b92SMickaël Salaün 
2341f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
2342f4056b92SMickaël Salaün 	ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
2343f4056b92SMickaël Salaün }
2344f4056b92SMickaël Salaün 
TEST_F_FORK(layout1,reparent_rename)2345f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_rename)
2346f4056b92SMickaël Salaün {
2347f4056b92SMickaël Salaün 	/* Same rules as for reparent_link. */
2348f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2349f4056b92SMickaël Salaün 		{
2350f4056b92SMickaël Salaün 			.path = dir_s1d2,
2351f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2352f4056b92SMickaël Salaün 		},
2353f4056b92SMickaël Salaün 		{
2354f4056b92SMickaël Salaün 			.path = dir_s1d3,
2355f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2356f4056b92SMickaël Salaün 		},
2357f4056b92SMickaël Salaün 		{
2358f4056b92SMickaël Salaün 			.path = dir_s2d2,
2359f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2360f4056b92SMickaël Salaün 		},
2361f4056b92SMickaël Salaün 		{
2362f4056b92SMickaël Salaün 			.path = dir_s2d3,
2363f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2364f4056b92SMickaël Salaün 		},
2365f4056b92SMickaël Salaün 		{},
2366f4056b92SMickaël Salaün 	};
2367f4056b92SMickaël Salaün 	const int ruleset_fd = create_ruleset(
2368f4056b92SMickaël Salaün 		_metadata,
2369f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2370f4056b92SMickaël Salaün 
2371f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2372f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2373f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2374f4056b92SMickaël Salaün 
2375f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2376f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2377f4056b92SMickaël Salaün 
2378f4056b92SMickaël Salaün 	/* Denies renaming because of missing MAKE_REG. */
2379f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1,
2380f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2381f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2382f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1,
2383f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2384f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2385f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
2386f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
2387f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2388f4056b92SMickaël Salaün 	/* Even denies same file exchange. */
2389f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1,
2390f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2391f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2392f4056b92SMickaël Salaün 
2393f4056b92SMickaël Salaün 	/* Denies renaming because of missing source and destination REFER. */
2394f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2));
2395f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2396f4056b92SMickaël Salaün 	/*
2397f4056b92SMickaël Salaün 	 * Denies renaming because of missing MAKE_REG, source and destination
2398f4056b92SMickaël Salaün 	 * REFER.
2399f4056b92SMickaël Salaün 	 */
2400f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1,
2401f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2402f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2403f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1,
2404f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2405f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2406f4056b92SMickaël Salaün 
2407f4056b92SMickaël Salaün 	/* Denies renaming because of missing source REFER. */
2408f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2409f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2410f4056b92SMickaël Salaün 	/* Denies renaming because of missing MAKE_REG. */
2411f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3,
2412f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2413f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2414f4056b92SMickaël Salaün 
2415f4056b92SMickaël Salaün 	/* Denies renaming because of missing MAKE_REG. */
2416f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1));
2417f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2418f4056b92SMickaël Salaün 	/* Denies renaming because of missing destination REFER*/
2419f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
2420f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2421f4056b92SMickaël Salaün 
2422f4056b92SMickaël Salaün 	/* Denies exchange because of one missing MAKE_REG. */
2423f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3,
2424f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2425f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2426f4056b92SMickaël Salaün 	/* Allows renaming because of REFER and MAKE_REG. */
2427f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3));
2428f4056b92SMickaël Salaün 
2429f4056b92SMickaël Salaün 	/* Reverse renaming denied because of missing MAKE_REG. */
2430f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2));
2431f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2432f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d3));
2433f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2434f4056b92SMickaël Salaün 
2435f4056b92SMickaël Salaün 	/* Tests reverse renaming. */
2436f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2437f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3,
2438f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2439f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2440f4056b92SMickaël Salaün 
2441f4056b92SMickaël Salaün 	/*
2442f4056b92SMickaël Salaün 	 * This is OK for a file rename, but it should not be allowed for a
2443f4056b92SMickaël Salaün 	 * directory rename (because of the superset of access rights).
2444f4056b92SMickaël Salaün 	 */
2445f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2446f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2447f4056b92SMickaël Salaün 
2448f4056b92SMickaël Salaün 	/*
2449f4056b92SMickaël Salaün 	 * Tests superset restrictions applied to directories.  Not only the
2450f4056b92SMickaël Salaün 	 * dir_s2d3's parent (dir_s2d2) should be taken into account but also
2451f4056b92SMickaël Salaün 	 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right
2452f4056b92SMickaël Salaün 	 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided
2453f4056b92SMickaël Salaün 	 * directly by the moved dir_s2d3.
2454f4056b92SMickaël Salaün 	 */
2455f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3));
2456f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3));
2457f4056b92SMickaël Salaün 	/*
2458f4056b92SMickaël Salaün 	 * The first rename is allowed but not the exchange because dir_s1d3's
2459f4056b92SMickaël Salaün 	 * parent (dir_s1d2) doesn't have REFER.
2460f4056b92SMickaël Salaün 	 */
2461f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
2462f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2463f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2464f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3,
2465f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2466f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2467f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3));
2468f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2469f4056b92SMickaël Salaün 
2470f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3));
2471f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2472f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2));
2473f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2474f4056b92SMickaël Salaün 
2475f4056b92SMickaël Salaün 	/* Renaming in the same directory is always allowed. */
2476f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2));
2477f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3));
2478f4056b92SMickaël Salaün 
2479f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2480f4056b92SMickaël Salaün 	/* Denies because of missing source MAKE_REG and destination REFER. */
2481f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2));
2482f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2483f4056b92SMickaël Salaün 
2484f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2485f4056b92SMickaël Salaün 	/* Denies because of missing source MAKE_REG and REFER. */
2486f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3));
2487f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2488f4056b92SMickaël Salaün }
2489f4056b92SMickaël Salaün 
2490f4056b92SMickaël Salaün static void
reparent_exdev_layers_enforce1(struct __test_metadata * const _metadata)2491f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata)
2492f4056b92SMickaël Salaün {
2493f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2494f4056b92SMickaël Salaün 		{
2495f4056b92SMickaël Salaün 			.path = dir_s1d2,
2496f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2497f4056b92SMickaël Salaün 		},
2498f4056b92SMickaël Salaün 		{
2499f4056b92SMickaël Salaün 			/* Interesting for the layer2 tests. */
2500f4056b92SMickaël Salaün 			.path = dir_s1d3,
2501f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2502f4056b92SMickaël Salaün 		},
2503f4056b92SMickaël Salaün 		{
2504f4056b92SMickaël Salaün 			.path = dir_s2d2,
2505f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2506f4056b92SMickaël Salaün 		},
2507f4056b92SMickaël Salaün 		{
2508f4056b92SMickaël Salaün 			.path = dir_s2d3,
2509f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
2510f4056b92SMickaël Salaün 		},
2511f4056b92SMickaël Salaün 		{},
2512f4056b92SMickaël Salaün 	};
2513f4056b92SMickaël Salaün 	const int ruleset_fd = create_ruleset(
2514f4056b92SMickaël Salaün 		_metadata,
2515f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2516f4056b92SMickaël Salaün 
2517f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2518f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2519f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2520f4056b92SMickaël Salaün }
2521f4056b92SMickaël Salaün 
2522f4056b92SMickaël Salaün static void
reparent_exdev_layers_enforce2(struct __test_metadata * const _metadata)2523f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata)
2524f4056b92SMickaël Salaün {
2525f4056b92SMickaël Salaün 	const struct rule layer2[] = {
2526f4056b92SMickaël Salaün 		{
2527f4056b92SMickaël Salaün 			.path = dir_s2d3,
2528f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_DIR,
2529f4056b92SMickaël Salaün 		},
2530f4056b92SMickaël Salaün 		{},
2531f4056b92SMickaël Salaün 	};
2532f4056b92SMickaël Salaün 	/*
2533f4056b92SMickaël Salaün 	 * Same checks as before but with a second layer and a new MAKE_DIR
2534f4056b92SMickaël Salaün 	 * rule (and no explicit handling of REFER).
2535f4056b92SMickaël Salaün 	 */
2536f4056b92SMickaël Salaün 	const int ruleset_fd =
2537f4056b92SMickaël Salaün 		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2);
2538f4056b92SMickaël Salaün 
2539f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2540f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2541f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2542f4056b92SMickaël Salaün }
2543f4056b92SMickaël Salaün 
TEST_F_FORK(layout1,reparent_exdev_layers_rename1)2544f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename1)
2545f4056b92SMickaël Salaün {
2546f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d2));
2547f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d3));
2548f4056b92SMickaël Salaün 
2549f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2550f4056b92SMickaël Salaün 
2551f4056b92SMickaël Salaün 	/*
2552f4056b92SMickaël Salaün 	 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock
2553f4056b92SMickaël Salaün 	 * because it doesn't inherit new access rights.
2554f4056b92SMickaël Salaün 	 */
2555f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
2556f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
2557f4056b92SMickaël Salaün 
2558f4056b92SMickaël Salaün 	/*
2559f4056b92SMickaël Salaün 	 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it
2560f4056b92SMickaël Salaün 	 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is
2561f4056b92SMickaël Salaün 	 * already allowed for dir_s1d3.
2562f4056b92SMickaël Salaün 	 */
2563f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3));
2564f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3));
2565f4056b92SMickaël Salaün 
2566f4056b92SMickaël Salaün 	/*
2567f4056b92SMickaël Salaün 	 * However, moving the file1_s1d3 file below dir_s2d3 is allowed
2568f4056b92SMickaël Salaün 	 * because it cannot inherit MAKE_REG right (which is dedicated to
2569f4056b92SMickaël Salaün 	 * directories).
2570f4056b92SMickaël Salaün 	 */
2571f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2572f4056b92SMickaël Salaün 
2573f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce2(_metadata);
2574f4056b92SMickaël Salaün 
2575f4056b92SMickaël Salaün 	/*
2576f4056b92SMickaël Salaün 	 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because
2577f4056b92SMickaël Salaün 	 * MAKE_DIR is not tied to dir_s2d2.
2578f4056b92SMickaël Salaün 	 */
2579f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2));
2580f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2581f4056b92SMickaël Salaün 
2582f4056b92SMickaël Salaün 	/*
2583f4056b92SMickaël Salaün 	 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it
2584f4056b92SMickaël Salaün 	 * would grants MAKE_REG and MAKE_DIR rights to it.
2585f4056b92SMickaël Salaün 	 */
2586f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
2587f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2588f4056b92SMickaël Salaün 
2589f4056b92SMickaël Salaün 	/*
259055e55920SMickaël Salaün 	 * Moving the file2_s1d3 file below dir_s2d3 is denied because the
259155e55920SMickaël Salaün 	 * second layer does not handle REFER, which is always denied by
259255e55920SMickaël Salaün 	 * default.
2593f4056b92SMickaël Salaün 	 */
259455e55920SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3));
259555e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2596f4056b92SMickaël Salaün }
2597f4056b92SMickaël Salaün 
TEST_F_FORK(layout1,reparent_exdev_layers_rename2)2598f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename2)
2599f4056b92SMickaël Salaün {
2600f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2601f4056b92SMickaël Salaün 
2602f4056b92SMickaël Salaün 	/* Checks EACCES predominance over EXDEV. */
2603f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
2604f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2605f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2));
2606f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2607f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
2608f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2609f4056b92SMickaël Salaün 	/* Modify layout! */
2610f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3));
2611f4056b92SMickaël Salaün 
2612f4056b92SMickaël Salaün 	/* Without REFER source. */
2613f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
2614f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2615f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
2616f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2617f4056b92SMickaël Salaün 
2618f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce2(_metadata);
2619f4056b92SMickaël Salaün 
2620f4056b92SMickaël Salaün 	/* Checks EACCES predominance over EXDEV. */
2621f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
2622f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2623f4056b92SMickaël Salaün 	/* Checks with actual file2_s1d2. */
2624f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2));
2625f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2626f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
2627f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
262855e55920SMickaël Salaün 	/*
262955e55920SMickaël Salaün 	 * Modifying the layout is now denied because the second layer does not
263055e55920SMickaël Salaün 	 * handle REFER, which is always denied by default.
263155e55920SMickaël Salaün 	 */
263255e55920SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
263355e55920SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2634f4056b92SMickaël Salaün 
2635f4056b92SMickaël Salaün 	/* Without REFER source, EACCES wins over EXDEV. */
2636f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
2637f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2638f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
2639f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2640f4056b92SMickaël Salaün }
2641f4056b92SMickaël Salaün 
TEST_F_FORK(layout1,reparent_exdev_layers_exchange1)2642f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange1)
2643f4056b92SMickaël Salaün {
2644f4056b92SMickaël Salaün 	const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 =
2645f4056b92SMickaël Salaün 							       file2_s2d3;
2646f4056b92SMickaël Salaün 
2647f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2648f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
2649f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2650f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
2651f4056b92SMickaël Salaün 
2652f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2653f4056b92SMickaël Salaün 
2654f4056b92SMickaël Salaün 	/* Error predominance with file exchange: returns EXDEV and EACCES. */
2655f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
2656f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2657f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2658f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
2659f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2660f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2661f4056b92SMickaël Salaün 
2662f4056b92SMickaël Salaün 	/*
2663f4056b92SMickaël Salaün 	 * Checks with directories which creation could be allowed, but denied
2664f4056b92SMickaël Salaün 	 * because of access rights that would be inherited.
2665f4056b92SMickaël Salaün 	 */
2666f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
2667f4056b92SMickaël Salaün 				dir_file2_s2d3, RENAME_EXCHANGE));
2668f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2669f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
2670f4056b92SMickaël Salaün 				dir_file1_s1d2, RENAME_EXCHANGE));
2671f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2672f4056b92SMickaël Salaün 
2673f4056b92SMickaël Salaün 	/* Checks with same access rights. */
2674f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
2675f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2676f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2677f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2678f4056b92SMickaël Salaün 
2679f4056b92SMickaël Salaün 	/* Checks with different (child-only) access rights. */
2680f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
2681f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2682f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
2683f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2684f4056b92SMickaël Salaün 
2685f4056b92SMickaël Salaün 	/*
2686f4056b92SMickaël Salaün 	 * Checks that exchange between file and directory are consistent.
2687f4056b92SMickaël Salaün 	 *
2688f4056b92SMickaël Salaün 	 * Moving a file (file1_s2d2) to a directory which only grants more
2689f4056b92SMickaël Salaün 	 * directory-related access rights is allowed, and at the same time
2690f4056b92SMickaël Salaün 	 * moving a directory (dir_file2_s2d3) to another directory which
2691f4056b92SMickaël Salaün 	 * grants less access rights is allowed too.
2692f4056b92SMickaël Salaün 	 *
2693f4056b92SMickaël Salaün 	 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments.
2694f4056b92SMickaël Salaün 	 */
2695f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2696f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2697f4056b92SMickaël Salaün 	/*
2698f4056b92SMickaël Salaün 	 * However, moving back the directory is denied because it would get
2699f4056b92SMickaël Salaün 	 * more access rights than the current state and because file creation
2700f4056b92SMickaël Salaün 	 * is forbidden (in dir_s2d2).
2701f4056b92SMickaël Salaün 	 */
2702f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2703f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2704f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2705f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2706f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2707f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2708f4056b92SMickaël Salaün 
2709f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce2(_metadata);
2710f4056b92SMickaël Salaün 
2711f4056b92SMickaël Salaün 	/* Error predominance with file exchange: returns EXDEV and EACCES. */
2712f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
2713f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2714f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2715f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
2716f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2717f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2718f4056b92SMickaël Salaün 
2719f4056b92SMickaël Salaün 	/* Checks with directories which creation is now denied. */
2720f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
2721f4056b92SMickaël Salaün 				dir_file2_s2d3, RENAME_EXCHANGE));
2722f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2723f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
2724f4056b92SMickaël Salaün 				dir_file1_s1d2, RENAME_EXCHANGE));
2725f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2726f4056b92SMickaël Salaün 
2727f4056b92SMickaël Salaün 	/* Checks with different (child-only) access rights. */
2728f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
2729f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2730f4056b92SMickaël Salaün 	/* Denied because of MAKE_DIR. */
2731f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2732f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2733f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2734f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2735f4056b92SMickaël Salaün 
2736f4056b92SMickaël Salaün 	/* Checks with different (child-only) access rights. */
2737f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
2738f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2739f4056b92SMickaël Salaün 	/* Denied because of MAKE_DIR. */
2740f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2741f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
2742f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2743f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2744f4056b92SMickaël Salaün 
2745f4056b92SMickaël Salaün 	/* See layout1.reparent_exdev_layers_exchange2 for complement. */
2746f4056b92SMickaël Salaün }
2747f4056b92SMickaël Salaün 
TEST_F_FORK(layout1,reparent_exdev_layers_exchange2)2748f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange2)
2749f4056b92SMickaël Salaün {
2750f4056b92SMickaël Salaün 	const char *const dir_file2_s2d3 = file2_s2d3;
2751f4056b92SMickaël Salaün 
2752f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2753f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
2754f4056b92SMickaël Salaün 
2755f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2756f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce2(_metadata);
2757f4056b92SMickaël Salaün 
2758f4056b92SMickaël Salaün 	/* Checks that exchange between file and directory are consistent. */
2759f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2760f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2761f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2762f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2763f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2764f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2765f4056b92SMickaël Salaün }
2766f4056b92SMickaël Salaün 
TEST_F_FORK(layout1,reparent_exdev_layers_exchange3)2767f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange3)
2768f4056b92SMickaël Salaün {
2769f4056b92SMickaël Salaün 	const char *const dir_file2_s2d3 = file2_s2d3;
2770f4056b92SMickaël Salaün 
2771f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2772f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
2773f4056b92SMickaël Salaün 
2774f4056b92SMickaël Salaün 	reparent_exdev_layers_enforce1(_metadata);
2775f4056b92SMickaël Salaün 
2776f4056b92SMickaël Salaün 	/*
2777f4056b92SMickaël Salaün 	 * Checks that exchange between file and directory are consistent,
2778f4056b92SMickaël Salaün 	 * including with inverted arguments (see
2779f4056b92SMickaël Salaün 	 * layout1.reparent_exdev_layers_exchange1).
2780f4056b92SMickaël Salaün 	 */
2781f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2782f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2783f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2784f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2785f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2786f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2787f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2788f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2789f4056b92SMickaël Salaün }
2790f4056b92SMickaël Salaün 
TEST_F_FORK(layout1,reparent_remove)2791f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_remove)
2792f4056b92SMickaël Salaün {
2793f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2794f4056b92SMickaël Salaün 		{
2795f4056b92SMickaël Salaün 			.path = dir_s1d1,
2796f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER |
2797f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_REMOVE_DIR,
2798f4056b92SMickaël Salaün 		},
2799f4056b92SMickaël Salaün 		{
2800f4056b92SMickaël Salaün 			.path = dir_s1d2,
2801f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2802f4056b92SMickaël Salaün 		},
2803f4056b92SMickaël Salaün 		{
2804f4056b92SMickaël Salaün 			.path = dir_s2d1,
2805f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER |
2806f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_REMOVE_FILE,
2807f4056b92SMickaël Salaün 		},
2808f4056b92SMickaël Salaün 		{},
2809f4056b92SMickaël Salaün 	};
2810f4056b92SMickaël Salaün 	const int ruleset_fd = create_ruleset(
2811f4056b92SMickaël Salaün 		_metadata,
2812f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR |
2813f4056b92SMickaël Salaün 			LANDLOCK_ACCESS_FS_REMOVE_FILE,
2814f4056b92SMickaël Salaün 		layer1);
2815f4056b92SMickaël Salaün 
2816f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2817f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2818f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2819f4056b92SMickaël Salaün 
2820f4056b92SMickaël Salaün 	/* Access denied because of wrong/swapped remove file/dir. */
2821f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2));
2822f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2823f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1));
2824f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2825f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2,
2826f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2827f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2828f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3,
2829f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2830f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2831f4056b92SMickaël Salaün 
2832f4056b92SMickaël Salaün 	/* Access allowed thanks to the matching rights. */
2833f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2));
2834f4056b92SMickaël Salaün 	ASSERT_EQ(EISDIR, errno);
2835f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1));
2836f4056b92SMickaël Salaün 	ASSERT_EQ(ENOTDIR, errno);
2837f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
2838f4056b92SMickaël Salaün 	ASSERT_EQ(ENOTDIR, errno);
2839f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s2d1));
2840f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2841f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
2842f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1));
2843f4056b92SMickaël Salaün 
2844f4056b92SMickaël Salaün 	/* Effectively removes a file and a directory by exchanging them. */
2845f4056b92SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
2846f4056b92SMickaël Salaün 	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
2847f4056b92SMickaël Salaün 			       RENAME_EXCHANGE));
2848f4056b92SMickaël Salaün 	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
2849f4056b92SMickaël Salaün 				RENAME_EXCHANGE));
2850f4056b92SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2851f4056b92SMickaël Salaün }
2852f4056b92SMickaël Salaün 
TEST_F_FORK(layout1,reparent_dom_superset)2853f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_dom_superset)
2854f4056b92SMickaël Salaün {
2855f4056b92SMickaël Salaün 	const struct rule layer1[] = {
2856f4056b92SMickaël Salaün 		{
2857f4056b92SMickaël Salaün 			.path = dir_s1d2,
2858f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
2859f4056b92SMickaël Salaün 		},
2860f4056b92SMickaël Salaün 		{
2861f4056b92SMickaël Salaün 			.path = file1_s1d2,
2862f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
2863f4056b92SMickaël Salaün 		},
2864f4056b92SMickaël Salaün 		{
2865f4056b92SMickaël Salaün 			.path = dir_s1d3,
2866f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_SOCK |
2867f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_EXECUTE,
2868f4056b92SMickaël Salaün 		},
2869f4056b92SMickaël Salaün 		{
2870f4056b92SMickaël Salaün 			.path = dir_s2d2,
2871f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER |
2872f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_EXECUTE |
2873f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_MAKE_SOCK,
2874f4056b92SMickaël Salaün 		},
2875f4056b92SMickaël Salaün 		{
2876f4056b92SMickaël Salaün 			.path = dir_s2d3,
2877f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
2878f4056b92SMickaël Salaün 				  LANDLOCK_ACCESS_FS_MAKE_FIFO,
2879f4056b92SMickaël Salaün 		},
2880f4056b92SMickaël Salaün 		{},
2881f4056b92SMickaël Salaün 	};
2882f4056b92SMickaël Salaün 	int ruleset_fd = create_ruleset(_metadata,
2883f4056b92SMickaël Salaün 					LANDLOCK_ACCESS_FS_REFER |
2884f4056b92SMickaël Salaün 						LANDLOCK_ACCESS_FS_EXECUTE |
2885f4056b92SMickaël Salaün 						LANDLOCK_ACCESS_FS_MAKE_SOCK |
2886f4056b92SMickaël Salaün 						LANDLOCK_ACCESS_FS_READ_FILE |
2887f4056b92SMickaël Salaün 						LANDLOCK_ACCESS_FS_MAKE_FIFO,
2888f4056b92SMickaël Salaün 					layer1);
2889f4056b92SMickaël Salaün 
2890f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2891f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2892f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2893f4056b92SMickaël Salaün 
2894f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1));
2895f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2896f4056b92SMickaël Salaün 	/*
2897f4056b92SMickaël Salaün 	 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE
2898f4056b92SMickaël Salaün 	 * access right.
2899f4056b92SMickaël Salaün 	 */
2900f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3));
2901f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2902f4056b92SMickaël Salaün 	/*
2903f4056b92SMickaël Salaün 	 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a
2904f4056b92SMickaël Salaün 	 * superset of access rights compared to dir_s1d2, because file1_s1d2
2905f4056b92SMickaël Salaün 	 * already has these access rights anyway.
2906f4056b92SMickaël Salaün 	 */
2907f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2));
2908f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2));
2909f4056b92SMickaël Salaün 
2910f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
2911f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2912f4056b92SMickaël Salaün 	/*
2913f4056b92SMickaël Salaün 	 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access
2914f4056b92SMickaël Salaün 	 * right.
2915f4056b92SMickaël Salaün 	 */
2916f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
2917f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2918f4056b92SMickaël Salaün 	/*
2919f4056b92SMickaël Salaün 	 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset
2920f4056b92SMickaël Salaün 	 * of access rights compared to dir_s1d2, because dir_s1d3 already has
2921f4056b92SMickaël Salaün 	 * these access rights anyway.
2922f4056b92SMickaël Salaün 	 */
2923f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
2924f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
2925f4056b92SMickaël Salaün 
2926f4056b92SMickaël Salaün 	/*
2927f4056b92SMickaël Salaün 	 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back
2928f4056b92SMickaël Salaün 	 * will be denied because the new inherited access rights from dir_s1d2
2929f4056b92SMickaël Salaün 	 * will be less than the destination (original) dir_s2d3.  This is a
2930f4056b92SMickaël Salaün 	 * sinkhole scenario where we cannot move back files or directories.
2931f4056b92SMickaël Salaün 	 */
2932f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2));
2933f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
2934f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2935f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
2936f4056b92SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s2d3));
2937f4056b92SMickaël Salaün 	/*
2938f4056b92SMickaël Salaün 	 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and
2939f4056b92SMickaël Salaün 	 * MAKE_SOCK which were inherited from dir_s1d3.
2940f4056b92SMickaël Salaün 	 */
2941f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2));
2942f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3));
2943f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
2944f4056b92SMickaël Salaün }
2945f4056b92SMickaël Salaün 
TEST_F_FORK(layout1,remove_dir)2946e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_dir)
2947e1199815SMickaël Salaün {
2948e1199815SMickaël Salaün 	const struct rule rules[] = {
2949e1199815SMickaël Salaün 		{
2950e1199815SMickaël Salaün 			.path = dir_s1d2,
2951e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2952e1199815SMickaël Salaün 		},
2953135464f9SMickaël Salaün 		{},
2954e1199815SMickaël Salaün 	};
2955371183faSMickaël Salaün 	const int ruleset_fd =
2956371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
2957e1199815SMickaël Salaün 
2958e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2959e1199815SMickaël Salaün 
2960e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
2961e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
2962e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
2963e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
2964e1199815SMickaël Salaün 
2965e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2966e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2967e1199815SMickaël Salaün 
2968e1199815SMickaël Salaün 	ASSERT_EQ(0, rmdir(dir_s1d3));
2969e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
2970e1199815SMickaël Salaün 	ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));
2971e1199815SMickaël Salaün 
2972e1199815SMickaël Salaün 	/* dir_s1d2 itself cannot be removed. */
2973e1199815SMickaël Salaün 	ASSERT_EQ(-1, rmdir(dir_s1d2));
2974e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2975e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR));
2976e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2977e1199815SMickaël Salaün 	ASSERT_EQ(-1, rmdir(dir_s1d1));
2978e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2979e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR));
2980e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
2981e1199815SMickaël Salaün }
2982e1199815SMickaël Salaün 
TEST_F_FORK(layout1,remove_file)2983e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_file)
2984e1199815SMickaël Salaün {
2985e1199815SMickaël Salaün 	const struct rule rules[] = {
2986e1199815SMickaël Salaün 		{
2987e1199815SMickaël Salaün 			.path = dir_s1d2,
2988e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2989e1199815SMickaël Salaün 		},
2990135464f9SMickaël Salaün 		{},
2991e1199815SMickaël Salaün 	};
2992371183faSMickaël Salaün 	const int ruleset_fd =
2993371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
2994e1199815SMickaël Salaün 
2995e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
2996e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
2997e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
2998e1199815SMickaël Salaün 
2999e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlink(file1_s1d1));
3000e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3001e1199815SMickaël Salaün 	ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0));
3002e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3003e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
3004e1199815SMickaël Salaün 	ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0));
3005e1199815SMickaël Salaün }
3006e1199815SMickaël Salaün 
test_make_file(struct __test_metadata * const _metadata,const __u64 access,const mode_t mode,const dev_t dev)3007e1199815SMickaël Salaün static void test_make_file(struct __test_metadata *const _metadata,
3008371183faSMickaël Salaün 			   const __u64 access, const mode_t mode,
3009371183faSMickaël Salaün 			   const dev_t dev)
3010e1199815SMickaël Salaün {
3011e1199815SMickaël Salaün 	const struct rule rules[] = {
3012e1199815SMickaël Salaün 		{
3013e1199815SMickaël Salaün 			.path = dir_s1d2,
3014e1199815SMickaël Salaün 			.access = access,
3015e1199815SMickaël Salaün 		},
3016135464f9SMickaël Salaün 		{},
3017e1199815SMickaël Salaün 	};
3018e1199815SMickaël Salaün 	const int ruleset_fd = create_ruleset(_metadata, access, rules);
3019e1199815SMickaël Salaün 
3020e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3021e1199815SMickaël Salaün 
3022e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
3023e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d1));
3024371183faSMickaël Salaün 	ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev))
3025371183faSMickaël Salaün 	{
3026371183faSMickaël Salaün 		TH_LOG("Failed to make file \"%s\": %s", file2_s1d1,
3027371183faSMickaël Salaün 		       strerror(errno));
3028e1199815SMickaël Salaün 	};
3029e1199815SMickaël Salaün 
3030e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
3031e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
3032e1199815SMickaël Salaün 
3033e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
3034e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
3035e1199815SMickaël Salaün 
3036e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3037e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3038e1199815SMickaël Salaün 
3039e1199815SMickaël Salaün 	ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev));
3040e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3041e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
3042e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3043e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
3044e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3045e1199815SMickaël Salaün 
3046371183faSMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev))
3047371183faSMickaël Salaün 	{
3048371183faSMickaël Salaün 		TH_LOG("Failed to make file \"%s\": %s", file1_s1d2,
3049371183faSMickaël Salaün 		       strerror(errno));
3050e1199815SMickaël Salaün 	};
3051e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
3052e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
3053e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
3054e1199815SMickaël Salaün 
3055e1199815SMickaël Salaün 	ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev));
3056e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
3057e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
3058e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
3059e1199815SMickaël Salaün }
3060e1199815SMickaël Salaün 
TEST_F_FORK(layout1,make_char)3061e1199815SMickaël Salaün TEST_F_FORK(layout1, make_char)
3062e1199815SMickaël Salaün {
3063e1199815SMickaël Salaün 	/* Creates a /dev/null device. */
3064e1199815SMickaël Salaün 	set_cap(_metadata, CAP_MKNOD);
3065e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR,
3066e1199815SMickaël Salaün 		       makedev(1, 3));
3067e1199815SMickaël Salaün }
3068e1199815SMickaël Salaün 
TEST_F_FORK(layout1,make_block)3069e1199815SMickaël Salaün TEST_F_FORK(layout1, make_block)
3070e1199815SMickaël Salaün {
3071e1199815SMickaël Salaün 	/* Creates a /dev/loop0 device. */
3072e1199815SMickaël Salaün 	set_cap(_metadata, CAP_MKNOD);
3073e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK,
3074e1199815SMickaël Salaün 		       makedev(7, 0));
3075e1199815SMickaël Salaün }
3076e1199815SMickaël Salaün 
TEST_F_FORK(layout1,make_reg_1)3077e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_1)
3078e1199815SMickaël Salaün {
3079e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);
3080e1199815SMickaël Salaün }
3081e1199815SMickaël Salaün 
TEST_F_FORK(layout1,make_reg_2)3082e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_2)
3083e1199815SMickaël Salaün {
3084e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);
3085e1199815SMickaël Salaün }
3086e1199815SMickaël Salaün 
TEST_F_FORK(layout1,make_sock)3087e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sock)
3088e1199815SMickaël Salaün {
3089e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);
3090e1199815SMickaël Salaün }
3091e1199815SMickaël Salaün 
TEST_F_FORK(layout1,make_fifo)3092e1199815SMickaël Salaün TEST_F_FORK(layout1, make_fifo)
3093e1199815SMickaël Salaün {
3094e1199815SMickaël Salaün 	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);
3095e1199815SMickaël Salaün }
3096e1199815SMickaël Salaün 
TEST_F_FORK(layout1,make_sym)3097e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sym)
3098e1199815SMickaël Salaün {
3099e1199815SMickaël Salaün 	const struct rule rules[] = {
3100e1199815SMickaël Salaün 		{
3101e1199815SMickaël Salaün 			.path = dir_s1d2,
3102e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_SYM,
3103e1199815SMickaël Salaün 		},
3104135464f9SMickaël Salaün 		{},
3105e1199815SMickaël Salaün 	};
3106371183faSMickaël Salaün 	const int ruleset_fd =
3107371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
3108e1199815SMickaël Salaün 
3109e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3110e1199815SMickaël Salaün 
3111e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
3112e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d1));
3113e1199815SMickaël Salaün 	ASSERT_EQ(0, symlink("none", file2_s1d1));
3114e1199815SMickaël Salaün 
3115e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
3116e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
3117e1199815SMickaël Salaün 
3118e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
3119e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
3120e1199815SMickaël Salaün 
3121e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3122e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3123e1199815SMickaël Salaün 
3124e1199815SMickaël Salaün 	ASSERT_EQ(-1, symlink("none", file1_s1d1));
3125e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3126e1199815SMickaël Salaün 	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
3127e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3128e1199815SMickaël Salaün 	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
3129e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3130e1199815SMickaël Salaün 
3131e1199815SMickaël Salaün 	ASSERT_EQ(0, symlink("none", file1_s1d2));
3132e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
3133e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d2));
3134e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
3135e1199815SMickaël Salaün 
3136e1199815SMickaël Salaün 	ASSERT_EQ(0, symlink("none", file1_s1d3));
3137e1199815SMickaël Salaün 	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
3138e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file2_s1d3));
3139e1199815SMickaël Salaün 	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
3140e1199815SMickaël Salaün }
3141e1199815SMickaël Salaün 
TEST_F_FORK(layout1,make_dir)3142e1199815SMickaël Salaün TEST_F_FORK(layout1, make_dir)
3143e1199815SMickaël Salaün {
3144e1199815SMickaël Salaün 	const struct rule rules[] = {
3145e1199815SMickaël Salaün 		{
3146e1199815SMickaël Salaün 			.path = dir_s1d2,
3147e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_MAKE_DIR,
3148e1199815SMickaël Salaün 		},
3149135464f9SMickaël Salaün 		{},
3150e1199815SMickaël Salaün 	};
3151371183faSMickaël Salaün 	const int ruleset_fd =
3152371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
3153e1199815SMickaël Salaün 
3154e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3155e1199815SMickaël Salaün 
3156e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d1));
3157e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
3158e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d3));
3159e1199815SMickaël Salaün 
3160e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3161e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3162e1199815SMickaël Salaün 
3163e1199815SMickaël Salaün 	/* Uses file_* as directory names. */
3164e1199815SMickaël Salaün 	ASSERT_EQ(-1, mkdir(file1_s1d1, 0700));
3165e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3166e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
3167e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(file1_s1d3, 0700));
3168e1199815SMickaël Salaün }
3169e1199815SMickaël Salaün 
open_proc_fd(struct __test_metadata * const _metadata,const int fd,const int open_flags)3170e1199815SMickaël Salaün static int open_proc_fd(struct __test_metadata *const _metadata, const int fd,
3171e1199815SMickaël Salaün 			const int open_flags)
3172e1199815SMickaël Salaün {
3173e1199815SMickaël Salaün 	static const char path_template[] = "/proc/self/fd/%d";
3174e1199815SMickaël Salaün 	char procfd_path[sizeof(path_template) + 10];
3175371183faSMickaël Salaün 	const int procfd_path_size =
3176371183faSMickaël Salaün 		snprintf(procfd_path, sizeof(procfd_path), path_template, fd);
3177e1199815SMickaël Salaün 
3178e1199815SMickaël Salaün 	ASSERT_LT(procfd_path_size, sizeof(procfd_path));
3179e1199815SMickaël Salaün 	return open(procfd_path, open_flags);
3180e1199815SMickaël Salaün }
3181e1199815SMickaël Salaün 
TEST_F_FORK(layout1,proc_unlinked_file)3182e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_unlinked_file)
3183e1199815SMickaël Salaün {
3184e1199815SMickaël Salaün 	const struct rule rules[] = {
3185e1199815SMickaël Salaün 		{
3186e1199815SMickaël Salaün 			.path = file1_s1d2,
3187e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
3188e1199815SMickaël Salaün 		},
3189135464f9SMickaël Salaün 		{},
3190e1199815SMickaël Salaün 	};
3191e1199815SMickaël Salaün 	int reg_fd, proc_fd;
3192371183faSMickaël Salaün 	const int ruleset_fd = create_ruleset(
3193371183faSMickaël Salaün 		_metadata,
3194371183faSMickaël Salaün 		LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
3195371183faSMickaël Salaün 		rules);
3196e1199815SMickaël Salaün 
3197e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3198e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3199e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3200e1199815SMickaël Salaün 
3201e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
3202e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3203e1199815SMickaël Salaün 	reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC);
3204e1199815SMickaël Salaün 	ASSERT_LE(0, reg_fd);
3205e1199815SMickaël Salaün 	ASSERT_EQ(0, unlink(file1_s1d2));
3206e1199815SMickaël Salaün 
3207e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC);
3208e1199815SMickaël Salaün 	ASSERT_LE(0, proc_fd);
3209e1199815SMickaël Salaün 	ASSERT_EQ(0, close(proc_fd));
3210e1199815SMickaël Salaün 
3211e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC);
3212371183faSMickaël Salaün 	ASSERT_EQ(-1, proc_fd)
3213371183faSMickaël Salaün 	{
3214371183faSMickaël Salaün 		TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd,
3215371183faSMickaël Salaün 		       strerror(errno));
3216e1199815SMickaël Salaün 	}
3217e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, errno);
3218e1199815SMickaël Salaün 
3219e1199815SMickaël Salaün 	ASSERT_EQ(0, close(reg_fd));
3220e1199815SMickaël Salaün }
3221e1199815SMickaël Salaün 
TEST_F_FORK(layout1,proc_pipe)3222e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_pipe)
3223e1199815SMickaël Salaün {
3224e1199815SMickaël Salaün 	int proc_fd;
3225e1199815SMickaël Salaün 	int pipe_fds[2];
3226e1199815SMickaël Salaün 	char buf = '\0';
3227e1199815SMickaël Salaün 	const struct rule rules[] = {
3228e1199815SMickaël Salaün 		{
3229e1199815SMickaël Salaün 			.path = dir_s1d2,
3230e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
3231e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
3232e1199815SMickaël Salaün 		},
3233135464f9SMickaël Salaün 		{},
3234e1199815SMickaël Salaün 	};
3235e1199815SMickaël Salaün 	/* Limits read and write access to files tied to the filesystem. */
3236371183faSMickaël Salaün 	const int ruleset_fd =
3237371183faSMickaël Salaün 		create_ruleset(_metadata, rules[0].access, rules);
3238e1199815SMickaël Salaün 
3239e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3240e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3241e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3242e1199815SMickaël Salaün 
3243e1199815SMickaël Salaün 	/* Checks enforcement for normal files. */
3244e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
3245e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
3246e1199815SMickaël Salaün 
3247e1199815SMickaël Salaün 	/* Checks access to pipes through FD. */
3248e1199815SMickaël Salaün 	ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC));
3249371183faSMickaël Salaün 	ASSERT_EQ(1, write(pipe_fds[1], ".", 1))
3250371183faSMickaël Salaün 	{
3251e1199815SMickaël Salaün 		TH_LOG("Failed to write in pipe: %s", strerror(errno));
3252e1199815SMickaël Salaün 	}
3253e1199815SMickaël Salaün 	ASSERT_EQ(1, read(pipe_fds[0], &buf, 1));
3254e1199815SMickaël Salaün 	ASSERT_EQ('.', buf);
3255e1199815SMickaël Salaün 
3256e1199815SMickaël Salaün 	/* Checks write access to pipe through /proc/self/fd . */
3257e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC);
3258e1199815SMickaël Salaün 	ASSERT_LE(0, proc_fd);
3259371183faSMickaël Salaün 	ASSERT_EQ(1, write(proc_fd, ".", 1))
3260371183faSMickaël Salaün 	{
3261e1199815SMickaël Salaün 		TH_LOG("Failed to write through /proc/self/fd/%d: %s",
3262e1199815SMickaël Salaün 		       pipe_fds[1], strerror(errno));
3263e1199815SMickaël Salaün 	}
3264e1199815SMickaël Salaün 	ASSERT_EQ(0, close(proc_fd));
3265e1199815SMickaël Salaün 
3266e1199815SMickaël Salaün 	/* Checks read access to pipe through /proc/self/fd . */
3267e1199815SMickaël Salaün 	proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC);
3268e1199815SMickaël Salaün 	ASSERT_LE(0, proc_fd);
3269e1199815SMickaël Salaün 	buf = '\0';
3270371183faSMickaël Salaün 	ASSERT_EQ(1, read(proc_fd, &buf, 1))
3271371183faSMickaël Salaün 	{
3272e1199815SMickaël Salaün 		TH_LOG("Failed to read through /proc/self/fd/%d: %s",
3273e1199815SMickaël Salaün 		       pipe_fds[1], strerror(errno));
3274e1199815SMickaël Salaün 	}
3275e1199815SMickaël Salaün 	ASSERT_EQ(0, close(proc_fd));
3276e1199815SMickaël Salaün 
3277e1199815SMickaël Salaün 	ASSERT_EQ(0, close(pipe_fds[0]));
3278e1199815SMickaël Salaün 	ASSERT_EQ(0, close(pipe_fds[1]));
3279e1199815SMickaël Salaün }
3280e1199815SMickaël Salaün 
3281225351abSGünther Noack /* Invokes truncate(2) and returns its errno or 0. */
test_truncate(const char * const path)3282225351abSGünther Noack static int test_truncate(const char *const path)
3283225351abSGünther Noack {
3284225351abSGünther Noack 	if (truncate(path, 10) < 0)
3285225351abSGünther Noack 		return errno;
3286225351abSGünther Noack 	return 0;
3287225351abSGünther Noack }
3288225351abSGünther Noack 
3289225351abSGünther Noack /*
3290225351abSGünther Noack  * Invokes creat(2) and returns its errno or 0.
3291225351abSGünther Noack  * Closes the opened file descriptor on success.
3292225351abSGünther Noack  */
test_creat(const char * const path)3293225351abSGünther Noack static int test_creat(const char *const path)
3294225351abSGünther Noack {
3295225351abSGünther Noack 	int fd = creat(path, 0600);
3296225351abSGünther Noack 
3297225351abSGünther Noack 	if (fd < 0)
3298225351abSGünther Noack 		return errno;
3299225351abSGünther Noack 
3300225351abSGünther Noack 	/*
3301225351abSGünther Noack 	 * Mixing error codes from close(2) and creat(2) should not lead to any
3302225351abSGünther Noack 	 * (access type) confusion for this test.
3303225351abSGünther Noack 	 */
3304225351abSGünther Noack 	if (close(fd) < 0)
3305225351abSGünther Noack 		return errno;
3306225351abSGünther Noack 	return 0;
3307225351abSGünther Noack }
3308225351abSGünther Noack 
3309225351abSGünther Noack /*
3310225351abSGünther Noack  * Exercises file truncation when it's not restricted,
3311225351abSGünther Noack  * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed.
3312225351abSGünther Noack  */
TEST_F_FORK(layout1,truncate_unhandled)3313225351abSGünther Noack TEST_F_FORK(layout1, truncate_unhandled)
3314225351abSGünther Noack {
3315225351abSGünther Noack 	const char *const file_r = file1_s1d1;
3316225351abSGünther Noack 	const char *const file_w = file2_s1d1;
3317225351abSGünther Noack 	const char *const file_none = file1_s1d2;
3318225351abSGünther Noack 	const struct rule rules[] = {
3319225351abSGünther Noack 		{
3320225351abSGünther Noack 			.path = file_r,
3321225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
3322225351abSGünther Noack 		},
3323225351abSGünther Noack 		{
3324225351abSGünther Noack 			.path = file_w,
3325225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3326225351abSGünther Noack 		},
3327225351abSGünther Noack 		/* Implicitly: No rights for file_none. */
3328225351abSGünther Noack 		{},
3329225351abSGünther Noack 	};
3330225351abSGünther Noack 
3331225351abSGünther Noack 	const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
3332225351abSGünther Noack 			      LANDLOCK_ACCESS_FS_WRITE_FILE;
3333225351abSGünther Noack 	int ruleset_fd;
3334225351abSGünther Noack 
3335225351abSGünther Noack 	/* Enable Landlock. */
3336225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled, rules);
3337225351abSGünther Noack 
3338225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3339225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3340225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3341225351abSGünther Noack 
3342225351abSGünther Noack 	/*
3343225351abSGünther Noack 	 * Checks read right: truncate and open with O_TRUNC work, unless the
3344225351abSGünther Noack 	 * file is attempted to be opened for writing.
3345225351abSGünther Noack 	 */
3346225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_r));
3347225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC));
3348225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC));
3349225351abSGünther Noack 	EXPECT_EQ(EACCES, test_creat(file_r));
3350225351abSGünther Noack 
3351225351abSGünther Noack 	/*
3352225351abSGünther Noack 	 * Checks write right: truncate and open with O_TRUNC work, unless the
3353225351abSGünther Noack 	 * file is attempted to be opened for reading.
3354225351abSGünther Noack 	 */
3355225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_w));
3356225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC));
3357225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC));
3358225351abSGünther Noack 	EXPECT_EQ(0, test_creat(file_w));
3359225351abSGünther Noack 
3360225351abSGünther Noack 	/*
3361225351abSGünther Noack 	 * Checks "no rights" case: truncate works but all open attempts fail,
3362225351abSGünther Noack 	 * including creat.
3363225351abSGünther Noack 	 */
3364225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_none));
3365225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3366225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3367225351abSGünther Noack 	EXPECT_EQ(EACCES, test_creat(file_none));
3368225351abSGünther Noack }
3369225351abSGünther Noack 
TEST_F_FORK(layout1,truncate)3370225351abSGünther Noack TEST_F_FORK(layout1, truncate)
3371225351abSGünther Noack {
3372225351abSGünther Noack 	const char *const file_rwt = file1_s1d1;
3373225351abSGünther Noack 	const char *const file_rw = file2_s1d1;
3374225351abSGünther Noack 	const char *const file_rt = file1_s1d2;
3375225351abSGünther Noack 	const char *const file_t = file2_s1d2;
3376225351abSGünther Noack 	const char *const file_none = file1_s1d3;
3377225351abSGünther Noack 	const char *const dir_t = dir_s2d1;
3378225351abSGünther Noack 	const char *const file_in_dir_t = file1_s2d1;
3379225351abSGünther Noack 	const char *const dir_w = dir_s3d1;
3380225351abSGünther Noack 	const char *const file_in_dir_w = file1_s3d1;
3381225351abSGünther Noack 	const struct rule rules[] = {
3382225351abSGünther Noack 		{
3383225351abSGünther Noack 			.path = file_rwt,
3384225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
3385225351abSGünther Noack 				  LANDLOCK_ACCESS_FS_WRITE_FILE |
3386225351abSGünther Noack 				  LANDLOCK_ACCESS_FS_TRUNCATE,
3387225351abSGünther Noack 		},
3388225351abSGünther Noack 		{
3389225351abSGünther Noack 			.path = file_rw,
3390225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
3391225351abSGünther Noack 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
3392225351abSGünther Noack 		},
3393225351abSGünther Noack 		{
3394225351abSGünther Noack 			.path = file_rt,
3395225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
3396225351abSGünther Noack 				  LANDLOCK_ACCESS_FS_TRUNCATE,
3397225351abSGünther Noack 		},
3398225351abSGünther Noack 		{
3399225351abSGünther Noack 			.path = file_t,
3400225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_TRUNCATE,
3401225351abSGünther Noack 		},
3402225351abSGünther Noack 		/* Implicitly: No access rights for file_none. */
3403225351abSGünther Noack 		{
3404225351abSGünther Noack 			.path = dir_t,
3405225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_TRUNCATE,
3406225351abSGünther Noack 		},
3407225351abSGünther Noack 		{
3408225351abSGünther Noack 			.path = dir_w,
3409225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3410225351abSGünther Noack 		},
3411225351abSGünther Noack 		{},
3412225351abSGünther Noack 	};
3413225351abSGünther Noack 	const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
3414225351abSGünther Noack 			      LANDLOCK_ACCESS_FS_WRITE_FILE |
3415225351abSGünther Noack 			      LANDLOCK_ACCESS_FS_TRUNCATE;
3416225351abSGünther Noack 	int ruleset_fd;
3417225351abSGünther Noack 
3418225351abSGünther Noack 	/* Enable Landlock. */
3419225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled, rules);
3420225351abSGünther Noack 
3421225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3422225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3423225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3424225351abSGünther Noack 
3425225351abSGünther Noack 	/* Checks read, write and truncate rights: truncation works. */
3426225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_rwt));
3427225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC));
3428225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC));
3429225351abSGünther Noack 
3430225351abSGünther Noack 	/* Checks read and write rights: no truncate variant works. */
3431225351abSGünther Noack 	EXPECT_EQ(EACCES, test_truncate(file_rw));
3432225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC));
3433225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC));
3434225351abSGünther Noack 
3435225351abSGünther Noack 	/*
3436225351abSGünther Noack 	 * Checks read and truncate rights: truncation works.
3437225351abSGünther Noack 	 *
3438225351abSGünther Noack 	 * Note: Files can get truncated using open() even with O_RDONLY.
3439225351abSGünther Noack 	 */
3440225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_rt));
3441225351abSGünther Noack 	EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC));
3442225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC));
3443225351abSGünther Noack 
3444225351abSGünther Noack 	/* Checks truncate right: truncate works, but can't open file. */
3445225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_t));
3446225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC));
3447225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC));
3448225351abSGünther Noack 
3449225351abSGünther Noack 	/* Checks "no rights" case: No form of truncation works. */
3450225351abSGünther Noack 	EXPECT_EQ(EACCES, test_truncate(file_none));
3451225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3452225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3453225351abSGünther Noack 
3454225351abSGünther Noack 	/*
3455225351abSGünther Noack 	 * Checks truncate right on directory: truncate works on contained
3456225351abSGünther Noack 	 * files.
3457225351abSGünther Noack 	 */
3458225351abSGünther Noack 	EXPECT_EQ(0, test_truncate(file_in_dir_t));
3459225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC));
3460225351abSGünther Noack 	EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC));
3461225351abSGünther Noack 
3462225351abSGünther Noack 	/*
3463225351abSGünther Noack 	 * Checks creat in dir_w: This requires the truncate right when
3464225351abSGünther Noack 	 * overwriting an existing file, but does not require it when the file
3465225351abSGünther Noack 	 * is new.
3466225351abSGünther Noack 	 */
3467225351abSGünther Noack 	EXPECT_EQ(EACCES, test_creat(file_in_dir_w));
3468225351abSGünther Noack 
3469225351abSGünther Noack 	ASSERT_EQ(0, unlink(file_in_dir_w));
3470225351abSGünther Noack 	EXPECT_EQ(0, test_creat(file_in_dir_w));
3471225351abSGünther Noack }
3472225351abSGünther Noack 
3473225351abSGünther Noack /* Invokes ftruncate(2) and returns its errno or 0. */
test_ftruncate(int fd)3474225351abSGünther Noack static int test_ftruncate(int fd)
3475225351abSGünther Noack {
3476225351abSGünther Noack 	if (ftruncate(fd, 10) < 0)
3477225351abSGünther Noack 		return errno;
3478225351abSGünther Noack 	return 0;
3479225351abSGünther Noack }
3480225351abSGünther Noack 
TEST_F_FORK(layout1,ftruncate)3481225351abSGünther Noack TEST_F_FORK(layout1, ftruncate)
3482225351abSGünther Noack {
3483225351abSGünther Noack 	/*
3484225351abSGünther Noack 	 * This test opens a new file descriptor at different stages of
3485225351abSGünther Noack 	 * Landlock restriction:
3486225351abSGünther Noack 	 *
3487225351abSGünther Noack 	 * without restriction:                    ftruncate works
3488225351abSGünther Noack 	 * something else but truncate restricted: ftruncate works
3489225351abSGünther Noack 	 * truncate restricted and permitted:      ftruncate works
3490225351abSGünther Noack 	 * truncate restricted and not permitted:  ftruncate fails
3491225351abSGünther Noack 	 *
3492225351abSGünther Noack 	 * Whether this works or not is expected to depend on the time when the
3493225351abSGünther Noack 	 * FD was opened, not to depend on the time when ftruncate() was
3494225351abSGünther Noack 	 * called.
3495225351abSGünther Noack 	 */
3496225351abSGünther Noack 	const char *const path = file1_s1d1;
3497225351abSGünther Noack 	const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE |
3498225351abSGünther Noack 			       LANDLOCK_ACCESS_FS_WRITE_FILE;
3499225351abSGünther Noack 	const struct rule layer1[] = {
3500225351abSGünther Noack 		{
3501225351abSGünther Noack 			.path = path,
3502225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3503225351abSGünther Noack 		},
3504225351abSGünther Noack 		{},
3505225351abSGünther Noack 	};
3506225351abSGünther Noack 	const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE;
3507225351abSGünther Noack 	const struct rule layer2[] = {
3508225351abSGünther Noack 		{
3509225351abSGünther Noack 			.path = path,
3510225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_TRUNCATE,
3511225351abSGünther Noack 		},
3512225351abSGünther Noack 		{},
3513225351abSGünther Noack 	};
3514225351abSGünther Noack 	const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE |
3515225351abSGünther Noack 			       LANDLOCK_ACCESS_FS_WRITE_FILE;
3516225351abSGünther Noack 	const struct rule layer3[] = {
3517225351abSGünther Noack 		{
3518225351abSGünther Noack 			.path = path,
3519225351abSGünther Noack 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3520225351abSGünther Noack 		},
3521225351abSGünther Noack 		{},
3522225351abSGünther Noack 	};
3523225351abSGünther Noack 	int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd;
3524225351abSGünther Noack 
3525225351abSGünther Noack 	fd_layer0 = open(path, O_WRONLY);
3526225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer0));
3527225351abSGünther Noack 
3528225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled1, layer1);
3529225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3530225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3531225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3532225351abSGünther Noack 
3533225351abSGünther Noack 	fd_layer1 = open(path, O_WRONLY);
3534225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer0));
3535225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer1));
3536225351abSGünther Noack 
3537225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled2, layer2);
3538225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3539225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3540225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3541225351abSGünther Noack 
3542225351abSGünther Noack 	fd_layer2 = open(path, O_WRONLY);
3543225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer0));
3544225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer1));
3545225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer2));
3546225351abSGünther Noack 
3547225351abSGünther Noack 	ruleset_fd = create_ruleset(_metadata, handled3, layer3);
3548225351abSGünther Noack 	ASSERT_LE(0, ruleset_fd);
3549225351abSGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
3550225351abSGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
3551225351abSGünther Noack 
3552225351abSGünther Noack 	fd_layer3 = open(path, O_WRONLY);
3553225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer0));
3554225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer1));
3555225351abSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd_layer2));
3556225351abSGünther Noack 	EXPECT_EQ(EACCES, test_ftruncate(fd_layer3));
3557225351abSGünther Noack 
3558225351abSGünther Noack 	ASSERT_EQ(0, close(fd_layer0));
3559225351abSGünther Noack 	ASSERT_EQ(0, close(fd_layer1));
3560225351abSGünther Noack 	ASSERT_EQ(0, close(fd_layer2));
3561225351abSGünther Noack 	ASSERT_EQ(0, close(fd_layer3));
3562225351abSGünther Noack }
3563225351abSGünther Noack 
35644598d9abSMickaël Salaün /* clang-format off */
FIXTURE(ftruncate)356541729af2SGünther Noack FIXTURE(ftruncate) {};
356641729af2SGünther Noack /* clang-format on */
356741729af2SGünther Noack 
FIXTURE_SETUP(ftruncate)356841729af2SGünther Noack FIXTURE_SETUP(ftruncate)
356941729af2SGünther Noack {
357041729af2SGünther Noack 	prepare_layout(_metadata);
357141729af2SGünther Noack 	create_file(_metadata, file1_s1d1);
357241729af2SGünther Noack }
357341729af2SGünther Noack 
FIXTURE_TEARDOWN(ftruncate)357441729af2SGünther Noack FIXTURE_TEARDOWN(ftruncate)
357541729af2SGünther Noack {
357641729af2SGünther Noack 	EXPECT_EQ(0, remove_path(file1_s1d1));
357741729af2SGünther Noack 	cleanup_layout(_metadata);
357841729af2SGünther Noack }
357941729af2SGünther Noack 
FIXTURE_VARIANT(ftruncate)358041729af2SGünther Noack FIXTURE_VARIANT(ftruncate)
358141729af2SGünther Noack {
358241729af2SGünther Noack 	const __u64 handled;
358341729af2SGünther Noack 	const __u64 permitted;
358441729af2SGünther Noack 	const int expected_open_result;
358541729af2SGünther Noack 	const int expected_ftruncate_result;
358641729af2SGünther Noack };
358741729af2SGünther Noack 
358841729af2SGünther Noack /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,w_w)358941729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, w_w) {
359041729af2SGünther Noack 	/* clang-format on */
359141729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE,
359241729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_WRITE_FILE,
359341729af2SGünther Noack 	.expected_open_result = 0,
359441729af2SGünther Noack 	.expected_ftruncate_result = 0,
359541729af2SGünther Noack };
359641729af2SGünther Noack 
359741729af2SGünther Noack /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,t_t)359841729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, t_t) {
359941729af2SGünther Noack 	/* clang-format on */
360041729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_TRUNCATE,
360141729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_TRUNCATE,
360241729af2SGünther Noack 	.expected_open_result = 0,
360341729af2SGünther Noack 	.expected_ftruncate_result = 0,
360441729af2SGünther Noack };
360541729af2SGünther Noack 
360641729af2SGünther Noack /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_w)360741729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_w) {
360841729af2SGünther Noack 	/* clang-format on */
360941729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
361041729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_WRITE_FILE,
361141729af2SGünther Noack 	.expected_open_result = 0,
361241729af2SGünther Noack 	.expected_ftruncate_result = EACCES,
361341729af2SGünther Noack };
361441729af2SGünther Noack 
361541729af2SGünther Noack /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_wt)361641729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_wt) {
361741729af2SGünther Noack 	/* clang-format on */
361841729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
361941729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_WRITE_FILE |
362041729af2SGünther Noack 		     LANDLOCK_ACCESS_FS_TRUNCATE,
362141729af2SGünther Noack 	.expected_open_result = 0,
362241729af2SGünther Noack 	.expected_ftruncate_result = 0,
362341729af2SGünther Noack };
362441729af2SGünther Noack 
362541729af2SGünther Noack /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_t)362641729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_t) {
362741729af2SGünther Noack 	/* clang-format on */
362841729af2SGünther Noack 	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
362941729af2SGünther Noack 	.permitted = LANDLOCK_ACCESS_FS_TRUNCATE,
363041729af2SGünther Noack 	.expected_open_result = EACCES,
363141729af2SGünther Noack };
363241729af2SGünther Noack 
TEST_F_FORK(ftruncate,open_and_ftruncate)363341729af2SGünther Noack TEST_F_FORK(ftruncate, open_and_ftruncate)
363441729af2SGünther Noack {
363541729af2SGünther Noack 	const char *const path = file1_s1d1;
363641729af2SGünther Noack 	const struct rule rules[] = {
363741729af2SGünther Noack 		{
363841729af2SGünther Noack 			.path = path,
363941729af2SGünther Noack 			.access = variant->permitted,
364041729af2SGünther Noack 		},
364141729af2SGünther Noack 		{},
364241729af2SGünther Noack 	};
364341729af2SGünther Noack 	int fd, ruleset_fd;
364441729af2SGünther Noack 
364541729af2SGünther Noack 	/* Enable Landlock. */
364641729af2SGünther Noack 	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
364741729af2SGünther Noack 	ASSERT_LE(0, ruleset_fd);
364841729af2SGünther Noack 	enforce_ruleset(_metadata, ruleset_fd);
364941729af2SGünther Noack 	ASSERT_EQ(0, close(ruleset_fd));
365041729af2SGünther Noack 
365141729af2SGünther Noack 	fd = open(path, O_WRONLY);
365241729af2SGünther Noack 	EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
365341729af2SGünther Noack 	if (fd >= 0) {
365441729af2SGünther Noack 		EXPECT_EQ(variant->expected_ftruncate_result,
365541729af2SGünther Noack 			  test_ftruncate(fd));
365641729af2SGünther Noack 		ASSERT_EQ(0, close(fd));
365741729af2SGünther Noack 	}
365841729af2SGünther Noack }
365941729af2SGünther Noack 
TEST_F_FORK(ftruncate,open_and_ftruncate_in_different_processes)3660a1a202a5SGünther Noack TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)
3661a1a202a5SGünther Noack {
3662a1a202a5SGünther Noack 	int child, fd, status;
3663a1a202a5SGünther Noack 	int socket_fds[2];
3664a1a202a5SGünther Noack 
3665a1a202a5SGünther Noack 	ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
3666a1a202a5SGünther Noack 				socket_fds));
3667a1a202a5SGünther Noack 
3668a1a202a5SGünther Noack 	child = fork();
3669a1a202a5SGünther Noack 	ASSERT_LE(0, child);
3670a1a202a5SGünther Noack 	if (child == 0) {
3671a1a202a5SGünther Noack 		/*
3672a1a202a5SGünther Noack 		 * Enables Landlock in the child process, open a file descriptor
3673a1a202a5SGünther Noack 		 * where truncation is forbidden and send it to the
3674a1a202a5SGünther Noack 		 * non-landlocked parent process.
3675a1a202a5SGünther Noack 		 */
3676a1a202a5SGünther Noack 		const char *const path = file1_s1d1;
3677a1a202a5SGünther Noack 		const struct rule rules[] = {
3678a1a202a5SGünther Noack 			{
3679a1a202a5SGünther Noack 				.path = path,
3680a1a202a5SGünther Noack 				.access = variant->permitted,
3681a1a202a5SGünther Noack 			},
3682a1a202a5SGünther Noack 			{},
3683a1a202a5SGünther Noack 		};
3684a1a202a5SGünther Noack 		int fd, ruleset_fd;
3685a1a202a5SGünther Noack 
3686a1a202a5SGünther Noack 		ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
3687a1a202a5SGünther Noack 		ASSERT_LE(0, ruleset_fd);
3688a1a202a5SGünther Noack 		enforce_ruleset(_metadata, ruleset_fd);
3689a1a202a5SGünther Noack 		ASSERT_EQ(0, close(ruleset_fd));
3690a1a202a5SGünther Noack 
3691a1a202a5SGünther Noack 		fd = open(path, O_WRONLY);
3692a1a202a5SGünther Noack 		ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
3693a1a202a5SGünther Noack 
3694a1a202a5SGünther Noack 		if (fd >= 0) {
3695a1a202a5SGünther Noack 			ASSERT_EQ(0, send_fd(socket_fds[0], fd));
3696a1a202a5SGünther Noack 			ASSERT_EQ(0, close(fd));
3697a1a202a5SGünther Noack 		}
3698a1a202a5SGünther Noack 
3699a1a202a5SGünther Noack 		ASSERT_EQ(0, close(socket_fds[0]));
3700a1a202a5SGünther Noack 
3701a1a202a5SGünther Noack 		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
3702a1a202a5SGünther Noack 		return;
3703a1a202a5SGünther Noack 	}
3704a1a202a5SGünther Noack 
3705a1a202a5SGünther Noack 	if (variant->expected_open_result == 0) {
3706a1a202a5SGünther Noack 		fd = recv_fd(socket_fds[1]);
3707a1a202a5SGünther Noack 		ASSERT_LE(0, fd);
3708a1a202a5SGünther Noack 
3709a1a202a5SGünther Noack 		EXPECT_EQ(variant->expected_ftruncate_result,
3710a1a202a5SGünther Noack 			  test_ftruncate(fd));
3711a1a202a5SGünther Noack 		ASSERT_EQ(0, close(fd));
3712a1a202a5SGünther Noack 	}
3713a1a202a5SGünther Noack 
3714a1a202a5SGünther Noack 	ASSERT_EQ(child, waitpid(child, &status, 0));
3715a1a202a5SGünther Noack 	ASSERT_EQ(1, WIFEXITED(status));
3716a1a202a5SGünther Noack 	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
3717a1a202a5SGünther Noack 
3718a1a202a5SGünther Noack 	ASSERT_EQ(0, close(socket_fds[0]));
3719a1a202a5SGünther Noack 	ASSERT_EQ(0, close(socket_fds[1]));
3720a1a202a5SGünther Noack }
3721a1a202a5SGünther Noack 
TEST(memfd_ftruncate)37220d8c658bSGünther Noack TEST(memfd_ftruncate)
37230d8c658bSGünther Noack {
37240d8c658bSGünther Noack 	int fd;
37250d8c658bSGünther Noack 
37260d8c658bSGünther Noack 	fd = memfd_create("name", MFD_CLOEXEC);
37270d8c658bSGünther Noack 	ASSERT_LE(0, fd);
37280d8c658bSGünther Noack 
37290d8c658bSGünther Noack 	/*
37300d8c658bSGünther Noack 	 * Checks that ftruncate is permitted on file descriptors that are
37310d8c658bSGünther Noack 	 * created in ways other than open(2).
37320d8c658bSGünther Noack 	 */
37330d8c658bSGünther Noack 	EXPECT_EQ(0, test_ftruncate(fd));
37340d8c658bSGünther Noack 
37350d8c658bSGünther Noack 	ASSERT_EQ(0, close(fd));
37360d8c658bSGünther Noack }
37370d8c658bSGünther Noack 
373841729af2SGünther Noack /* clang-format off */
FIXTURE(layout1_bind)37394598d9abSMickaël Salaün FIXTURE(layout1_bind) {};
37404598d9abSMickaël Salaün /* clang-format on */
3741e1199815SMickaël Salaün 
FIXTURE_SETUP(layout1_bind)3742e1199815SMickaël Salaün FIXTURE_SETUP(layout1_bind)
3743e1199815SMickaël Salaün {
3744e1199815SMickaël Salaün 	prepare_layout(_metadata);
3745e1199815SMickaël Salaün 
3746e1199815SMickaël Salaün 	create_layout1(_metadata);
3747e1199815SMickaël Salaün 
3748e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
3749e1199815SMickaël Salaün 	ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
3750e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
3751e1199815SMickaël Salaün }
3752e1199815SMickaël Salaün 
FIXTURE_TEARDOWN(layout1_bind)3753e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1_bind)
3754e1199815SMickaël Salaün {
3755e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
3756e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(dir_s2d2));
3757e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
3758e1199815SMickaël Salaün 
3759e1199815SMickaël Salaün 	remove_layout1(_metadata);
3760e1199815SMickaël Salaün 
3761e1199815SMickaël Salaün 	cleanup_layout(_metadata);
3762e1199815SMickaël Salaün }
3763e1199815SMickaël Salaün 
3764e1199815SMickaël Salaün static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";
3765e1199815SMickaël Salaün static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";
3766e1199815SMickaël Salaün 
3767e1199815SMickaël Salaün /*
3768e1199815SMickaël Salaün  * layout1_bind hierarchy:
3769e1199815SMickaël Salaün  *
3770e1199815SMickaël Salaün  * tmp
3771e1199815SMickaël Salaün  * ├── s1d1
3772e1199815SMickaël Salaün  * │   ├── f1
3773e1199815SMickaël Salaün  * │   ├── f2
3774e1199815SMickaël Salaün  * │   └── s1d2
3775e1199815SMickaël Salaün  * │       ├── f1
3776e1199815SMickaël Salaün  * │       ├── f2
3777e1199815SMickaël Salaün  * │       └── s1d3
3778e1199815SMickaël Salaün  * │           ├── f1
3779e1199815SMickaël Salaün  * │           └── f2
3780e1199815SMickaël Salaün  * ├── s2d1
3781e1199815SMickaël Salaün  * │   ├── f1
3782e1199815SMickaël Salaün  * │   └── s2d2
3783e1199815SMickaël Salaün  * │       ├── f1
3784e1199815SMickaël Salaün  * │       ├── f2
3785e1199815SMickaël Salaün  * │       └── s1d3
3786e1199815SMickaël Salaün  * │           ├── f1
3787e1199815SMickaël Salaün  * │           └── f2
3788e1199815SMickaël Salaün  * └── s3d1
3789e1199815SMickaël Salaün  *     └── s3d2
3790e1199815SMickaël Salaün  *         └── s3d3
3791e1199815SMickaël Salaün  */
3792e1199815SMickaël Salaün 
TEST_F_FORK(layout1_bind,no_restriction)3793e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, no_restriction)
3794e1199815SMickaël Salaün {
3795e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
3796e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
3797e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
3798e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3799e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
3800e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
3801e1199815SMickaël Salaün 
3802e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
3803e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
3804e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
3805e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
3806e1199815SMickaël Salaün 	ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY));
3807e1199815SMickaël Salaün 	ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY));
3808e1199815SMickaël Salaün 
3809e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY));
3810e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
3811e1199815SMickaël Salaün 
3812e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
3813e1199815SMickaël Salaün }
3814e1199815SMickaël Salaün 
TEST_F_FORK(layout1_bind,same_content_same_file)3815e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, same_content_same_file)
3816e1199815SMickaël Salaün {
3817e1199815SMickaël Salaün 	/*
3818e1199815SMickaël Salaün 	 * Sets access right on parent directories of both source and
3819e1199815SMickaël Salaün 	 * destination mount points.
3820e1199815SMickaël Salaün 	 */
3821e1199815SMickaël Salaün 	const struct rule layer1_parent[] = {
3822e1199815SMickaël Salaün 		{
3823e1199815SMickaël Salaün 			.path = dir_s1d1,
3824e1199815SMickaël Salaün 			.access = ACCESS_RO,
3825e1199815SMickaël Salaün 		},
3826e1199815SMickaël Salaün 		{
3827e1199815SMickaël Salaün 			.path = dir_s2d1,
3828e1199815SMickaël Salaün 			.access = ACCESS_RW,
3829e1199815SMickaël Salaün 		},
3830135464f9SMickaël Salaün 		{},
3831e1199815SMickaël Salaün 	};
3832e1199815SMickaël Salaün 	/*
3833e1199815SMickaël Salaün 	 * Sets access rights on the same bind-mounted directories.  The result
3834e1199815SMickaël Salaün 	 * should be ACCESS_RW for both directories, but not both hierarchies
3835e1199815SMickaël Salaün 	 * because of the first layer.
3836e1199815SMickaël Salaün 	 */
3837e1199815SMickaël Salaün 	const struct rule layer2_mount_point[] = {
3838e1199815SMickaël Salaün 		{
3839e1199815SMickaël Salaün 			.path = dir_s1d2,
3840e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
3841e1199815SMickaël Salaün 		},
3842e1199815SMickaël Salaün 		{
3843e1199815SMickaël Salaün 			.path = dir_s2d2,
3844e1199815SMickaël Salaün 			.access = ACCESS_RW,
3845e1199815SMickaël Salaün 		},
3846135464f9SMickaël Salaün 		{},
3847e1199815SMickaël Salaün 	};
3848e1199815SMickaël Salaün 	/* Only allow read-access to the s1d3 hierarchies. */
3849e1199815SMickaël Salaün 	const struct rule layer3_source[] = {
3850e1199815SMickaël Salaün 		{
3851e1199815SMickaël Salaün 			.path = dir_s1d3,
3852e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
3853e1199815SMickaël Salaün 		},
3854135464f9SMickaël Salaün 		{},
3855e1199815SMickaël Salaün 	};
3856e1199815SMickaël Salaün 	/* Removes all access rights. */
3857e1199815SMickaël Salaün 	const struct rule layer4_destination[] = {
3858e1199815SMickaël Salaün 		{
3859e1199815SMickaël Salaün 			.path = bind_file1_s1d3,
3860e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3861e1199815SMickaël Salaün 		},
3862135464f9SMickaël Salaün 		{},
3863e1199815SMickaël Salaün 	};
3864e1199815SMickaël Salaün 	int ruleset_fd;
3865e1199815SMickaël Salaün 
3866e1199815SMickaël Salaün 	/* Sets rules for the parent directories. */
3867e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent);
3868e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3869e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3870e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3871e1199815SMickaël Salaün 
3872e1199815SMickaël Salaün 	/* Checks source hierarchy. */
3873e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
3874e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
3875e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
3876e1199815SMickaël Salaün 
3877e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3878e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
3879e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
3880e1199815SMickaël Salaün 
3881e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
3882e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR));
3883e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
3884e1199815SMickaël Salaün 
3885e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
3886e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
3887e1199815SMickaël Salaün 
3888e1199815SMickaël Salaün 	/* Sets rules for the mount points. */
3889e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point);
3890e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3891e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3892e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3893e1199815SMickaël Salaün 
3894e1199815SMickaël Salaün 	/* Checks source hierarchy. */
3895e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
3896e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
3897e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
3898e1199815SMickaël Salaün 
3899e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3900e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
3901e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
3902e1199815SMickaël Salaün 
3903e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
3904e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY));
3905e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY));
3906e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
3907e1199815SMickaël Salaün 
3908e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
3909e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
3910e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
3911e1199815SMickaël Salaün 
3912e1199815SMickaël Salaün 	/* Sets a (shared) rule only on the source. */
3913e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source);
3914e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3915e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3916e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3917e1199815SMickaël Salaün 
3918e1199815SMickaël Salaün 	/* Checks source hierarchy. */
3919e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
3920e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
3921e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
3922e1199815SMickaël Salaün 
3923e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
3924e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
3925e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
3926e1199815SMickaël Salaün 
3927e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
3928e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY));
3929e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY));
3930e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
3931e1199815SMickaël Salaün 
3932e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
3933e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
3934e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
3935e1199815SMickaël Salaün 
3936e1199815SMickaël Salaün 	/* Sets a (shared) rule only on the destination. */
3937e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination);
3938e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3939e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3940e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3941e1199815SMickaël Salaün 
3942e1199815SMickaël Salaün 	/* Checks source hierarchy. */
3943e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
3944e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
3945e1199815SMickaël Salaün 
3946e1199815SMickaël Salaün 	/* Checks destination hierarchy. */
3947e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY));
3948e1199815SMickaël Salaün 	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
3949e1199815SMickaël Salaün }
3950e1199815SMickaël Salaün 
TEST_F_FORK(layout1_bind,reparent_cross_mount)3951f4056b92SMickaël Salaün TEST_F_FORK(layout1_bind, reparent_cross_mount)
3952f4056b92SMickaël Salaün {
3953f4056b92SMickaël Salaün 	const struct rule layer1[] = {
3954f4056b92SMickaël Salaün 		{
3955f4056b92SMickaël Salaün 			/* dir_s2d1 is beneath the dir_s2d2 mount point. */
3956f4056b92SMickaël Salaün 			.path = dir_s2d1,
3957f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_REFER,
3958f4056b92SMickaël Salaün 		},
3959f4056b92SMickaël Salaün 		{
3960f4056b92SMickaël Salaün 			.path = bind_dir_s1d3,
3961f4056b92SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_EXECUTE,
3962f4056b92SMickaël Salaün 		},
3963f4056b92SMickaël Salaün 		{},
3964f4056b92SMickaël Salaün 	};
3965f4056b92SMickaël Salaün 	int ruleset_fd = create_ruleset(
3966f4056b92SMickaël Salaün 		_metadata,
3967f4056b92SMickaël Salaün 		LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1);
3968f4056b92SMickaël Salaün 
3969f4056b92SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
3970f4056b92SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
3971f4056b92SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
3972f4056b92SMickaël Salaün 
3973f4056b92SMickaël Salaün 	/* Checks basic denied move. */
3974f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2));
3975f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
3976f4056b92SMickaël Salaün 
3977f4056b92SMickaël Salaün 	/* Checks real cross-mount move (Landlock is not involved). */
3978f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2));
3979f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
3980f4056b92SMickaël Salaün 
3981f4056b92SMickaël Salaün 	/* Checks move that will give more accesses. */
3982f4056b92SMickaël Salaün 	ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3));
3983f4056b92SMickaël Salaün 	ASSERT_EQ(EXDEV, errno);
3984f4056b92SMickaël Salaün 
3985f4056b92SMickaël Salaün 	/* Checks legitimate downgrade move. */
3986f4056b92SMickaël Salaün 	ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2));
3987f4056b92SMickaël Salaün }
3988f4056b92SMickaël Salaün 
3989e1199815SMickaël Salaün #define LOWER_BASE TMP_DIR "/lower"
3990e1199815SMickaël Salaün #define LOWER_DATA LOWER_BASE "/data"
3991e1199815SMickaël Salaün static const char lower_fl1[] = LOWER_DATA "/fl1";
3992e1199815SMickaël Salaün static const char lower_dl1[] = LOWER_DATA "/dl1";
3993e1199815SMickaël Salaün static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";
3994e1199815SMickaël Salaün static const char lower_fo1[] = LOWER_DATA "/fo1";
3995e1199815SMickaël Salaün static const char lower_do1[] = LOWER_DATA "/do1";
3996e1199815SMickaël Salaün static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";
3997e1199815SMickaël Salaün static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";
3998e1199815SMickaël Salaün 
3999e1199815SMickaël Salaün static const char (*lower_base_files[])[] = {
4000e1199815SMickaël Salaün 	&lower_fl1,
4001e1199815SMickaël Salaün 	&lower_fo1,
4002135464f9SMickaël Salaün 	NULL,
4003e1199815SMickaël Salaün };
4004e1199815SMickaël Salaün static const char (*lower_base_directories[])[] = {
4005e1199815SMickaël Salaün 	&lower_dl1,
4006e1199815SMickaël Salaün 	&lower_do1,
4007135464f9SMickaël Salaün 	NULL,
4008e1199815SMickaël Salaün };
4009e1199815SMickaël Salaün static const char (*lower_sub_files[])[] = {
4010e1199815SMickaël Salaün 	&lower_dl1_fl2,
4011e1199815SMickaël Salaün 	&lower_do1_fo2,
4012e1199815SMickaël Salaün 	&lower_do1_fl3,
4013135464f9SMickaël Salaün 	NULL,
4014e1199815SMickaël Salaün };
4015e1199815SMickaël Salaün 
4016e1199815SMickaël Salaün #define UPPER_BASE TMP_DIR "/upper"
4017e1199815SMickaël Salaün #define UPPER_DATA UPPER_BASE "/data"
4018e1199815SMickaël Salaün #define UPPER_WORK UPPER_BASE "/work"
4019e1199815SMickaël Salaün static const char upper_fu1[] = UPPER_DATA "/fu1";
4020e1199815SMickaël Salaün static const char upper_du1[] = UPPER_DATA "/du1";
4021e1199815SMickaël Salaün static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";
4022e1199815SMickaël Salaün static const char upper_fo1[] = UPPER_DATA "/fo1";
4023e1199815SMickaël Salaün static const char upper_do1[] = UPPER_DATA "/do1";
4024e1199815SMickaël Salaün static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";
4025e1199815SMickaël Salaün static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";
4026e1199815SMickaël Salaün 
4027e1199815SMickaël Salaün static const char (*upper_base_files[])[] = {
4028e1199815SMickaël Salaün 	&upper_fu1,
4029e1199815SMickaël Salaün 	&upper_fo1,
4030135464f9SMickaël Salaün 	NULL,
4031e1199815SMickaël Salaün };
4032e1199815SMickaël Salaün static const char (*upper_base_directories[])[] = {
4033e1199815SMickaël Salaün 	&upper_du1,
4034e1199815SMickaël Salaün 	&upper_do1,
4035135464f9SMickaël Salaün 	NULL,
4036e1199815SMickaël Salaün };
4037e1199815SMickaël Salaün static const char (*upper_sub_files[])[] = {
4038e1199815SMickaël Salaün 	&upper_du1_fu2,
4039e1199815SMickaël Salaün 	&upper_do1_fo2,
4040e1199815SMickaël Salaün 	&upper_do1_fu3,
4041135464f9SMickaël Salaün 	NULL,
4042e1199815SMickaël Salaün };
4043e1199815SMickaël Salaün 
4044e1199815SMickaël Salaün #define MERGE_BASE TMP_DIR "/merge"
4045e1199815SMickaël Salaün #define MERGE_DATA MERGE_BASE "/data"
4046e1199815SMickaël Salaün static const char merge_fl1[] = MERGE_DATA "/fl1";
4047e1199815SMickaël Salaün static const char merge_dl1[] = MERGE_DATA "/dl1";
4048e1199815SMickaël Salaün static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";
4049e1199815SMickaël Salaün static const char merge_fu1[] = MERGE_DATA "/fu1";
4050e1199815SMickaël Salaün static const char merge_du1[] = MERGE_DATA "/du1";
4051e1199815SMickaël Salaün static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";
4052e1199815SMickaël Salaün static const char merge_fo1[] = MERGE_DATA "/fo1";
4053e1199815SMickaël Salaün static const char merge_do1[] = MERGE_DATA "/do1";
4054e1199815SMickaël Salaün static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";
4055e1199815SMickaël Salaün static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";
4056e1199815SMickaël Salaün static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";
4057e1199815SMickaël Salaün 
4058e1199815SMickaël Salaün static const char (*merge_base_files[])[] = {
4059e1199815SMickaël Salaün 	&merge_fl1,
4060e1199815SMickaël Salaün 	&merge_fu1,
4061e1199815SMickaël Salaün 	&merge_fo1,
4062135464f9SMickaël Salaün 	NULL,
4063e1199815SMickaël Salaün };
4064e1199815SMickaël Salaün static const char (*merge_base_directories[])[] = {
4065e1199815SMickaël Salaün 	&merge_dl1,
4066e1199815SMickaël Salaün 	&merge_du1,
4067e1199815SMickaël Salaün 	&merge_do1,
4068135464f9SMickaël Salaün 	NULL,
4069e1199815SMickaël Salaün };
4070e1199815SMickaël Salaün static const char (*merge_sub_files[])[] = {
4071371183faSMickaël Salaün 	&merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2,
4072371183faSMickaël Salaün 	&merge_do1_fl3, &merge_do1_fu3, NULL,
4073e1199815SMickaël Salaün };
4074e1199815SMickaël Salaün 
4075e1199815SMickaël Salaün /*
4076e1199815SMickaël Salaün  * layout2_overlay hierarchy:
4077e1199815SMickaël Salaün  *
4078e1199815SMickaël Salaün  * tmp
4079e1199815SMickaël Salaün  * ├── lower
4080e1199815SMickaël Salaün  * │   └── data
4081e1199815SMickaël Salaün  * │       ├── dl1
4082e1199815SMickaël Salaün  * │       │   └── fl2
4083e1199815SMickaël Salaün  * │       ├── do1
4084e1199815SMickaël Salaün  * │       │   ├── fl3
4085e1199815SMickaël Salaün  * │       │   └── fo2
4086e1199815SMickaël Salaün  * │       ├── fl1
4087e1199815SMickaël Salaün  * │       └── fo1
4088e1199815SMickaël Salaün  * ├── merge
4089e1199815SMickaël Salaün  * │   └── data
4090e1199815SMickaël Salaün  * │       ├── dl1
4091e1199815SMickaël Salaün  * │       │   └── fl2
4092e1199815SMickaël Salaün  * │       ├── do1
4093e1199815SMickaël Salaün  * │       │   ├── fl3
4094e1199815SMickaël Salaün  * │       │   ├── fo2
4095e1199815SMickaël Salaün  * │       │   └── fu3
4096e1199815SMickaël Salaün  * │       ├── du1
4097e1199815SMickaël Salaün  * │       │   └── fu2
4098e1199815SMickaël Salaün  * │       ├── fl1
4099e1199815SMickaël Salaün  * │       ├── fo1
4100e1199815SMickaël Salaün  * │       └── fu1
4101e1199815SMickaël Salaün  * └── upper
4102e1199815SMickaël Salaün  *     ├── data
4103e1199815SMickaël Salaün  *     │   ├── do1
4104e1199815SMickaël Salaün  *     │   │   ├── fo2
4105e1199815SMickaël Salaün  *     │   │   └── fu3
4106e1199815SMickaël Salaün  *     │   ├── du1
4107e1199815SMickaël Salaün  *     │   │   └── fu2
4108e1199815SMickaël Salaün  *     │   ├── fo1
4109e1199815SMickaël Salaün  *     │   └── fu1
4110e1199815SMickaël Salaün  *     └── work
4111e1199815SMickaël Salaün  *         └── work
4112e1199815SMickaël Salaün  */
4113e1199815SMickaël Salaün 
FIXTURE(layout2_overlay)41143de64b65SMickaël Salaün FIXTURE(layout2_overlay)
41153de64b65SMickaël Salaün {
41163de64b65SMickaël Salaün 	bool skip_test;
41173de64b65SMickaël Salaün };
4118e1199815SMickaël Salaün 
FIXTURE_SETUP(layout2_overlay)4119e1199815SMickaël Salaün FIXTURE_SETUP(layout2_overlay)
4120e1199815SMickaël Salaün {
41213de64b65SMickaël Salaün 	if (!supports_filesystem("overlay")) {
41223de64b65SMickaël Salaün 		self->skip_test = true;
41233de64b65SMickaël Salaün 		SKIP(return, "overlayfs is not supported (setup)");
41243de64b65SMickaël Salaün 	}
4125366617a6SJeff Xu 
4126e1199815SMickaël Salaün 	prepare_layout(_metadata);
4127e1199815SMickaël Salaün 
4128e1199815SMickaël Salaün 	create_directory(_metadata, LOWER_BASE);
4129e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4130e1199815SMickaël Salaün 	/* Creates tmpfs mount points to get deterministic overlayfs. */
413155ab3fbeSMickaël Salaün 	ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE));
4132e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4133e1199815SMickaël Salaün 	create_file(_metadata, lower_fl1);
4134e1199815SMickaël Salaün 	create_file(_metadata, lower_dl1_fl2);
4135e1199815SMickaël Salaün 	create_file(_metadata, lower_fo1);
4136e1199815SMickaël Salaün 	create_file(_metadata, lower_do1_fo2);
4137e1199815SMickaël Salaün 	create_file(_metadata, lower_do1_fl3);
4138e1199815SMickaël Salaün 
4139e1199815SMickaël Salaün 	create_directory(_metadata, UPPER_BASE);
4140e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
414155ab3fbeSMickaël Salaün 	ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE));
4142e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4143e1199815SMickaël Salaün 	create_file(_metadata, upper_fu1);
4144e1199815SMickaël Salaün 	create_file(_metadata, upper_du1_fu2);
4145e1199815SMickaël Salaün 	create_file(_metadata, upper_fo1);
4146e1199815SMickaël Salaün 	create_file(_metadata, upper_do1_fo2);
4147e1199815SMickaël Salaün 	create_file(_metadata, upper_do1_fu3);
4148e1199815SMickaël Salaün 	ASSERT_EQ(0, mkdir(UPPER_WORK, 0700));
4149e1199815SMickaël Salaün 
4150e1199815SMickaël Salaün 	create_directory(_metadata, MERGE_DATA);
4151e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4152e1199815SMickaël Salaün 	set_cap(_metadata, CAP_DAC_OVERRIDE);
4153e1199815SMickaël Salaün 	ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0,
4154371183faSMickaël Salaün 			   "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA
4155e1199815SMickaël Salaün 			   ",workdir=" UPPER_WORK));
4156e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_DAC_OVERRIDE);
4157e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4158e1199815SMickaël Salaün }
4159e1199815SMickaël Salaün 
FIXTURE_TEARDOWN(layout2_overlay)4160e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout2_overlay)
4161e1199815SMickaël Salaün {
41623de64b65SMickaël Salaün 	if (self->skip_test)
41633de64b65SMickaël Salaün 		SKIP(return, "overlayfs is not supported (teardown)");
4164366617a6SJeff Xu 
4165e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_do1_fl3));
4166e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_dl1_fl2));
4167e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_fl1));
4168e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_do1_fo2));
4169e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(lower_fo1));
4170e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4171e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(LOWER_BASE));
4172e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4173e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(LOWER_BASE));
4174e1199815SMickaël Salaün 
4175e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_do1_fu3));
4176e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_du1_fu2));
4177e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_fu1));
4178e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_do1_fo2));
4179e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(upper_fo1));
4180e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(UPPER_WORK "/work"));
4181e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4182e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(UPPER_BASE));
4183e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4184e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(UPPER_BASE));
4185e1199815SMickaël Salaün 
4186e1199815SMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
4187e1199815SMickaël Salaün 	EXPECT_EQ(0, umount(MERGE_DATA));
4188e1199815SMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
4189e1199815SMickaël Salaün 	EXPECT_EQ(0, remove_path(MERGE_DATA));
4190e1199815SMickaël Salaün 
4191e1199815SMickaël Salaün 	cleanup_layout(_metadata);
4192e1199815SMickaël Salaün }
4193e1199815SMickaël Salaün 
TEST_F_FORK(layout2_overlay,no_restriction)4194e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, no_restriction)
4195e1199815SMickaël Salaün {
41963de64b65SMickaël Salaün 	if (self->skip_test)
41973de64b65SMickaël Salaün 		SKIP(return, "overlayfs is not supported (test)");
4198366617a6SJeff Xu 
4199e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
4200e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
4201e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));
4202e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY));
4203e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_do1, O_RDONLY));
4204e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY));
4205e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY));
4206e1199815SMickaël Salaün 
4207e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY));
4208e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_du1, O_RDONLY));
4209e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY));
4210e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY));
4211e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_do1, O_RDONLY));
4212e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY));
4213e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY));
4214e1199815SMickaël Salaün 
4215e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY));
4216e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY));
4217e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY));
4218e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY));
4219e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_du1, O_RDONLY));
4220e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY));
4221e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY));
4222e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1, O_RDONLY));
4223e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY));
4224e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY));
4225e1199815SMickaël Salaün 	ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY));
4226e1199815SMickaël Salaün }
4227e1199815SMickaël Salaün 
4228e1199815SMickaël Salaün #define for_each_path(path_list, path_entry, i)               \
4229e1199815SMickaël Salaün 	for (i = 0, path_entry = *path_list[i]; path_list[i]; \
4230e1199815SMickaël Salaün 	     path_entry = *path_list[++i])
4231e1199815SMickaël Salaün 
TEST_F_FORK(layout2_overlay,same_content_different_file)4232e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, same_content_different_file)
4233e1199815SMickaël Salaün {
4234e1199815SMickaël Salaün 	/* Sets access right on parent directories of both layers. */
4235e1199815SMickaël Salaün 	const struct rule layer1_base[] = {
4236e1199815SMickaël Salaün 		{
4237e1199815SMickaël Salaün 			.path = LOWER_BASE,
4238e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4239e1199815SMickaël Salaün 		},
4240e1199815SMickaël Salaün 		{
4241e1199815SMickaël Salaün 			.path = UPPER_BASE,
4242e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4243e1199815SMickaël Salaün 		},
4244e1199815SMickaël Salaün 		{
4245e1199815SMickaël Salaün 			.path = MERGE_BASE,
4246e1199815SMickaël Salaün 			.access = ACCESS_RW,
4247e1199815SMickaël Salaün 		},
4248135464f9SMickaël Salaün 		{},
4249e1199815SMickaël Salaün 	};
4250e1199815SMickaël Salaün 	const struct rule layer2_data[] = {
4251e1199815SMickaël Salaün 		{
4252e1199815SMickaël Salaün 			.path = LOWER_DATA,
4253e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4254e1199815SMickaël Salaün 		},
4255e1199815SMickaël Salaün 		{
4256e1199815SMickaël Salaün 			.path = UPPER_DATA,
4257e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4258e1199815SMickaël Salaün 		},
4259e1199815SMickaël Salaün 		{
4260e1199815SMickaël Salaün 			.path = MERGE_DATA,
4261e1199815SMickaël Salaün 			.access = ACCESS_RW,
4262e1199815SMickaël Salaün 		},
4263135464f9SMickaël Salaün 		{},
4264e1199815SMickaël Salaün 	};
4265e1199815SMickaël Salaün 	/* Sets access right on directories inside both layers. */
4266e1199815SMickaël Salaün 	const struct rule layer3_subdirs[] = {
4267e1199815SMickaël Salaün 		{
4268e1199815SMickaël Salaün 			.path = lower_dl1,
4269e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4270e1199815SMickaël Salaün 		},
4271e1199815SMickaël Salaün 		{
4272e1199815SMickaël Salaün 			.path = lower_do1,
4273e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4274e1199815SMickaël Salaün 		},
4275e1199815SMickaël Salaün 		{
4276e1199815SMickaël Salaün 			.path = upper_du1,
4277e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4278e1199815SMickaël Salaün 		},
4279e1199815SMickaël Salaün 		{
4280e1199815SMickaël Salaün 			.path = upper_do1,
4281e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4282e1199815SMickaël Salaün 		},
4283e1199815SMickaël Salaün 		{
4284e1199815SMickaël Salaün 			.path = merge_dl1,
4285e1199815SMickaël Salaün 			.access = ACCESS_RW,
4286e1199815SMickaël Salaün 		},
4287e1199815SMickaël Salaün 		{
4288e1199815SMickaël Salaün 			.path = merge_du1,
4289e1199815SMickaël Salaün 			.access = ACCESS_RW,
4290e1199815SMickaël Salaün 		},
4291e1199815SMickaël Salaün 		{
4292e1199815SMickaël Salaün 			.path = merge_do1,
4293e1199815SMickaël Salaün 			.access = ACCESS_RW,
4294e1199815SMickaël Salaün 		},
4295135464f9SMickaël Salaün 		{},
4296e1199815SMickaël Salaün 	};
4297e1199815SMickaël Salaün 	/* Tighten access rights to the files. */
4298e1199815SMickaël Salaün 	const struct rule layer4_files[] = {
4299e1199815SMickaël Salaün 		{
4300e1199815SMickaël Salaün 			.path = lower_dl1_fl2,
4301e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4302e1199815SMickaël Salaün 		},
4303e1199815SMickaël Salaün 		{
4304e1199815SMickaël Salaün 			.path = lower_do1_fo2,
4305e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4306e1199815SMickaël Salaün 		},
4307e1199815SMickaël Salaün 		{
4308e1199815SMickaël Salaün 			.path = lower_do1_fl3,
4309e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4310e1199815SMickaël Salaün 		},
4311e1199815SMickaël Salaün 		{
4312e1199815SMickaël Salaün 			.path = upper_du1_fu2,
4313e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4314e1199815SMickaël Salaün 		},
4315e1199815SMickaël Salaün 		{
4316e1199815SMickaël Salaün 			.path = upper_do1_fo2,
4317e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4318e1199815SMickaël Salaün 		},
4319e1199815SMickaël Salaün 		{
4320e1199815SMickaël Salaün 			.path = upper_do1_fu3,
4321e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
4322e1199815SMickaël Salaün 		},
4323e1199815SMickaël Salaün 		{
4324e1199815SMickaël Salaün 			.path = merge_dl1_fl2,
4325e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4326e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4327e1199815SMickaël Salaün 		},
4328e1199815SMickaël Salaün 		{
4329e1199815SMickaël Salaün 			.path = merge_du1_fu2,
4330e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4331e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4332e1199815SMickaël Salaün 		},
4333e1199815SMickaël Salaün 		{
4334e1199815SMickaël Salaün 			.path = merge_do1_fo2,
4335e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4336e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4337e1199815SMickaël Salaün 		},
4338e1199815SMickaël Salaün 		{
4339e1199815SMickaël Salaün 			.path = merge_do1_fl3,
4340e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4341e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4342e1199815SMickaël Salaün 		},
4343e1199815SMickaël Salaün 		{
4344e1199815SMickaël Salaün 			.path = merge_do1_fu3,
4345e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4346e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4347e1199815SMickaël Salaün 		},
4348135464f9SMickaël Salaün 		{},
4349e1199815SMickaël Salaün 	};
4350e1199815SMickaël Salaün 	const struct rule layer5_merge_only[] = {
4351e1199815SMickaël Salaün 		{
4352e1199815SMickaël Salaün 			.path = MERGE_DATA,
4353e1199815SMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE |
4354e1199815SMickaël Salaün 				  LANDLOCK_ACCESS_FS_WRITE_FILE,
4355e1199815SMickaël Salaün 		},
4356135464f9SMickaël Salaün 		{},
4357e1199815SMickaël Salaün 	};
4358e1199815SMickaël Salaün 	int ruleset_fd;
4359e1199815SMickaël Salaün 	size_t i;
4360e1199815SMickaël Salaün 	const char *path_entry;
4361e1199815SMickaël Salaün 
43623de64b65SMickaël Salaün 	if (self->skip_test)
43633de64b65SMickaël Salaün 		SKIP(return, "overlayfs is not supported (test)");
4364366617a6SJeff Xu 
4365e1199815SMickaël Salaün 	/* Sets rules on base directories (i.e. outside overlay scope). */
4366e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
4367e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4368e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4369e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4370e1199815SMickaël Salaün 
4371e1199815SMickaël Salaün 	/* Checks lower layer. */
4372e1199815SMickaël Salaün 	for_each_path(lower_base_files, path_entry, i) {
4373e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4374e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4375e1199815SMickaël Salaün 	}
4376e1199815SMickaël Salaün 	for_each_path(lower_base_directories, path_entry, i) {
4377371183faSMickaël Salaün 		ASSERT_EQ(EACCES,
4378371183faSMickaël Salaün 			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
4379e1199815SMickaël Salaün 	}
4380e1199815SMickaël Salaün 	for_each_path(lower_sub_files, path_entry, i) {
4381e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4382e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4383e1199815SMickaël Salaün 	}
4384e1199815SMickaël Salaün 	/* Checks upper layer. */
4385e1199815SMickaël Salaün 	for_each_path(upper_base_files, path_entry, i) {
4386e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4387e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4388e1199815SMickaël Salaün 	}
4389e1199815SMickaël Salaün 	for_each_path(upper_base_directories, path_entry, i) {
4390371183faSMickaël Salaün 		ASSERT_EQ(EACCES,
4391371183faSMickaël Salaün 			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
4392e1199815SMickaël Salaün 	}
4393e1199815SMickaël Salaün 	for_each_path(upper_sub_files, path_entry, i) {
4394e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4395e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4396e1199815SMickaël Salaün 	}
4397e1199815SMickaël Salaün 	/*
4398e1199815SMickaël Salaün 	 * Checks that access rights are independent from the lower and upper
4399e1199815SMickaël Salaün 	 * layers: write access to upper files viewed through the merge point
4400e1199815SMickaël Salaün 	 * is still allowed, and write access to lower file viewed (and copied)
4401e1199815SMickaël Salaün 	 * through the merge point is still allowed.
4402e1199815SMickaël Salaün 	 */
4403e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4404e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4405e1199815SMickaël Salaün 	}
4406e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4407e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
4408e1199815SMickaël Salaün 	}
4409e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4410e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4411e1199815SMickaël Salaün 	}
4412e1199815SMickaël Salaün 
4413e1199815SMickaël Salaün 	/* Sets rules on data directories (i.e. inside overlay scope). */
4414e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data);
4415e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4416e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4417e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4418e1199815SMickaël Salaün 
4419e1199815SMickaël Salaün 	/* Checks merge. */
4420e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4421e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4422e1199815SMickaël Salaün 	}
4423e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4424e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
4425e1199815SMickaël Salaün 	}
4426e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4427e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4428e1199815SMickaël Salaün 	}
4429e1199815SMickaël Salaün 
4430e1199815SMickaël Salaün 	/* Same checks with tighter rules. */
4431e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs);
4432e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4433e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4434e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4435e1199815SMickaël Salaün 
4436e1199815SMickaël Salaün 	/* Checks changes for lower layer. */
4437e1199815SMickaël Salaün 	for_each_path(lower_base_files, path_entry, i) {
4438e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
4439e1199815SMickaël Salaün 	}
4440e1199815SMickaël Salaün 	/* Checks changes for upper layer. */
4441e1199815SMickaël Salaün 	for_each_path(upper_base_files, path_entry, i) {
4442e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
4443e1199815SMickaël Salaün 	}
4444e1199815SMickaël Salaün 	/* Checks all merge accesses. */
4445e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4446e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
4447e1199815SMickaël Salaün 	}
4448e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4449e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
4450e1199815SMickaël Salaün 	}
4451e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4452e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4453e1199815SMickaël Salaün 	}
4454e1199815SMickaël Salaün 
4455e1199815SMickaël Salaün 	/* Sets rules directly on overlayed files. */
4456e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files);
4457e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4458e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4459e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4460e1199815SMickaël Salaün 
4461e1199815SMickaël Salaün 	/* Checks unchanged accesses on lower layer. */
4462e1199815SMickaël Salaün 	for_each_path(lower_sub_files, path_entry, i) {
4463e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4464e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4465e1199815SMickaël Salaün 	}
4466e1199815SMickaël Salaün 	/* Checks unchanged accesses on upper layer. */
4467e1199815SMickaël Salaün 	for_each_path(upper_sub_files, path_entry, i) {
4468e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4469e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4470e1199815SMickaël Salaün 	}
4471e1199815SMickaël Salaün 	/* Checks all merge accesses. */
4472e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4473e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
4474e1199815SMickaël Salaün 	}
4475e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4476371183faSMickaël Salaün 		ASSERT_EQ(EACCES,
4477371183faSMickaël Salaün 			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
4478e1199815SMickaël Salaün 	}
4479e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4480e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4481e1199815SMickaël Salaün 	}
4482e1199815SMickaël Salaün 
4483e1199815SMickaël Salaün 	/* Only allowes access to the merge hierarchy. */
4484e1199815SMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only);
4485e1199815SMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
4486e1199815SMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
4487e1199815SMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
4488e1199815SMickaël Salaün 
4489e1199815SMickaël Salaün 	/* Checks new accesses on lower layer. */
4490e1199815SMickaël Salaün 	for_each_path(lower_sub_files, path_entry, i) {
4491e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
4492e1199815SMickaël Salaün 	}
4493e1199815SMickaël Salaün 	/* Checks new accesses on upper layer. */
4494e1199815SMickaël Salaün 	for_each_path(upper_sub_files, path_entry, i) {
4495e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
4496e1199815SMickaël Salaün 	}
4497e1199815SMickaël Salaün 	/* Checks all merge accesses. */
4498e1199815SMickaël Salaün 	for_each_path(merge_base_files, path_entry, i) {
4499e1199815SMickaël Salaün 		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
4500e1199815SMickaël Salaün 	}
4501e1199815SMickaël Salaün 	for_each_path(merge_base_directories, path_entry, i) {
4502371183faSMickaël Salaün 		ASSERT_EQ(EACCES,
4503371183faSMickaël Salaün 			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
4504e1199815SMickaël Salaün 	}
4505e1199815SMickaël Salaün 	for_each_path(merge_sub_files, path_entry, i) {
4506e1199815SMickaël Salaün 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
4507e1199815SMickaël Salaün 	}
4508e1199815SMickaël Salaün }
4509e1199815SMickaël Salaün 
FIXTURE(layout3_fs)451004f9070eSMickaël Salaün FIXTURE(layout3_fs)
451104f9070eSMickaël Salaün {
451204f9070eSMickaël Salaün 	bool has_created_dir;
451304f9070eSMickaël Salaün 	bool has_created_file;
451404f9070eSMickaël Salaün 	char *dir_path;
451504f9070eSMickaël Salaün 	bool skip_test;
451604f9070eSMickaël Salaün };
451704f9070eSMickaël Salaün 
FIXTURE_VARIANT(layout3_fs)451804f9070eSMickaël Salaün FIXTURE_VARIANT(layout3_fs)
451904f9070eSMickaël Salaün {
452004f9070eSMickaël Salaün 	const struct mnt_opt mnt;
452104f9070eSMickaël Salaün 	const char *const file_path;
452235ca4239SMickaël Salaün 	unsigned int cwd_fs_magic;
452304f9070eSMickaël Salaün };
452404f9070eSMickaël Salaün 
452504f9070eSMickaël Salaün /* clang-format off */
FIXTURE_VARIANT_ADD(layout3_fs,tmpfs)452604f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) {
452704f9070eSMickaël Salaün 	/* clang-format on */
4528*854357dbSHu Yadi 	.mnt = {
4529*854357dbSHu Yadi 		.type = "tmpfs",
4530*854357dbSHu Yadi 		.data = MNT_TMP_DATA,
4531*854357dbSHu Yadi 	},
453204f9070eSMickaël Salaün 	.file_path = file1_s1d1,
453304f9070eSMickaël Salaün };
453404f9070eSMickaël Salaün 
FIXTURE_VARIANT_ADD(layout3_fs,ramfs)453504f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, ramfs) {
453604f9070eSMickaël Salaün 	.mnt = {
453704f9070eSMickaël Salaün 		.type = "ramfs",
453804f9070eSMickaël Salaün 		.data = "mode=700",
453904f9070eSMickaël Salaün 	},
454004f9070eSMickaël Salaün 	.file_path = TMP_DIR "/dir/file",
454104f9070eSMickaël Salaün };
454204f9070eSMickaël Salaün 
FIXTURE_VARIANT_ADD(layout3_fs,cgroup2)454304f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) {
454404f9070eSMickaël Salaün 	.mnt = {
454504f9070eSMickaël Salaün 		.type = "cgroup2",
454604f9070eSMickaël Salaün 	},
454704f9070eSMickaël Salaün 	.file_path = TMP_DIR "/test/cgroup.procs",
454804f9070eSMickaël Salaün };
454904f9070eSMickaël Salaün 
FIXTURE_VARIANT_ADD(layout3_fs,proc)455004f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, proc) {
455104f9070eSMickaël Salaün 	.mnt = {
455204f9070eSMickaël Salaün 		.type = "proc",
455304f9070eSMickaël Salaün 	},
455404f9070eSMickaël Salaün 	.file_path = TMP_DIR "/self/status",
455504f9070eSMickaël Salaün };
455604f9070eSMickaël Salaün 
FIXTURE_VARIANT_ADD(layout3_fs,sysfs)455704f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, sysfs) {
455804f9070eSMickaël Salaün 	.mnt = {
455904f9070eSMickaël Salaün 		.type = "sysfs",
456004f9070eSMickaël Salaün 	},
456104f9070eSMickaël Salaün 	.file_path = TMP_DIR "/kernel/notes",
456204f9070eSMickaël Salaün };
456304f9070eSMickaël Salaün 
FIXTURE_VARIANT_ADD(layout3_fs,hostfs)456435ca4239SMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, hostfs) {
456535ca4239SMickaël Salaün 	.mnt = {
456635ca4239SMickaël Salaün 		.source = TMP_DIR,
456735ca4239SMickaël Salaün 		.flags = MS_BIND,
456835ca4239SMickaël Salaün 	},
456935ca4239SMickaël Salaün 	.file_path = TMP_DIR "/dir/file",
457035ca4239SMickaël Salaün 	.cwd_fs_magic = HOSTFS_SUPER_MAGIC,
457135ca4239SMickaël Salaün };
457235ca4239SMickaël Salaün 
FIXTURE_SETUP(layout3_fs)457304f9070eSMickaël Salaün FIXTURE_SETUP(layout3_fs)
457404f9070eSMickaël Salaün {
457504f9070eSMickaël Salaün 	struct stat statbuf;
457604f9070eSMickaël Salaün 	const char *slash;
457704f9070eSMickaël Salaün 	size_t dir_len;
457804f9070eSMickaël Salaün 
457935ca4239SMickaël Salaün 	if (!supports_filesystem(variant->mnt.type) ||
458035ca4239SMickaël Salaün 	    !cwd_matches_fs(variant->cwd_fs_magic)) {
458104f9070eSMickaël Salaün 		self->skip_test = true;
458204f9070eSMickaël Salaün 		SKIP(return, "this filesystem is not supported (setup)");
458304f9070eSMickaël Salaün 	}
458404f9070eSMickaël Salaün 
458504f9070eSMickaël Salaün 	slash = strrchr(variant->file_path, '/');
458604f9070eSMickaël Salaün 	ASSERT_NE(slash, NULL);
458704f9070eSMickaël Salaün 	dir_len = (size_t)slash - (size_t)variant->file_path;
458804f9070eSMickaël Salaün 	ASSERT_LT(0, dir_len);
458904f9070eSMickaël Salaün 	self->dir_path = malloc(dir_len + 1);
459004f9070eSMickaël Salaün 	self->dir_path[dir_len] = '\0';
459104f9070eSMickaël Salaün 	strncpy(self->dir_path, variant->file_path, dir_len);
459204f9070eSMickaël Salaün 
459304f9070eSMickaël Salaün 	prepare_layout_opt(_metadata, &variant->mnt);
459404f9070eSMickaël Salaün 
459504f9070eSMickaël Salaün 	/* Creates directory when required. */
459604f9070eSMickaël Salaün 	if (stat(self->dir_path, &statbuf)) {
459704f9070eSMickaël Salaün 		set_cap(_metadata, CAP_DAC_OVERRIDE);
459804f9070eSMickaël Salaün 		EXPECT_EQ(0, mkdir(self->dir_path, 0700))
459904f9070eSMickaël Salaün 		{
460004f9070eSMickaël Salaün 			TH_LOG("Failed to create directory \"%s\": %s",
460104f9070eSMickaël Salaün 			       self->dir_path, strerror(errno));
460204f9070eSMickaël Salaün 			free(self->dir_path);
460304f9070eSMickaël Salaün 			self->dir_path = NULL;
460404f9070eSMickaël Salaün 		}
460504f9070eSMickaël Salaün 		self->has_created_dir = true;
460604f9070eSMickaël Salaün 		clear_cap(_metadata, CAP_DAC_OVERRIDE);
460704f9070eSMickaël Salaün 	}
460804f9070eSMickaël Salaün 
460904f9070eSMickaël Salaün 	/* Creates file when required. */
461004f9070eSMickaël Salaün 	if (stat(variant->file_path, &statbuf)) {
461104f9070eSMickaël Salaün 		int fd;
461204f9070eSMickaël Salaün 
461304f9070eSMickaël Salaün 		set_cap(_metadata, CAP_DAC_OVERRIDE);
461404f9070eSMickaël Salaün 		fd = creat(variant->file_path, 0600);
461504f9070eSMickaël Salaün 		EXPECT_LE(0, fd)
461604f9070eSMickaël Salaün 		{
461704f9070eSMickaël Salaün 			TH_LOG("Failed to create file \"%s\": %s",
461804f9070eSMickaël Salaün 			       variant->file_path, strerror(errno));
461904f9070eSMickaël Salaün 		}
462004f9070eSMickaël Salaün 		EXPECT_EQ(0, close(fd));
462104f9070eSMickaël Salaün 		self->has_created_file = true;
462204f9070eSMickaël Salaün 		clear_cap(_metadata, CAP_DAC_OVERRIDE);
462304f9070eSMickaël Salaün 	}
462404f9070eSMickaël Salaün }
462504f9070eSMickaël Salaün 
FIXTURE_TEARDOWN(layout3_fs)462604f9070eSMickaël Salaün FIXTURE_TEARDOWN(layout3_fs)
462704f9070eSMickaël Salaün {
462804f9070eSMickaël Salaün 	if (self->skip_test)
462904f9070eSMickaël Salaün 		SKIP(return, "this filesystem is not supported (teardown)");
463004f9070eSMickaël Salaün 
463104f9070eSMickaël Salaün 	if (self->has_created_file) {
463204f9070eSMickaël Salaün 		set_cap(_metadata, CAP_DAC_OVERRIDE);
463304f9070eSMickaël Salaün 		/*
463404f9070eSMickaël Salaün 		 * Don't check for error because the file might already
463504f9070eSMickaël Salaün 		 * have been removed (cf. release_inode test).
463604f9070eSMickaël Salaün 		 */
463704f9070eSMickaël Salaün 		unlink(variant->file_path);
463804f9070eSMickaël Salaün 		clear_cap(_metadata, CAP_DAC_OVERRIDE);
463904f9070eSMickaël Salaün 	}
464004f9070eSMickaël Salaün 
464104f9070eSMickaël Salaün 	if (self->has_created_dir) {
464204f9070eSMickaël Salaün 		set_cap(_metadata, CAP_DAC_OVERRIDE);
464304f9070eSMickaël Salaün 		/*
464404f9070eSMickaël Salaün 		 * Don't check for error because the directory might already
464504f9070eSMickaël Salaün 		 * have been removed (cf. release_inode test).
464604f9070eSMickaël Salaün 		 */
464704f9070eSMickaël Salaün 		rmdir(self->dir_path);
464804f9070eSMickaël Salaün 		clear_cap(_metadata, CAP_DAC_OVERRIDE);
464904f9070eSMickaël Salaün 	}
465004f9070eSMickaël Salaün 	free(self->dir_path);
465104f9070eSMickaël Salaün 	self->dir_path = NULL;
465204f9070eSMickaël Salaün 
465304f9070eSMickaël Salaün 	cleanup_layout(_metadata);
465404f9070eSMickaël Salaün }
465504f9070eSMickaël Salaün 
layer3_fs_tag_inode(struct __test_metadata * const _metadata,FIXTURE_DATA (layout3_fs)* self,const FIXTURE_VARIANT (layout3_fs)* variant,const char * const rule_path)465604f9070eSMickaël Salaün static void layer3_fs_tag_inode(struct __test_metadata *const _metadata,
465704f9070eSMickaël Salaün 				FIXTURE_DATA(layout3_fs) * self,
465804f9070eSMickaël Salaün 				const FIXTURE_VARIANT(layout3_fs) * variant,
465904f9070eSMickaël Salaün 				const char *const rule_path)
466004f9070eSMickaël Salaün {
466104f9070eSMickaël Salaün 	const struct rule layer1_allow_read_file[] = {
466204f9070eSMickaël Salaün 		{
466304f9070eSMickaël Salaün 			.path = rule_path,
466404f9070eSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_FILE,
466504f9070eSMickaël Salaün 		},
466604f9070eSMickaël Salaün 		{},
466704f9070eSMickaël Salaün 	};
466804f9070eSMickaël Salaün 	const struct landlock_ruleset_attr layer2_deny_everything_attr = {
466904f9070eSMickaël Salaün 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
467004f9070eSMickaël Salaün 	};
467104f9070eSMickaël Salaün 	const char *const dev_null_path = "/dev/null";
467204f9070eSMickaël Salaün 	int ruleset_fd;
467304f9070eSMickaël Salaün 
467404f9070eSMickaël Salaün 	if (self->skip_test)
467504f9070eSMickaël Salaün 		SKIP(return, "this filesystem is not supported (test)");
467604f9070eSMickaël Salaün 
467704f9070eSMickaël Salaün 	/* Checks without Landlock. */
467804f9070eSMickaël Salaün 	EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
467904f9070eSMickaël Salaün 	EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
468004f9070eSMickaël Salaün 
468104f9070eSMickaël Salaün 	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
468204f9070eSMickaël Salaün 				    layer1_allow_read_file);
468304f9070eSMickaël Salaün 	EXPECT_LE(0, ruleset_fd);
468404f9070eSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
468504f9070eSMickaël Salaün 	EXPECT_EQ(0, close(ruleset_fd));
468604f9070eSMickaël Salaün 
468704f9070eSMickaël Salaün 	EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
468804f9070eSMickaël Salaün 	EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
468904f9070eSMickaël Salaün 
469004f9070eSMickaël Salaün 	/* Forbids directory reading. */
469104f9070eSMickaël Salaün 	ruleset_fd =
469204f9070eSMickaël Salaün 		landlock_create_ruleset(&layer2_deny_everything_attr,
469304f9070eSMickaël Salaün 					sizeof(layer2_deny_everything_attr), 0);
469404f9070eSMickaël Salaün 	EXPECT_LE(0, ruleset_fd);
469504f9070eSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
469604f9070eSMickaël Salaün 	EXPECT_EQ(0, close(ruleset_fd));
469704f9070eSMickaël Salaün 
469804f9070eSMickaël Salaün 	/* Checks with Landlock and forbidden access. */
469904f9070eSMickaël Salaün 	EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
470004f9070eSMickaël Salaün 	EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
470104f9070eSMickaël Salaün }
470204f9070eSMickaël Salaün 
470304f9070eSMickaël Salaün /* Matrix of tests to check file hierarchy evaluation. */
470404f9070eSMickaël Salaün 
TEST_F_FORK(layout3_fs,tag_inode_dir_parent)470504f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, tag_inode_dir_parent)
470604f9070eSMickaël Salaün {
470704f9070eSMickaël Salaün 	/* The current directory must not be the root for this test. */
470804f9070eSMickaël Salaün 	layer3_fs_tag_inode(_metadata, self, variant, ".");
470904f9070eSMickaël Salaün }
471004f9070eSMickaël Salaün 
TEST_F_FORK(layout3_fs,tag_inode_dir_mnt)471104f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, tag_inode_dir_mnt)
471204f9070eSMickaël Salaün {
471304f9070eSMickaël Salaün 	layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR);
471404f9070eSMickaël Salaün }
471504f9070eSMickaël Salaün 
TEST_F_FORK(layout3_fs,tag_inode_dir_child)471604f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, tag_inode_dir_child)
471704f9070eSMickaël Salaün {
471804f9070eSMickaël Salaün 	layer3_fs_tag_inode(_metadata, self, variant, self->dir_path);
471904f9070eSMickaël Salaün }
472004f9070eSMickaël Salaün 
TEST_F_FORK(layout3_fs,tag_inode_file)472104f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, tag_inode_file)
472204f9070eSMickaël Salaün {
472304f9070eSMickaël Salaün 	layer3_fs_tag_inode(_metadata, self, variant, variant->file_path);
472404f9070eSMickaël Salaün }
472504f9070eSMickaël Salaün 
472604f9070eSMickaël Salaün /* Light version of layout1.release_inodes */
TEST_F_FORK(layout3_fs,release_inodes)472704f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, release_inodes)
472804f9070eSMickaël Salaün {
472904f9070eSMickaël Salaün 	const struct rule layer1[] = {
473004f9070eSMickaël Salaün 		{
473104f9070eSMickaël Salaün 			.path = TMP_DIR,
473204f9070eSMickaël Salaün 			.access = LANDLOCK_ACCESS_FS_READ_DIR,
473304f9070eSMickaël Salaün 		},
473404f9070eSMickaël Salaün 		{},
473504f9070eSMickaël Salaün 	};
473604f9070eSMickaël Salaün 	int ruleset_fd;
473704f9070eSMickaël Salaün 
473804f9070eSMickaël Salaün 	if (self->skip_test)
473904f9070eSMickaël Salaün 		SKIP(return, "this filesystem is not supported (test)");
474004f9070eSMickaël Salaün 
474104f9070eSMickaël Salaün 	/* Clean up for the teardown to not fail. */
474204f9070eSMickaël Salaün 	if (self->has_created_file)
474304f9070eSMickaël Salaün 		EXPECT_EQ(0, remove_path(variant->file_path));
474404f9070eSMickaël Salaün 
474504f9070eSMickaël Salaün 	if (self->has_created_dir)
474604f9070eSMickaël Salaün 		/* Don't check for error because of cgroup specificities. */
474704f9070eSMickaël Salaün 		remove_path(self->dir_path);
474804f9070eSMickaël Salaün 
474904f9070eSMickaël Salaün 	ruleset_fd =
475004f9070eSMickaël Salaün 		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);
475104f9070eSMickaël Salaün 	ASSERT_LE(0, ruleset_fd);
475204f9070eSMickaël Salaün 
475304f9070eSMickaël Salaün 	/* Unmount the filesystem while it is being used by a ruleset. */
475404f9070eSMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
475504f9070eSMickaël Salaün 	ASSERT_EQ(0, umount(TMP_DIR));
475604f9070eSMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
475704f9070eSMickaël Salaün 
475804f9070eSMickaël Salaün 	/* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */
475904f9070eSMickaël Salaün 	set_cap(_metadata, CAP_SYS_ADMIN);
476004f9070eSMickaël Salaün 	ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR));
476104f9070eSMickaël Salaün 	clear_cap(_metadata, CAP_SYS_ADMIN);
476204f9070eSMickaël Salaün 
476304f9070eSMickaël Salaün 	enforce_ruleset(_metadata, ruleset_fd);
476404f9070eSMickaël Salaün 	ASSERT_EQ(0, close(ruleset_fd));
476504f9070eSMickaël Salaün 
476604f9070eSMickaël Salaün 	/* Checks that access to the new mount point is denied. */
476704f9070eSMickaël Salaün 	ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY));
476804f9070eSMickaël Salaün }
476904f9070eSMickaël Salaün 
4770e1199815SMickaël Salaün TEST_HARNESS_MAIN
4771