162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Landlock tests - Filesystem
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
662306a36Sopenharmony_ci * Copyright © 2020 ANSSI
762306a36Sopenharmony_ci * Copyright © 2020-2022 Microsoft Corporation
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define _GNU_SOURCE
1162306a36Sopenharmony_ci#include <fcntl.h>
1262306a36Sopenharmony_ci#include <linux/landlock.h>
1362306a36Sopenharmony_ci#include <linux/magic.h>
1462306a36Sopenharmony_ci#include <sched.h>
1562306a36Sopenharmony_ci#include <stdio.h>
1662306a36Sopenharmony_ci#include <string.h>
1762306a36Sopenharmony_ci#include <sys/capability.h>
1862306a36Sopenharmony_ci#include <sys/mount.h>
1962306a36Sopenharmony_ci#include <sys/prctl.h>
2062306a36Sopenharmony_ci#include <sys/sendfile.h>
2162306a36Sopenharmony_ci#include <sys/stat.h>
2262306a36Sopenharmony_ci#include <sys/sysmacros.h>
2362306a36Sopenharmony_ci#include <sys/vfs.h>
2462306a36Sopenharmony_ci#include <unistd.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "common.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#ifndef renameat2
2962306a36Sopenharmony_ciint renameat2(int olddirfd, const char *oldpath, int newdirfd,
3062306a36Sopenharmony_ci	      const char *newpath, unsigned int flags)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath,
3362306a36Sopenharmony_ci		       flags);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci#endif
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifndef RENAME_EXCHANGE
3862306a36Sopenharmony_ci#define RENAME_EXCHANGE (1 << 1)
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define TMP_DIR "tmp"
4262306a36Sopenharmony_ci#define BINARY_PATH "./true"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Paths (sibling number and depth) */
4562306a36Sopenharmony_cistatic const char dir_s1d1[] = TMP_DIR "/s1d1";
4662306a36Sopenharmony_cistatic const char file1_s1d1[] = TMP_DIR "/s1d1/f1";
4762306a36Sopenharmony_cistatic const char file2_s1d1[] = TMP_DIR "/s1d1/f2";
4862306a36Sopenharmony_cistatic const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2";
4962306a36Sopenharmony_cistatic const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1";
5062306a36Sopenharmony_cistatic const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2";
5162306a36Sopenharmony_cistatic const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3";
5262306a36Sopenharmony_cistatic const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1";
5362306a36Sopenharmony_cistatic const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2";
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic const char dir_s2d1[] = TMP_DIR "/s2d1";
5662306a36Sopenharmony_cistatic const char file1_s2d1[] = TMP_DIR "/s2d1/f1";
5762306a36Sopenharmony_cistatic const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2";
5862306a36Sopenharmony_cistatic const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1";
5962306a36Sopenharmony_cistatic const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3";
6062306a36Sopenharmony_cistatic const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1";
6162306a36Sopenharmony_cistatic const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2";
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic const char dir_s3d1[] = TMP_DIR "/s3d1";
6462306a36Sopenharmony_cistatic const char file1_s3d1[] = TMP_DIR "/s3d1/f1";
6562306a36Sopenharmony_ci/* dir_s3d2 is a mount point. */
6662306a36Sopenharmony_cistatic const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2";
6762306a36Sopenharmony_cistatic const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3";
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * layout1 hierarchy:
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * tmp
7362306a36Sopenharmony_ci * ├── s1d1
7462306a36Sopenharmony_ci * │   ├── f1
7562306a36Sopenharmony_ci * │   ├── f2
7662306a36Sopenharmony_ci * │   └── s1d2
7762306a36Sopenharmony_ci * │       ├── f1
7862306a36Sopenharmony_ci * │       ├── f2
7962306a36Sopenharmony_ci * │       └── s1d3
8062306a36Sopenharmony_ci * │           ├── f1
8162306a36Sopenharmony_ci * │           └── f2
8262306a36Sopenharmony_ci * ├── s2d1
8362306a36Sopenharmony_ci * │   ├── f1
8462306a36Sopenharmony_ci * │   └── s2d2
8562306a36Sopenharmony_ci * │       ├── f1
8662306a36Sopenharmony_ci * │       └── s2d3
8762306a36Sopenharmony_ci * │           ├── f1
8862306a36Sopenharmony_ci * │           └── f2
8962306a36Sopenharmony_ci * └── s3d1
9062306a36Sopenharmony_ci *     ├── f1
9162306a36Sopenharmony_ci *     └── s3d2
9262306a36Sopenharmony_ci *         └── s3d3
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic bool fgrep(FILE *const inf, const char *const str)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	char line[32];
9862306a36Sopenharmony_ci	const int slen = strlen(str);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	while (!feof(inf)) {
10162306a36Sopenharmony_ci		if (!fgets(line, sizeof(line), inf))
10262306a36Sopenharmony_ci			break;
10362306a36Sopenharmony_ci		if (strncmp(line, str, slen))
10462306a36Sopenharmony_ci			continue;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		return true;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return false;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic bool supports_filesystem(const char *const filesystem)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	char str[32];
11562306a36Sopenharmony_ci	int len;
11662306a36Sopenharmony_ci	bool res = true;
11762306a36Sopenharmony_ci	FILE *const inf = fopen("/proc/filesystems", "r");
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/*
12062306a36Sopenharmony_ci	 * Consider that the filesystem is supported if we cannot get the
12162306a36Sopenharmony_ci	 * supported ones.
12262306a36Sopenharmony_ci	 */
12362306a36Sopenharmony_ci	if (!inf)
12462306a36Sopenharmony_ci		return true;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* filesystem can be null for bind mounts. */
12762306a36Sopenharmony_ci	if (!filesystem)
12862306a36Sopenharmony_ci		goto out;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem);
13162306a36Sopenharmony_ci	if (len >= sizeof(str))
13262306a36Sopenharmony_ci		/* Ignores too-long filesystem names. */
13362306a36Sopenharmony_ci		goto out;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	res = fgrep(inf, str);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciout:
13862306a36Sopenharmony_ci	fclose(inf);
13962306a36Sopenharmony_ci	return res;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic bool cwd_matches_fs(unsigned int fs_magic)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct statfs statfs_buf;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (!fs_magic)
14762306a36Sopenharmony_ci		return true;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (statfs(".", &statfs_buf))
15062306a36Sopenharmony_ci		return true;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return statfs_buf.f_type == fs_magic;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic void mkdir_parents(struct __test_metadata *const _metadata,
15662306a36Sopenharmony_ci			  const char *const path)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	char *walker;
15962306a36Sopenharmony_ci	const char *parent;
16062306a36Sopenharmony_ci	int i, err;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ASSERT_NE(path[0], '\0');
16362306a36Sopenharmony_ci	walker = strdup(path);
16462306a36Sopenharmony_ci	ASSERT_NE(NULL, walker);
16562306a36Sopenharmony_ci	parent = walker;
16662306a36Sopenharmony_ci	for (i = 1; walker[i]; i++) {
16762306a36Sopenharmony_ci		if (walker[i] != '/')
16862306a36Sopenharmony_ci			continue;
16962306a36Sopenharmony_ci		walker[i] = '\0';
17062306a36Sopenharmony_ci		err = mkdir(parent, 0700);
17162306a36Sopenharmony_ci		ASSERT_FALSE(err && errno != EEXIST)
17262306a36Sopenharmony_ci		{
17362306a36Sopenharmony_ci			TH_LOG("Failed to create directory \"%s\": %s", parent,
17462306a36Sopenharmony_ci			       strerror(errno));
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci		walker[i] = '/';
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci	free(walker);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void create_directory(struct __test_metadata *const _metadata,
18262306a36Sopenharmony_ci			     const char *const path)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	mkdir_parents(_metadata, path);
18562306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(path, 0700))
18662306a36Sopenharmony_ci	{
18762306a36Sopenharmony_ci		TH_LOG("Failed to create directory \"%s\": %s", path,
18862306a36Sopenharmony_ci		       strerror(errno));
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void create_file(struct __test_metadata *const _metadata,
19362306a36Sopenharmony_ci			const char *const path)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	mkdir_parents(_metadata, path);
19662306a36Sopenharmony_ci	ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0))
19762306a36Sopenharmony_ci	{
19862306a36Sopenharmony_ci		TH_LOG("Failed to create file \"%s\": %s", path,
19962306a36Sopenharmony_ci		       strerror(errno));
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int remove_path(const char *const path)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	char *walker;
20662306a36Sopenharmony_ci	int i, ret, err = 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	walker = strdup(path);
20962306a36Sopenharmony_ci	if (!walker) {
21062306a36Sopenharmony_ci		err = ENOMEM;
21162306a36Sopenharmony_ci		goto out;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci	if (unlink(path) && rmdir(path)) {
21462306a36Sopenharmony_ci		if (errno != ENOENT && errno != ENOTDIR)
21562306a36Sopenharmony_ci			err = errno;
21662306a36Sopenharmony_ci		goto out;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci	for (i = strlen(walker); i > 0; i--) {
21962306a36Sopenharmony_ci		if (walker[i] != '/')
22062306a36Sopenharmony_ci			continue;
22162306a36Sopenharmony_ci		walker[i] = '\0';
22262306a36Sopenharmony_ci		ret = rmdir(walker);
22362306a36Sopenharmony_ci		if (ret) {
22462306a36Sopenharmony_ci			if (errno != ENOTEMPTY && errno != EBUSY)
22562306a36Sopenharmony_ci				err = errno;
22662306a36Sopenharmony_ci			goto out;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci		if (strcmp(walker, TMP_DIR) == 0)
22962306a36Sopenharmony_ci			goto out;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ciout:
23362306a36Sopenharmony_ci	free(walker);
23462306a36Sopenharmony_ci	return err;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistruct mnt_opt {
23862306a36Sopenharmony_ci	const char *const source;
23962306a36Sopenharmony_ci	const char *const type;
24062306a36Sopenharmony_ci	const unsigned long flags;
24162306a36Sopenharmony_ci	const char *const data;
24262306a36Sopenharmony_ci};
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci#define MNT_TMP_DATA "size=4m,mode=700"
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic const struct mnt_opt mnt_tmp = {
24762306a36Sopenharmony_ci	.type = "tmpfs",
24862306a36Sopenharmony_ci	.data = MNT_TMP_DATA,
24962306a36Sopenharmony_ci};
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int mount_opt(const struct mnt_opt *const mnt, const char *const target)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags,
25462306a36Sopenharmony_ci		     mnt->data);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void prepare_layout_opt(struct __test_metadata *const _metadata,
25862306a36Sopenharmony_ci			       const struct mnt_opt *const mnt)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	disable_caps(_metadata);
26162306a36Sopenharmony_ci	umask(0077);
26262306a36Sopenharmony_ci	create_directory(_metadata, TMP_DIR);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/*
26562306a36Sopenharmony_ci	 * Do not pollute the rest of the system: creates a private mount point
26662306a36Sopenharmony_ci	 * for tests relying on pivot_root(2) and move_mount(2).
26762306a36Sopenharmony_ci	 */
26862306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
26962306a36Sopenharmony_ci	ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP));
27062306a36Sopenharmony_ci	ASSERT_EQ(0, mount_opt(mnt, TMP_DIR))
27162306a36Sopenharmony_ci	{
27262306a36Sopenharmony_ci		TH_LOG("Failed to mount the %s filesystem: %s", mnt->type,
27362306a36Sopenharmony_ci		       strerror(errno));
27462306a36Sopenharmony_ci		/*
27562306a36Sopenharmony_ci		 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP()
27662306a36Sopenharmony_ci		 * failed, so we need to explicitly do a minimal cleanup to
27762306a36Sopenharmony_ci		 * avoid cascading errors with other tests that don't depend on
27862306a36Sopenharmony_ci		 * the same filesystem.
27962306a36Sopenharmony_ci		 */
28062306a36Sopenharmony_ci		remove_path(TMP_DIR);
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci	ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));
28362306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void prepare_layout(struct __test_metadata *const _metadata)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	prepare_layout_opt(_metadata, &mnt_tmp);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic void cleanup_layout(struct __test_metadata *const _metadata)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
29462306a36Sopenharmony_ci	EXPECT_EQ(0, umount(TMP_DIR));
29562306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
29662306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(TMP_DIR));
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/* clang-format off */
30062306a36Sopenharmony_ciFIXTURE(layout0) {};
30162306a36Sopenharmony_ci/* clang-format on */
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ciFIXTURE_SETUP(layout0)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	prepare_layout(_metadata);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout0)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	cleanup_layout(_metadata);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic void create_layout1(struct __test_metadata *const _metadata)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	create_file(_metadata, file1_s1d1);
31662306a36Sopenharmony_ci	create_file(_metadata, file1_s1d2);
31762306a36Sopenharmony_ci	create_file(_metadata, file1_s1d3);
31862306a36Sopenharmony_ci	create_file(_metadata, file2_s1d1);
31962306a36Sopenharmony_ci	create_file(_metadata, file2_s1d2);
32062306a36Sopenharmony_ci	create_file(_metadata, file2_s1d3);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	create_file(_metadata, file1_s2d1);
32362306a36Sopenharmony_ci	create_file(_metadata, file1_s2d2);
32462306a36Sopenharmony_ci	create_file(_metadata, file1_s2d3);
32562306a36Sopenharmony_ci	create_file(_metadata, file2_s2d3);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	create_file(_metadata, file1_s3d1);
32862306a36Sopenharmony_ci	create_directory(_metadata, dir_s3d2);
32962306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
33062306a36Sopenharmony_ci	ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2));
33162306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(dir_s3d3, 0700));
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void remove_layout1(struct __test_metadata *const _metadata)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file2_s1d3));
33962306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file2_s1d2));
34062306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file2_s1d1));
34162306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file1_s1d3));
34262306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file1_s1d2));
34362306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file1_s1d1));
34462306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(dir_s1d3));
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file2_s2d3));
34762306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file1_s2d3));
34862306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file1_s2d2));
34962306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file1_s2d1));
35062306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(dir_s2d2));
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file1_s3d1));
35362306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(dir_s3d3));
35462306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
35562306a36Sopenharmony_ci	umount(dir_s3d2);
35662306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
35762306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(dir_s3d2));
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/* clang-format off */
36162306a36Sopenharmony_ciFIXTURE(layout1) {};
36262306a36Sopenharmony_ci/* clang-format on */
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ciFIXTURE_SETUP(layout1)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	prepare_layout(_metadata);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	create_layout1(_metadata);
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout1)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	remove_layout1(_metadata);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	cleanup_layout(_metadata);
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/*
37962306a36Sopenharmony_ci * This helper enables to use the ASSERT_* macros and print the line number
38062306a36Sopenharmony_ci * pointing to the test caller.
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_cistatic int test_open_rel(const int dirfd, const char *const path,
38362306a36Sopenharmony_ci			 const int flags)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	int fd;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* Works with file and directories. */
38862306a36Sopenharmony_ci	fd = openat(dirfd, path, flags | O_CLOEXEC);
38962306a36Sopenharmony_ci	if (fd < 0)
39062306a36Sopenharmony_ci		return errno;
39162306a36Sopenharmony_ci	/*
39262306a36Sopenharmony_ci	 * Mixing error codes from close(2) and open(2) should not lead to any
39362306a36Sopenharmony_ci	 * (access type) confusion for this test.
39462306a36Sopenharmony_ci	 */
39562306a36Sopenharmony_ci	if (close(fd) != 0)
39662306a36Sopenharmony_ci		return errno;
39762306a36Sopenharmony_ci	return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int test_open(const char *const path, const int flags)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	return test_open_rel(AT_FDCWD, path, flags);
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ciTEST_F_FORK(layout1, no_restriction)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
40862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
40962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY));
41062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
41162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
41262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY));
41362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
41462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
41762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
41862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
41962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
42062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY));
42162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY));
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
42462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
42562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ciTEST_F_FORK(layout1, inval)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct landlock_path_beneath_attr path_beneath = {
43162306a36Sopenharmony_ci		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
43262306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
43362306a36Sopenharmony_ci		.parent_fd = -1,
43462306a36Sopenharmony_ci	};
43562306a36Sopenharmony_ci	struct landlock_ruleset_attr ruleset_attr = {
43662306a36Sopenharmony_ci		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |
43762306a36Sopenharmony_ci				     LANDLOCK_ACCESS_FS_WRITE_FILE,
43862306a36Sopenharmony_ci	};
43962306a36Sopenharmony_ci	int ruleset_fd;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	path_beneath.parent_fd =
44262306a36Sopenharmony_ci		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
44362306a36Sopenharmony_ci	ASSERT_LE(0, path_beneath.parent_fd);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);
44662306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
44762306a36Sopenharmony_ci	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
44862306a36Sopenharmony_ci					&path_beneath, 0));
44962306a36Sopenharmony_ci	/* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */
45062306a36Sopenharmony_ci	ASSERT_EQ(EBADF, errno);
45162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC);
45462306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
45562306a36Sopenharmony_ci	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
45662306a36Sopenharmony_ci					&path_beneath, 0));
45762306a36Sopenharmony_ci	/* Returns EBADFD because ruleset_fd is not a valid ruleset. */
45862306a36Sopenharmony_ci	ASSERT_EQ(EBADFD, errno);
45962306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* Gets a real ruleset. */
46262306a36Sopenharmony_ci	ruleset_fd =
46362306a36Sopenharmony_ci		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
46462306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
46562306a36Sopenharmony_ci	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
46662306a36Sopenharmony_ci				       &path_beneath, 0));
46762306a36Sopenharmony_ci	ASSERT_EQ(0, close(path_beneath.parent_fd));
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	/* Tests without O_PATH. */
47062306a36Sopenharmony_ci	path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC);
47162306a36Sopenharmony_ci	ASSERT_LE(0, path_beneath.parent_fd);
47262306a36Sopenharmony_ci	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
47362306a36Sopenharmony_ci				       &path_beneath, 0));
47462306a36Sopenharmony_ci	ASSERT_EQ(0, close(path_beneath.parent_fd));
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/* Tests with a ruleset FD. */
47762306a36Sopenharmony_ci	path_beneath.parent_fd = ruleset_fd;
47862306a36Sopenharmony_ci	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
47962306a36Sopenharmony_ci					&path_beneath, 0));
48062306a36Sopenharmony_ci	ASSERT_EQ(EBADFD, errno);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/* Checks unhandled allowed_access. */
48362306a36Sopenharmony_ci	path_beneath.parent_fd =
48462306a36Sopenharmony_ci		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
48562306a36Sopenharmony_ci	ASSERT_LE(0, path_beneath.parent_fd);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* Test with legitimate values. */
48862306a36Sopenharmony_ci	path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE;
48962306a36Sopenharmony_ci	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
49062306a36Sopenharmony_ci					&path_beneath, 0));
49162306a36Sopenharmony_ci	ASSERT_EQ(EINVAL, errno);
49262306a36Sopenharmony_ci	path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* Tests with denied-by-default access right. */
49562306a36Sopenharmony_ci	path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER;
49662306a36Sopenharmony_ci	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
49762306a36Sopenharmony_ci					&path_beneath, 0));
49862306a36Sopenharmony_ci	ASSERT_EQ(EINVAL, errno);
49962306a36Sopenharmony_ci	path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* Test with unknown (64-bits) value. */
50262306a36Sopenharmony_ci	path_beneath.allowed_access |= (1ULL << 60);
50362306a36Sopenharmony_ci	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
50462306a36Sopenharmony_ci					&path_beneath, 0));
50562306a36Sopenharmony_ci	ASSERT_EQ(EINVAL, errno);
50662306a36Sopenharmony_ci	path_beneath.allowed_access &= ~(1ULL << 60);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* Test with no access. */
50962306a36Sopenharmony_ci	path_beneath.allowed_access = 0;
51062306a36Sopenharmony_ci	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
51162306a36Sopenharmony_ci					&path_beneath, 0));
51262306a36Sopenharmony_ci	ASSERT_EQ(ENOMSG, errno);
51362306a36Sopenharmony_ci	path_beneath.allowed_access &= ~(1ULL << 60);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	ASSERT_EQ(0, close(path_beneath.parent_fd));
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* Enforces the ruleset. */
51862306a36Sopenharmony_ci	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
51962306a36Sopenharmony_ci	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci/* clang-format off */
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci#define ACCESS_FILE ( \
52762306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_EXECUTE | \
52862306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_WRITE_FILE | \
52962306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_READ_FILE | \
53062306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_TRUNCATE)
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci#define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci#define ACCESS_ALL ( \
53562306a36Sopenharmony_ci	ACCESS_FILE | \
53662306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_READ_DIR | \
53762306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_REMOVE_DIR | \
53862306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_REMOVE_FILE | \
53962306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_MAKE_CHAR | \
54062306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_MAKE_DIR | \
54162306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_MAKE_REG | \
54262306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_MAKE_SOCK | \
54362306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_MAKE_FIFO | \
54462306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
54562306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_MAKE_SYM | \
54662306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_REFER)
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci/* clang-format on */
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ciTEST_F_FORK(layout1, file_and_dir_access_rights)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	__u64 access;
55362306a36Sopenharmony_ci	int err;
55462306a36Sopenharmony_ci	struct landlock_path_beneath_attr path_beneath_file = {},
55562306a36Sopenharmony_ci					  path_beneath_dir = {};
55662306a36Sopenharmony_ci	struct landlock_ruleset_attr ruleset_attr = {
55762306a36Sopenharmony_ci		.handled_access_fs = ACCESS_ALL,
55862306a36Sopenharmony_ci	};
55962306a36Sopenharmony_ci	const int ruleset_fd =
56062306a36Sopenharmony_ci		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* Tests access rights for files. */
56562306a36Sopenharmony_ci	path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);
56662306a36Sopenharmony_ci	ASSERT_LE(0, path_beneath_file.parent_fd);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* Tests access rights for directories. */
56962306a36Sopenharmony_ci	path_beneath_dir.parent_fd =
57062306a36Sopenharmony_ci		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
57162306a36Sopenharmony_ci	ASSERT_LE(0, path_beneath_dir.parent_fd);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	for (access = 1; access <= ACCESS_LAST; access <<= 1) {
57462306a36Sopenharmony_ci		path_beneath_dir.allowed_access = access;
57562306a36Sopenharmony_ci		ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
57662306a36Sopenharmony_ci					       LANDLOCK_RULE_PATH_BENEATH,
57762306a36Sopenharmony_ci					       &path_beneath_dir, 0));
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci		path_beneath_file.allowed_access = access;
58062306a36Sopenharmony_ci		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
58162306a36Sopenharmony_ci					&path_beneath_file, 0);
58262306a36Sopenharmony_ci		if (access & ACCESS_FILE) {
58362306a36Sopenharmony_ci			ASSERT_EQ(0, err);
58462306a36Sopenharmony_ci		} else {
58562306a36Sopenharmony_ci			ASSERT_EQ(-1, err);
58662306a36Sopenharmony_ci			ASSERT_EQ(EINVAL, errno);
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci	ASSERT_EQ(0, close(path_beneath_file.parent_fd));
59062306a36Sopenharmony_ci	ASSERT_EQ(0, close(path_beneath_dir.parent_fd));
59162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ciTEST_F_FORK(layout0, unknown_access_rights)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	__u64 access_mask;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST;
59962306a36Sopenharmony_ci	     access_mask >>= 1) {
60062306a36Sopenharmony_ci		struct landlock_ruleset_attr ruleset_attr = {
60162306a36Sopenharmony_ci			.handled_access_fs = access_mask,
60262306a36Sopenharmony_ci		};
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
60562306a36Sopenharmony_ci						      sizeof(ruleset_attr), 0));
60662306a36Sopenharmony_ci		ASSERT_EQ(EINVAL, errno);
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void add_path_beneath(struct __test_metadata *const _metadata,
61162306a36Sopenharmony_ci			     const int ruleset_fd, const __u64 allowed_access,
61262306a36Sopenharmony_ci			     const char *const path)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct landlock_path_beneath_attr path_beneath = {
61562306a36Sopenharmony_ci		.allowed_access = allowed_access,
61662306a36Sopenharmony_ci	};
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC);
61962306a36Sopenharmony_ci	ASSERT_LE(0, path_beneath.parent_fd)
62062306a36Sopenharmony_ci	{
62162306a36Sopenharmony_ci		TH_LOG("Failed to open directory \"%s\": %s", path,
62262306a36Sopenharmony_ci		       strerror(errno));
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
62562306a36Sopenharmony_ci				       &path_beneath, 0))
62662306a36Sopenharmony_ci	{
62762306a36Sopenharmony_ci		TH_LOG("Failed to update the ruleset with \"%s\": %s", path,
62862306a36Sopenharmony_ci		       strerror(errno));
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci	ASSERT_EQ(0, close(path_beneath.parent_fd));
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistruct rule {
63462306a36Sopenharmony_ci	const char *path;
63562306a36Sopenharmony_ci	__u64 access;
63662306a36Sopenharmony_ci};
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/* clang-format off */
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci#define ACCESS_RO ( \
64162306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_READ_FILE | \
64262306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_READ_DIR)
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci#define ACCESS_RW ( \
64562306a36Sopenharmony_ci	ACCESS_RO | \
64662306a36Sopenharmony_ci	LANDLOCK_ACCESS_FS_WRITE_FILE)
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci/* clang-format on */
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic int create_ruleset(struct __test_metadata *const _metadata,
65162306a36Sopenharmony_ci			  const __u64 handled_access_fs,
65262306a36Sopenharmony_ci			  const struct rule rules[])
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	int ruleset_fd, i;
65562306a36Sopenharmony_ci	struct landlock_ruleset_attr ruleset_attr = {
65662306a36Sopenharmony_ci		.handled_access_fs = handled_access_fs,
65762306a36Sopenharmony_ci	};
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	ASSERT_NE(NULL, rules)
66062306a36Sopenharmony_ci	{
66162306a36Sopenharmony_ci		TH_LOG("No rule list");
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci	ASSERT_NE(NULL, rules[0].path)
66462306a36Sopenharmony_ci	{
66562306a36Sopenharmony_ci		TH_LOG("Empty rule list");
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	ruleset_fd =
66962306a36Sopenharmony_ci		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
67062306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd)
67162306a36Sopenharmony_ci	{
67262306a36Sopenharmony_ci		TH_LOG("Failed to create a ruleset: %s", strerror(errno));
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	for (i = 0; rules[i].path; i++) {
67662306a36Sopenharmony_ci		add_path_beneath(_metadata, ruleset_fd, rules[i].access,
67762306a36Sopenharmony_ci				 rules[i].path);
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci	return ruleset_fd;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic void enforce_ruleset(struct __test_metadata *const _metadata,
68362306a36Sopenharmony_ci			    const int ruleset_fd)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
68662306a36Sopenharmony_ci	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0))
68762306a36Sopenharmony_ci	{
68862306a36Sopenharmony_ci		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ciTEST_F_FORK(layout0, proc_nsfs)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	const struct rule rules[] = {
69562306a36Sopenharmony_ci		{
69662306a36Sopenharmony_ci			.path = "/dev/null",
69762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
69862306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
69962306a36Sopenharmony_ci		},
70062306a36Sopenharmony_ci		{},
70162306a36Sopenharmony_ci	};
70262306a36Sopenharmony_ci	struct landlock_path_beneath_attr path_beneath;
70362306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(
70462306a36Sopenharmony_ci		_metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR,
70562306a36Sopenharmony_ci		rules);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
70862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
71362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY));
71462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open("/dev/null", O_RDONLY));
71562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY));
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY));
71862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY));
71962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY));
72062306a36Sopenharmony_ci	/*
72162306a36Sopenharmony_ci	 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a
72262306a36Sopenharmony_ci	 * disconnected path.  Such path cannot be identified and must then be
72362306a36Sopenharmony_ci	 * allowed.
72462306a36Sopenharmony_ci	 */
72562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/*
72862306a36Sopenharmony_ci	 * Checks that it is not possible to add nsfs-like filesystem
72962306a36Sopenharmony_ci	 * references to a ruleset.
73062306a36Sopenharmony_ci	 */
73162306a36Sopenharmony_ci	path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
73262306a36Sopenharmony_ci				      LANDLOCK_ACCESS_FS_WRITE_FILE,
73362306a36Sopenharmony_ci	path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC);
73462306a36Sopenharmony_ci	ASSERT_LE(0, path_beneath.parent_fd);
73562306a36Sopenharmony_ci	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
73662306a36Sopenharmony_ci					&path_beneath, 0));
73762306a36Sopenharmony_ci	ASSERT_EQ(EBADFD, errno);
73862306a36Sopenharmony_ci	ASSERT_EQ(0, close(path_beneath.parent_fd));
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ciTEST_F_FORK(layout0, unpriv)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	const struct rule rules[] = {
74462306a36Sopenharmony_ci		{
74562306a36Sopenharmony_ci			.path = TMP_DIR,
74662306a36Sopenharmony_ci			.access = ACCESS_RO,
74762306a36Sopenharmony_ci		},
74862306a36Sopenharmony_ci		{},
74962306a36Sopenharmony_ci	};
75062306a36Sopenharmony_ci	int ruleset_fd;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	drop_caps(_metadata);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
75562306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
75662306a36Sopenharmony_ci	ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));
75762306a36Sopenharmony_ci	ASSERT_EQ(EPERM, errno);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* enforce_ruleset() calls prctl(no_new_privs). */
76062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
76162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ciTEST_F_FORK(layout1, effective_access)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	const struct rule rules[] = {
76762306a36Sopenharmony_ci		{
76862306a36Sopenharmony_ci			.path = dir_s1d2,
76962306a36Sopenharmony_ci			.access = ACCESS_RO,
77062306a36Sopenharmony_ci		},
77162306a36Sopenharmony_ci		{
77262306a36Sopenharmony_ci			.path = file1_s2d2,
77362306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
77462306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
77562306a36Sopenharmony_ci		},
77662306a36Sopenharmony_ci		{},
77762306a36Sopenharmony_ci	};
77862306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
77962306a36Sopenharmony_ci	char buf;
78062306a36Sopenharmony_ci	int reg_fd;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
78362306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
78462306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* Tests on a directory (with or without O_PATH). */
78762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
78862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH));
78962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
79062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH));
79162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
79262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH));
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
79562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
79662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
79762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* Tests on a file (with or without O_PATH). */
80062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY));
80162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH));
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/* Checks effective read and write actions. */
80662306a36Sopenharmony_ci	reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC);
80762306a36Sopenharmony_ci	ASSERT_LE(0, reg_fd);
80862306a36Sopenharmony_ci	ASSERT_EQ(1, write(reg_fd, ".", 1));
80962306a36Sopenharmony_ci	ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET));
81062306a36Sopenharmony_ci	ASSERT_EQ(1, read(reg_fd, &buf, 1));
81162306a36Sopenharmony_ci	ASSERT_EQ('.', buf);
81262306a36Sopenharmony_ci	ASSERT_EQ(0, close(reg_fd));
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	/* Just in case, double-checks effective actions. */
81562306a36Sopenharmony_ci	reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC);
81662306a36Sopenharmony_ci	ASSERT_LE(0, reg_fd);
81762306a36Sopenharmony_ci	ASSERT_EQ(-1, write(reg_fd, &buf, 1));
81862306a36Sopenharmony_ci	ASSERT_EQ(EBADF, errno);
81962306a36Sopenharmony_ci	ASSERT_EQ(0, close(reg_fd));
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ciTEST_F_FORK(layout1, unhandled_access)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	const struct rule rules[] = {
82562306a36Sopenharmony_ci		{
82662306a36Sopenharmony_ci			.path = dir_s1d2,
82762306a36Sopenharmony_ci			.access = ACCESS_RO,
82862306a36Sopenharmony_ci		},
82962306a36Sopenharmony_ci		{},
83062306a36Sopenharmony_ci	};
83162306a36Sopenharmony_ci	/* Here, we only handle read accesses, not write accesses. */
83262306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
83562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
83662306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/*
83962306a36Sopenharmony_ci	 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE,
84062306a36Sopenharmony_ci	 * opening for write-only should be allowed, but not read-write.
84162306a36Sopenharmony_ci	 */
84262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY));
84362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
84662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ciTEST_F_FORK(layout1, ruleset_overlap)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	const struct rule rules[] = {
85262306a36Sopenharmony_ci		/* These rules should be ORed among them. */
85362306a36Sopenharmony_ci		{
85462306a36Sopenharmony_ci			.path = dir_s1d2,
85562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
85662306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
85762306a36Sopenharmony_ci		},
85862306a36Sopenharmony_ci		{
85962306a36Sopenharmony_ci			.path = dir_s1d2,
86062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
86162306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_READ_DIR,
86262306a36Sopenharmony_ci		},
86362306a36Sopenharmony_ci		{},
86462306a36Sopenharmony_ci	};
86562306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
86862306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
86962306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* Checks s1d1 hierarchy. */
87262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
87362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
87462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
87562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* Checks s1d2 hierarchy. */
87862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
87962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
88062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
88162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* Checks s1d3 hierarchy. */
88462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
88562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
88662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
88762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ciTEST_F_FORK(layout1, layer_rule_unions)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	const struct rule layer1[] = {
89362306a36Sopenharmony_ci		{
89462306a36Sopenharmony_ci			.path = dir_s1d2,
89562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
89662306a36Sopenharmony_ci		},
89762306a36Sopenharmony_ci		/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
89862306a36Sopenharmony_ci		{
89962306a36Sopenharmony_ci			.path = dir_s1d3,
90062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
90162306a36Sopenharmony_ci		},
90262306a36Sopenharmony_ci		{},
90362306a36Sopenharmony_ci	};
90462306a36Sopenharmony_ci	const struct rule layer2[] = {
90562306a36Sopenharmony_ci		/* Doesn't change anything from layer1. */
90662306a36Sopenharmony_ci		{
90762306a36Sopenharmony_ci			.path = dir_s1d2,
90862306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
90962306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
91062306a36Sopenharmony_ci		},
91162306a36Sopenharmony_ci		{},
91262306a36Sopenharmony_ci	};
91362306a36Sopenharmony_ci	const struct rule layer3[] = {
91462306a36Sopenharmony_ci		/* Only allows write (but not read) to dir_s1d3. */
91562306a36Sopenharmony_ci		{
91662306a36Sopenharmony_ci			.path = dir_s1d2,
91762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
91862306a36Sopenharmony_ci		},
91962306a36Sopenharmony_ci		{},
92062306a36Sopenharmony_ci	};
92162306a36Sopenharmony_ci	int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
92462306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
92562306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	/* Checks s1d1 hierarchy with layer1. */
92862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
92962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
93062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
93162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/* Checks s1d2 hierarchy with layer1. */
93462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
93562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
93662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
93762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	/* Checks s1d3 hierarchy with layer1. */
94062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
94162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
94262306a36Sopenharmony_ci	/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
94362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
94462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/* Doesn't change anything from layer1. */
94762306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2);
94862306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
94962306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
95062306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* Checks s1d1 hierarchy with layer2. */
95362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
95462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
95562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
95662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	/* Checks s1d2 hierarchy with layer2. */
95962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
96062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
96162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
96262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* Checks s1d3 hierarchy with layer2. */
96562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
96662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
96762306a36Sopenharmony_ci	/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
96862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
96962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	/* Only allows write (but not read) to dir_s1d3. */
97262306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3);
97362306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
97462306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
97562306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	/* Checks s1d1 hierarchy with layer3. */
97862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
97962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
98062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
98162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	/* Checks s1d2 hierarchy with layer3. */
98462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
98562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
98662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
98762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/* Checks s1d3 hierarchy with layer3. */
99062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
99162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
99262306a36Sopenharmony_ci	/* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */
99362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR));
99462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ciTEST_F_FORK(layout1, non_overlapping_accesses)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	const struct rule layer1[] = {
100062306a36Sopenharmony_ci		{
100162306a36Sopenharmony_ci			.path = dir_s1d2,
100262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
100362306a36Sopenharmony_ci		},
100462306a36Sopenharmony_ci		{},
100562306a36Sopenharmony_ci	};
100662306a36Sopenharmony_ci	const struct rule layer2[] = {
100762306a36Sopenharmony_ci		{
100862306a36Sopenharmony_ci			.path = dir_s1d3,
100962306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
101062306a36Sopenharmony_ci		},
101162306a36Sopenharmony_ci		{},
101262306a36Sopenharmony_ci	};
101362306a36Sopenharmony_ci	int ruleset_fd;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d1));
101662306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	ruleset_fd =
101962306a36Sopenharmony_ci		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1);
102062306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
102162306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
102262306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
102562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
102662306a36Sopenharmony_ci	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
102762306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE,
103062306a36Sopenharmony_ci				    layer2);
103162306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
103262306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
103362306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/* Unchanged accesses for file creation. */
103662306a36Sopenharmony_ci	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
103762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
103862306a36Sopenharmony_ci	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* Checks file removing. */
104162306a36Sopenharmony_ci	ASSERT_EQ(-1, unlink(file1_s1d2));
104262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
104362306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ciTEST_F_FORK(layout1, interleaved_masked_accesses)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	/*
104962306a36Sopenharmony_ci	 * Checks overly restrictive rules:
105062306a36Sopenharmony_ci	 * layer 1: allows R   s1d1/s1d2/s1d3/file1
105162306a36Sopenharmony_ci	 * layer 2: allows RW  s1d1/s1d2/s1d3
105262306a36Sopenharmony_ci	 *          allows  W  s1d1/s1d2
105362306a36Sopenharmony_ci	 *          denies R   s1d1/s1d2
105462306a36Sopenharmony_ci	 * layer 3: allows R   s1d1
105562306a36Sopenharmony_ci	 * layer 4: allows R   s1d1/s1d2
105662306a36Sopenharmony_ci	 *          denies  W  s1d1/s1d2
105762306a36Sopenharmony_ci	 * layer 5: allows R   s1d1/s1d2
105862306a36Sopenharmony_ci	 * layer 6: allows   X ----
105962306a36Sopenharmony_ci	 * layer 7: allows  W  s1d1/s1d2
106062306a36Sopenharmony_ci	 *          denies R   s1d1/s1d2
106162306a36Sopenharmony_ci	 */
106262306a36Sopenharmony_ci	const struct rule layer1_read[] = {
106362306a36Sopenharmony_ci		/* Allows read access to file1_s1d3 with the first layer. */
106462306a36Sopenharmony_ci		{
106562306a36Sopenharmony_ci			.path = file1_s1d3,
106662306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
106762306a36Sopenharmony_ci		},
106862306a36Sopenharmony_ci		{},
106962306a36Sopenharmony_ci	};
107062306a36Sopenharmony_ci	/* First rule with write restrictions. */
107162306a36Sopenharmony_ci	const struct rule layer2_read_write[] = {
107262306a36Sopenharmony_ci		/* Start by granting read-write access via its parent directory... */
107362306a36Sopenharmony_ci		{
107462306a36Sopenharmony_ci			.path = dir_s1d3,
107562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
107662306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
107762306a36Sopenharmony_ci		},
107862306a36Sopenharmony_ci		/* ...but also denies read access via its grandparent directory. */
107962306a36Sopenharmony_ci		{
108062306a36Sopenharmony_ci			.path = dir_s1d2,
108162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
108262306a36Sopenharmony_ci		},
108362306a36Sopenharmony_ci		{},
108462306a36Sopenharmony_ci	};
108562306a36Sopenharmony_ci	const struct rule layer3_read[] = {
108662306a36Sopenharmony_ci		/* Allows read access via its great-grandparent directory. */
108762306a36Sopenharmony_ci		{
108862306a36Sopenharmony_ci			.path = dir_s1d1,
108962306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
109062306a36Sopenharmony_ci		},
109162306a36Sopenharmony_ci		{},
109262306a36Sopenharmony_ci	};
109362306a36Sopenharmony_ci	const struct rule layer4_read_write[] = {
109462306a36Sopenharmony_ci		/*
109562306a36Sopenharmony_ci		 * Try to confuse the deny access by denying write (but not
109662306a36Sopenharmony_ci		 * read) access via its grandparent directory.
109762306a36Sopenharmony_ci		 */
109862306a36Sopenharmony_ci		{
109962306a36Sopenharmony_ci			.path = dir_s1d2,
110062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
110162306a36Sopenharmony_ci		},
110262306a36Sopenharmony_ci		{},
110362306a36Sopenharmony_ci	};
110462306a36Sopenharmony_ci	const struct rule layer5_read[] = {
110562306a36Sopenharmony_ci		/*
110662306a36Sopenharmony_ci		 * Try to override layer2's deny read access by explicitly
110762306a36Sopenharmony_ci		 * allowing read access via file1_s1d3's grandparent.
110862306a36Sopenharmony_ci		 */
110962306a36Sopenharmony_ci		{
111062306a36Sopenharmony_ci			.path = dir_s1d2,
111162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
111262306a36Sopenharmony_ci		},
111362306a36Sopenharmony_ci		{},
111462306a36Sopenharmony_ci	};
111562306a36Sopenharmony_ci	const struct rule layer6_execute[] = {
111662306a36Sopenharmony_ci		/*
111762306a36Sopenharmony_ci		 * Restricts an unrelated file hierarchy with a new access
111862306a36Sopenharmony_ci		 * (non-overlapping) type.
111962306a36Sopenharmony_ci		 */
112062306a36Sopenharmony_ci		{
112162306a36Sopenharmony_ci			.path = dir_s2d1,
112262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_EXECUTE,
112362306a36Sopenharmony_ci		},
112462306a36Sopenharmony_ci		{},
112562306a36Sopenharmony_ci	};
112662306a36Sopenharmony_ci	const struct rule layer7_read_write[] = {
112762306a36Sopenharmony_ci		/*
112862306a36Sopenharmony_ci		 * Finally, denies read access to file1_s1d3 via its
112962306a36Sopenharmony_ci		 * grandparent.
113062306a36Sopenharmony_ci		 */
113162306a36Sopenharmony_ci		{
113262306a36Sopenharmony_ci			.path = dir_s1d2,
113362306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
113462306a36Sopenharmony_ci		},
113562306a36Sopenharmony_ci		{},
113662306a36Sopenharmony_ci	};
113762306a36Sopenharmony_ci	int ruleset_fd;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
114062306a36Sopenharmony_ci				    layer1_read);
114162306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
114262306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
114362306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	/* Checks that read access is granted for file1_s1d3 with layer 1. */
114662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
114762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
114862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata,
115162306a36Sopenharmony_ci				    LANDLOCK_ACCESS_FS_READ_FILE |
115262306a36Sopenharmony_ci					    LANDLOCK_ACCESS_FS_WRITE_FILE,
115362306a36Sopenharmony_ci				    layer2_read_write);
115462306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
115562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
115662306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	/* Checks that previous access rights are unchanged with layer 2. */
115962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
116062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
116162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
116462306a36Sopenharmony_ci				    layer3_read);
116562306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
116662306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
116762306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	/* Checks that previous access rights are unchanged with layer 3. */
117062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
117162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
117262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/* This time, denies write access for the file hierarchy. */
117562306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata,
117662306a36Sopenharmony_ci				    LANDLOCK_ACCESS_FS_READ_FILE |
117762306a36Sopenharmony_ci					    LANDLOCK_ACCESS_FS_WRITE_FILE,
117862306a36Sopenharmony_ci				    layer4_read_write);
117962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
118062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
118162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	/*
118462306a36Sopenharmony_ci	 * Checks that the only change with layer 4 is that write access is
118562306a36Sopenharmony_ci	 * denied.
118662306a36Sopenharmony_ci	 */
118762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
118862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
118962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
119062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
119362306a36Sopenharmony_ci				    layer5_read);
119462306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
119562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
119662306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	/* Checks that previous access rights are unchanged with layer 5. */
119962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
120062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
120162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
120262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE,
120562306a36Sopenharmony_ci				    layer6_execute);
120662306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
120762306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
120862306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	/* Checks that previous access rights are unchanged with layer 6. */
121162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
121262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
121362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
121462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata,
121762306a36Sopenharmony_ci				    LANDLOCK_ACCESS_FS_READ_FILE |
121862306a36Sopenharmony_ci					    LANDLOCK_ACCESS_FS_WRITE_FILE,
121962306a36Sopenharmony_ci				    layer7_read_write);
122062306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
122162306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
122262306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	/* Checks read access is now denied with layer 7. */
122562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
122662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
122762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
122862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ciTEST_F_FORK(layout1, inherit_subset)
123262306a36Sopenharmony_ci{
123362306a36Sopenharmony_ci	const struct rule rules[] = {
123462306a36Sopenharmony_ci		{
123562306a36Sopenharmony_ci			.path = dir_s1d2,
123662306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
123762306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_READ_DIR,
123862306a36Sopenharmony_ci		},
123962306a36Sopenharmony_ci		{},
124062306a36Sopenharmony_ci	};
124162306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
124462306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
124762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	/* Write access is forbidden. */
125062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
125162306a36Sopenharmony_ci	/* Readdir access is allowed. */
125262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* Write access is forbidden. */
125562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
125662306a36Sopenharmony_ci	/* Readdir access is allowed. */
125762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	/*
126062306a36Sopenharmony_ci	 * Tests shared rule extension: the following rules should not grant
126162306a36Sopenharmony_ci	 * any new access, only remove some.  Once enforced, these rules are
126262306a36Sopenharmony_ci	 * ANDed with the previous ones.
126362306a36Sopenharmony_ci	 */
126462306a36Sopenharmony_ci	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
126562306a36Sopenharmony_ci			 dir_s1d2);
126662306a36Sopenharmony_ci	/*
126762306a36Sopenharmony_ci	 * According to ruleset_fd, dir_s1d2 should now have the
126862306a36Sopenharmony_ci	 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE
126962306a36Sopenharmony_ci	 * access rights (even if this directory is opened a second time).
127062306a36Sopenharmony_ci	 * However, when enforcing this updated ruleset, the ruleset tied to
127162306a36Sopenharmony_ci	 * the current process (i.e. its domain) will still only have the
127262306a36Sopenharmony_ci	 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and
127362306a36Sopenharmony_ci	 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but
127462306a36Sopenharmony_ci	 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would
127562306a36Sopenharmony_ci	 * be a privilege escalation.
127662306a36Sopenharmony_ci	 */
127762306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	/* Same tests and results as above. */
128062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
128162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/* It is still forbidden to write in file1_s1d2. */
128462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
128562306a36Sopenharmony_ci	/* Readdir access is still allowed. */
128662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	/* It is still forbidden to write in file1_s1d3. */
128962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
129062306a36Sopenharmony_ci	/* Readdir access is still allowed. */
129162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/*
129462306a36Sopenharmony_ci	 * Try to get more privileges by adding new access rights to the parent
129562306a36Sopenharmony_ci	 * directory: dir_s1d1.
129662306a36Sopenharmony_ci	 */
129762306a36Sopenharmony_ci	add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1);
129862306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	/* Same tests and results as above. */
130162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
130262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	/* It is still forbidden to write in file1_s1d2. */
130562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
130662306a36Sopenharmony_ci	/* Readdir access is still allowed. */
130762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	/* It is still forbidden to write in file1_s1d3. */
131062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
131162306a36Sopenharmony_ci	/* Readdir access is still allowed. */
131262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	/*
131562306a36Sopenharmony_ci	 * Now, dir_s1d3 get a new rule tied to it, only allowing
131662306a36Sopenharmony_ci	 * LANDLOCK_ACCESS_FS_WRITE_FILE.  The (kernel internal) difference is
131762306a36Sopenharmony_ci	 * that there was no rule tied to it before.
131862306a36Sopenharmony_ci	 */
131962306a36Sopenharmony_ci	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
132062306a36Sopenharmony_ci			 dir_s1d3);
132162306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
132262306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	/*
132562306a36Sopenharmony_ci	 * Same tests and results as above, except for open(dir_s1d3) which is
132662306a36Sopenharmony_ci	 * now denied because the new rule mask the rule previously inherited
132762306a36Sopenharmony_ci	 * from dir_s1d2.
132862306a36Sopenharmony_ci	 */
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/* Same tests and results as above. */
133162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
133262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	/* It is still forbidden to write in file1_s1d2. */
133562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
133662306a36Sopenharmony_ci	/* Readdir access is still allowed. */
133762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	/* It is still forbidden to write in file1_s1d3. */
134062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
134162306a36Sopenharmony_ci	/*
134262306a36Sopenharmony_ci	 * Readdir of dir_s1d3 is still allowed because of the OR policy inside
134362306a36Sopenharmony_ci	 * the same layer.
134462306a36Sopenharmony_ci	 */
134562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ciTEST_F_FORK(layout1, inherit_superset)
134962306a36Sopenharmony_ci{
135062306a36Sopenharmony_ci	const struct rule rules[] = {
135162306a36Sopenharmony_ci		{
135262306a36Sopenharmony_ci			.path = dir_s1d3,
135362306a36Sopenharmony_ci			.access = ACCESS_RO,
135462306a36Sopenharmony_ci		},
135562306a36Sopenharmony_ci		{},
135662306a36Sopenharmony_ci	};
135762306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
136062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	/* Readdir access is denied for dir_s1d2. */
136362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
136462306a36Sopenharmony_ci	/* Readdir access is allowed for dir_s1d3. */
136562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
136662306a36Sopenharmony_ci	/* File access is allowed for file1_s1d3. */
136762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	/* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */
137062306a36Sopenharmony_ci	add_path_beneath(_metadata, ruleset_fd,
137162306a36Sopenharmony_ci			 LANDLOCK_ACCESS_FS_READ_FILE |
137262306a36Sopenharmony_ci				 LANDLOCK_ACCESS_FS_READ_DIR,
137362306a36Sopenharmony_ci			 dir_s1d2);
137462306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
137562306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	/* Readdir access is still denied for dir_s1d2. */
137862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
137962306a36Sopenharmony_ci	/* Readdir access is still allowed for dir_s1d3. */
138062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
138162306a36Sopenharmony_ci	/* File access is still allowed for file1_s1d3. */
138262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ciTEST_F_FORK(layout0, max_layers)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	int i, err;
138862306a36Sopenharmony_ci	const struct rule rules[] = {
138962306a36Sopenharmony_ci		{
139062306a36Sopenharmony_ci			.path = TMP_DIR,
139162306a36Sopenharmony_ci			.access = ACCESS_RO,
139262306a36Sopenharmony_ci		},
139362306a36Sopenharmony_ci		{},
139462306a36Sopenharmony_ci	};
139562306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
139862306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
139962306a36Sopenharmony_ci		enforce_ruleset(_metadata, ruleset_fd);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
140262306a36Sopenharmony_ci		err = landlock_restrict_self(ruleset_fd, 0);
140362306a36Sopenharmony_ci		ASSERT_EQ(-1, err);
140462306a36Sopenharmony_ci		ASSERT_EQ(E2BIG, errno);
140562306a36Sopenharmony_ci	}
140662306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
140762306a36Sopenharmony_ci}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ciTEST_F_FORK(layout1, empty_or_same_ruleset)
141062306a36Sopenharmony_ci{
141162306a36Sopenharmony_ci	struct landlock_ruleset_attr ruleset_attr = {};
141262306a36Sopenharmony_ci	int ruleset_fd;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	/* Tests empty handled_access_fs. */
141562306a36Sopenharmony_ci	ruleset_fd =
141662306a36Sopenharmony_ci		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
141762306a36Sopenharmony_ci	ASSERT_LE(-1, ruleset_fd);
141862306a36Sopenharmony_ci	ASSERT_EQ(ENOMSG, errno);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	/* Enforces policy which deny read access to all files. */
142162306a36Sopenharmony_ci	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE;
142262306a36Sopenharmony_ci	ruleset_fd =
142362306a36Sopenharmony_ci		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
142462306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
142562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
142662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
142762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	/* Nests a policy which deny read access to all directories. */
143062306a36Sopenharmony_ci	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR;
143162306a36Sopenharmony_ci	ruleset_fd =
143262306a36Sopenharmony_ci		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
143362306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
143462306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
143562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
143662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	/* Enforces a second time with the same ruleset. */
143962306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
144062306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
144162306a36Sopenharmony_ci}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_on_mountpoint)
144462306a36Sopenharmony_ci{
144562306a36Sopenharmony_ci	const struct rule rules[] = {
144662306a36Sopenharmony_ci		{
144762306a36Sopenharmony_ci			.path = dir_s1d1,
144862306a36Sopenharmony_ci			.access = ACCESS_RO,
144962306a36Sopenharmony_ci		},
145062306a36Sopenharmony_ci		{
145162306a36Sopenharmony_ci			/* dir_s3d2 is a mount point. */
145262306a36Sopenharmony_ci			.path = dir_s3d2,
145362306a36Sopenharmony_ci			.access = ACCESS_RO,
145462306a36Sopenharmony_ci		},
145562306a36Sopenharmony_ci		{},
145662306a36Sopenharmony_ci	};
145762306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
146062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
146162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY));
146862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
146962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
147062306a36Sopenharmony_ci}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_over_mountpoint)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	const struct rule rules[] = {
147562306a36Sopenharmony_ci		{
147662306a36Sopenharmony_ci			.path = dir_s1d1,
147762306a36Sopenharmony_ci			.access = ACCESS_RO,
147862306a36Sopenharmony_ci		},
147962306a36Sopenharmony_ci		{
148062306a36Sopenharmony_ci			/* dir_s3d2 is a mount point. */
148162306a36Sopenharmony_ci			.path = dir_s3d1,
148262306a36Sopenharmony_ci			.access = ACCESS_RO,
148362306a36Sopenharmony_ci		},
148462306a36Sopenharmony_ci		{},
148562306a36Sopenharmony_ci	};
148662306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
148962306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
149062306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
149762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
149862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci/*
150262306a36Sopenharmony_ci * This test verifies that we can apply a landlock rule on the root directory
150362306a36Sopenharmony_ci * (which might require special handling).
150462306a36Sopenharmony_ci */
150562306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_over_root_allow_then_deny)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	struct rule rules[] = {
150862306a36Sopenharmony_ci		{
150962306a36Sopenharmony_ci			.path = "/",
151062306a36Sopenharmony_ci			.access = ACCESS_RO,
151162306a36Sopenharmony_ci		},
151262306a36Sopenharmony_ci		{},
151362306a36Sopenharmony_ci	};
151462306a36Sopenharmony_ci	int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
151762306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
151862306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	/* Checks allowed access. */
152162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open("/", O_RDONLY));
152262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE;
152562306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
152662306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
152762306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
152862306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	/* Checks denied access (on a directory). */
153162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
153262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
153362306a36Sopenharmony_ci}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_over_root_deny)
153662306a36Sopenharmony_ci{
153762306a36Sopenharmony_ci	const struct rule rules[] = {
153862306a36Sopenharmony_ci		{
153962306a36Sopenharmony_ci			.path = "/",
154062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
154162306a36Sopenharmony_ci		},
154262306a36Sopenharmony_ci		{},
154362306a36Sopenharmony_ci	};
154462306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
154762306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
154862306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	/* Checks denied access (on a directory). */
155162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
155262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
155362306a36Sopenharmony_ci}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_inside_mount_ns)
155662306a36Sopenharmony_ci{
155762306a36Sopenharmony_ci	const struct rule rules[] = {
155862306a36Sopenharmony_ci		{
155962306a36Sopenharmony_ci			.path = "s3d3",
156062306a36Sopenharmony_ci			.access = ACCESS_RO,
156162306a36Sopenharmony_ci		},
156262306a36Sopenharmony_ci		{},
156362306a36Sopenharmony_ci	};
156462306a36Sopenharmony_ci	int ruleset_fd;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
156762306a36Sopenharmony_ci	ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3))
156862306a36Sopenharmony_ci	{
156962306a36Sopenharmony_ci		TH_LOG("Failed to pivot root: %s", strerror(errno));
157062306a36Sopenharmony_ci	};
157162306a36Sopenharmony_ci	ASSERT_EQ(0, chdir("/"));
157262306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
157562306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
157662306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
157762306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open("s3d3", O_RDONLY));
158062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
158162306a36Sopenharmony_ci}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ciTEST_F_FORK(layout1, mount_and_pivot)
158462306a36Sopenharmony_ci{
158562306a36Sopenharmony_ci	const struct rule rules[] = {
158662306a36Sopenharmony_ci		{
158762306a36Sopenharmony_ci			.path = dir_s3d2,
158862306a36Sopenharmony_ci			.access = ACCESS_RO,
158962306a36Sopenharmony_ci		},
159062306a36Sopenharmony_ci		{},
159162306a36Sopenharmony_ci	};
159262306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
159562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
159662306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
159962306a36Sopenharmony_ci	ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
160062306a36Sopenharmony_ci	ASSERT_EQ(EPERM, errno);
160162306a36Sopenharmony_ci	ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));
160262306a36Sopenharmony_ci	ASSERT_EQ(EPERM, errno);
160362306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ciTEST_F_FORK(layout1, move_mount)
160762306a36Sopenharmony_ci{
160862306a36Sopenharmony_ci	const struct rule rules[] = {
160962306a36Sopenharmony_ci		{
161062306a36Sopenharmony_ci			.path = dir_s3d2,
161162306a36Sopenharmony_ci			.access = ACCESS_RO,
161262306a36Sopenharmony_ci		},
161362306a36Sopenharmony_ci		{},
161462306a36Sopenharmony_ci	};
161562306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
162062306a36Sopenharmony_ci	ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
162162306a36Sopenharmony_ci			     dir_s1d2, 0))
162262306a36Sopenharmony_ci	{
162362306a36Sopenharmony_ci		TH_LOG("Failed to move mount: %s", strerror(errno));
162462306a36Sopenharmony_ci	}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,
162762306a36Sopenharmony_ci			     dir_s3d2, 0));
162862306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
163162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
163462306a36Sopenharmony_ci	ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
163562306a36Sopenharmony_ci			      dir_s1d2, 0));
163662306a36Sopenharmony_ci	ASSERT_EQ(EPERM, errno);
163762306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
163862306a36Sopenharmony_ci}
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ciTEST_F_FORK(layout1, release_inodes)
164162306a36Sopenharmony_ci{
164262306a36Sopenharmony_ci	const struct rule rules[] = {
164362306a36Sopenharmony_ci		{
164462306a36Sopenharmony_ci			.path = dir_s1d1,
164562306a36Sopenharmony_ci			.access = ACCESS_RO,
164662306a36Sopenharmony_ci		},
164762306a36Sopenharmony_ci		{
164862306a36Sopenharmony_ci			.path = dir_s3d2,
164962306a36Sopenharmony_ci			.access = ACCESS_RO,
165062306a36Sopenharmony_ci		},
165162306a36Sopenharmony_ci		{
165262306a36Sopenharmony_ci			.path = dir_s3d3,
165362306a36Sopenharmony_ci			.access = ACCESS_RO,
165462306a36Sopenharmony_ci		},
165562306a36Sopenharmony_ci		{},
165662306a36Sopenharmony_ci	};
165762306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
166062306a36Sopenharmony_ci	/* Unmount a file hierarchy while it is being used by a ruleset. */
166162306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
166262306a36Sopenharmony_ci	ASSERT_EQ(0, umount(dir_s3d2));
166362306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
166662306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
166962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));
167062306a36Sopenharmony_ci	/* This dir_s3d3 would not be allowed and does not exist anyway. */
167162306a36Sopenharmony_ci	ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY));
167262306a36Sopenharmony_ci}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_cienum relative_access {
167562306a36Sopenharmony_ci	REL_OPEN,
167662306a36Sopenharmony_ci	REL_CHDIR,
167762306a36Sopenharmony_ci	REL_CHROOT_ONLY,
167862306a36Sopenharmony_ci	REL_CHROOT_CHDIR,
167962306a36Sopenharmony_ci};
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_cistatic void test_relative_path(struct __test_metadata *const _metadata,
168262306a36Sopenharmony_ci			       const enum relative_access rel)
168362306a36Sopenharmony_ci{
168462306a36Sopenharmony_ci	/*
168562306a36Sopenharmony_ci	 * Common layer to check that chroot doesn't ignore it (i.e. a chroot
168662306a36Sopenharmony_ci	 * is not a disconnected root directory).
168762306a36Sopenharmony_ci	 */
168862306a36Sopenharmony_ci	const struct rule layer1_base[] = {
168962306a36Sopenharmony_ci		{
169062306a36Sopenharmony_ci			.path = TMP_DIR,
169162306a36Sopenharmony_ci			.access = ACCESS_RO,
169262306a36Sopenharmony_ci		},
169362306a36Sopenharmony_ci		{},
169462306a36Sopenharmony_ci	};
169562306a36Sopenharmony_ci	const struct rule layer2_subs[] = {
169662306a36Sopenharmony_ci		{
169762306a36Sopenharmony_ci			.path = dir_s1d2,
169862306a36Sopenharmony_ci			.access = ACCESS_RO,
169962306a36Sopenharmony_ci		},
170062306a36Sopenharmony_ci		{
170162306a36Sopenharmony_ci			.path = dir_s2d2,
170262306a36Sopenharmony_ci			.access = ACCESS_RO,
170362306a36Sopenharmony_ci		},
170462306a36Sopenharmony_ci		{},
170562306a36Sopenharmony_ci	};
170662306a36Sopenharmony_ci	int dirfd, ruleset_fd;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
170962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
171062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
171162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs);
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
171662306a36Sopenharmony_ci	switch (rel) {
171762306a36Sopenharmony_ci	case REL_OPEN:
171862306a36Sopenharmony_ci	case REL_CHDIR:
171962306a36Sopenharmony_ci		break;
172062306a36Sopenharmony_ci	case REL_CHROOT_ONLY:
172162306a36Sopenharmony_ci		ASSERT_EQ(0, chdir(dir_s2d2));
172262306a36Sopenharmony_ci		break;
172362306a36Sopenharmony_ci	case REL_CHROOT_CHDIR:
172462306a36Sopenharmony_ci		ASSERT_EQ(0, chdir(dir_s1d2));
172562306a36Sopenharmony_ci		break;
172662306a36Sopenharmony_ci	default:
172762306a36Sopenharmony_ci		ASSERT_TRUE(false);
172862306a36Sopenharmony_ci		return;
172962306a36Sopenharmony_ci	}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_CHROOT);
173262306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	switch (rel) {
173562306a36Sopenharmony_ci	case REL_OPEN:
173662306a36Sopenharmony_ci		dirfd = open(dir_s1d2, O_DIRECTORY);
173762306a36Sopenharmony_ci		ASSERT_LE(0, dirfd);
173862306a36Sopenharmony_ci		break;
173962306a36Sopenharmony_ci	case REL_CHDIR:
174062306a36Sopenharmony_ci		ASSERT_EQ(0, chdir(dir_s1d2));
174162306a36Sopenharmony_ci		dirfd = AT_FDCWD;
174262306a36Sopenharmony_ci		break;
174362306a36Sopenharmony_ci	case REL_CHROOT_ONLY:
174462306a36Sopenharmony_ci		/* Do chroot into dir_s1d2 (relative to dir_s2d2). */
174562306a36Sopenharmony_ci		ASSERT_EQ(0, chroot("../../s1d1/s1d2"))
174662306a36Sopenharmony_ci		{
174762306a36Sopenharmony_ci			TH_LOG("Failed to chroot: %s", strerror(errno));
174862306a36Sopenharmony_ci		}
174962306a36Sopenharmony_ci		dirfd = AT_FDCWD;
175062306a36Sopenharmony_ci		break;
175162306a36Sopenharmony_ci	case REL_CHROOT_CHDIR:
175262306a36Sopenharmony_ci		/* Do chroot into dir_s1d2. */
175362306a36Sopenharmony_ci		ASSERT_EQ(0, chroot("."))
175462306a36Sopenharmony_ci		{
175562306a36Sopenharmony_ci			TH_LOG("Failed to chroot: %s", strerror(errno));
175662306a36Sopenharmony_ci		}
175762306a36Sopenharmony_ci		dirfd = AT_FDCWD;
175862306a36Sopenharmony_ci		break;
175962306a36Sopenharmony_ci	}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES,
176262306a36Sopenharmony_ci		  test_open_rel(dirfd, "..", O_RDONLY));
176362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY));
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	if (rel == REL_CHROOT_ONLY) {
176662306a36Sopenharmony_ci		/* The current directory is dir_s2d2. */
176762306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY));
176862306a36Sopenharmony_ci	} else {
176962306a36Sopenharmony_ci		/* The current directory is dir_s1d2. */
177062306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY));
177162306a36Sopenharmony_ci	}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) {
177462306a36Sopenharmony_ci		/* Checks the root dir_s1d2. */
177562306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY));
177662306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY));
177762306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY));
177862306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY));
177962306a36Sopenharmony_ci	}
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	if (rel != REL_CHROOT_CHDIR) {
178262306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY));
178362306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY));
178462306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3",
178562306a36Sopenharmony_ci					   O_RDONLY));
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY));
178862306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY));
178962306a36Sopenharmony_ci		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3",
179062306a36Sopenharmony_ci					   O_RDONLY));
179162306a36Sopenharmony_ci	}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	if (rel == REL_OPEN)
179462306a36Sopenharmony_ci		ASSERT_EQ(0, close(dirfd));
179562306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
179662306a36Sopenharmony_ci}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ciTEST_F_FORK(layout1, relative_open)
179962306a36Sopenharmony_ci{
180062306a36Sopenharmony_ci	test_relative_path(_metadata, REL_OPEN);
180162306a36Sopenharmony_ci}
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ciTEST_F_FORK(layout1, relative_chdir)
180462306a36Sopenharmony_ci{
180562306a36Sopenharmony_ci	test_relative_path(_metadata, REL_CHDIR);
180662306a36Sopenharmony_ci}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ciTEST_F_FORK(layout1, relative_chroot_only)
180962306a36Sopenharmony_ci{
181062306a36Sopenharmony_ci	test_relative_path(_metadata, REL_CHROOT_ONLY);
181162306a36Sopenharmony_ci}
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ciTEST_F_FORK(layout1, relative_chroot_chdir)
181462306a36Sopenharmony_ci{
181562306a36Sopenharmony_ci	test_relative_path(_metadata, REL_CHROOT_CHDIR);
181662306a36Sopenharmony_ci}
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_cistatic void copy_binary(struct __test_metadata *const _metadata,
181962306a36Sopenharmony_ci			const char *const dst_path)
182062306a36Sopenharmony_ci{
182162306a36Sopenharmony_ci	int dst_fd, src_fd;
182262306a36Sopenharmony_ci	struct stat statbuf;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC);
182562306a36Sopenharmony_ci	ASSERT_LE(0, dst_fd)
182662306a36Sopenharmony_ci	{
182762306a36Sopenharmony_ci		TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno));
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci	src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC);
183062306a36Sopenharmony_ci	ASSERT_LE(0, src_fd)
183162306a36Sopenharmony_ci	{
183262306a36Sopenharmony_ci		TH_LOG("Failed to open \"" BINARY_PATH "\": %s",
183362306a36Sopenharmony_ci		       strerror(errno));
183462306a36Sopenharmony_ci	}
183562306a36Sopenharmony_ci	ASSERT_EQ(0, fstat(src_fd, &statbuf));
183662306a36Sopenharmony_ci	ASSERT_EQ(statbuf.st_size,
183762306a36Sopenharmony_ci		  sendfile(dst_fd, src_fd, 0, statbuf.st_size));
183862306a36Sopenharmony_ci	ASSERT_EQ(0, close(src_fd));
183962306a36Sopenharmony_ci	ASSERT_EQ(0, close(dst_fd));
184062306a36Sopenharmony_ci}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_cistatic void test_execute(struct __test_metadata *const _metadata, const int err,
184362306a36Sopenharmony_ci			 const char *const path)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	int status;
184662306a36Sopenharmony_ci	char *const argv[] = { (char *)path, NULL };
184762306a36Sopenharmony_ci	const pid_t child = fork();
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	ASSERT_LE(0, child);
185062306a36Sopenharmony_ci	if (child == 0) {
185162306a36Sopenharmony_ci		ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL))
185262306a36Sopenharmony_ci		{
185362306a36Sopenharmony_ci			TH_LOG("Failed to execute \"%s\": %s", path,
185462306a36Sopenharmony_ci			       strerror(errno));
185562306a36Sopenharmony_ci		};
185662306a36Sopenharmony_ci		ASSERT_EQ(err, errno);
185762306a36Sopenharmony_ci		_exit(_metadata->passed ? 2 : 1);
185862306a36Sopenharmony_ci		return;
185962306a36Sopenharmony_ci	}
186062306a36Sopenharmony_ci	ASSERT_EQ(child, waitpid(child, &status, 0));
186162306a36Sopenharmony_ci	ASSERT_EQ(1, WIFEXITED(status));
186262306a36Sopenharmony_ci	ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status))
186362306a36Sopenharmony_ci	{
186462306a36Sopenharmony_ci		TH_LOG("Unexpected return code for \"%s\": %s", path,
186562306a36Sopenharmony_ci		       strerror(errno));
186662306a36Sopenharmony_ci	};
186762306a36Sopenharmony_ci}
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ciTEST_F_FORK(layout1, execute)
187062306a36Sopenharmony_ci{
187162306a36Sopenharmony_ci	const struct rule rules[] = {
187262306a36Sopenharmony_ci		{
187362306a36Sopenharmony_ci			.path = dir_s1d2,
187462306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_EXECUTE,
187562306a36Sopenharmony_ci		},
187662306a36Sopenharmony_ci		{},
187762306a36Sopenharmony_ci	};
187862306a36Sopenharmony_ci	const int ruleset_fd =
187962306a36Sopenharmony_ci		create_ruleset(_metadata, rules[0].access, rules);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
188262306a36Sopenharmony_ci	copy_binary(_metadata, file1_s1d1);
188362306a36Sopenharmony_ci	copy_binary(_metadata, file1_s1d2);
188462306a36Sopenharmony_ci	copy_binary(_metadata, file1_s1d3);
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
188762306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
189062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
189162306a36Sopenharmony_ci	test_execute(_metadata, EACCES, file1_s1d1);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
189462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
189562306a36Sopenharmony_ci	test_execute(_metadata, 0, file1_s1d2);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
189862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
189962306a36Sopenharmony_ci	test_execute(_metadata, 0, file1_s1d3);
190062306a36Sopenharmony_ci}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ciTEST_F_FORK(layout1, link)
190362306a36Sopenharmony_ci{
190462306a36Sopenharmony_ci	const struct rule layer1[] = {
190562306a36Sopenharmony_ci		{
190662306a36Sopenharmony_ci			.path = dir_s1d2,
190762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
190862306a36Sopenharmony_ci		},
190962306a36Sopenharmony_ci		{},
191062306a36Sopenharmony_ci	};
191162306a36Sopenharmony_ci	const struct rule layer2[] = {
191262306a36Sopenharmony_ci		{
191362306a36Sopenharmony_ci			.path = dir_s1d3,
191462306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
191562306a36Sopenharmony_ci		},
191662306a36Sopenharmony_ci		{},
191762306a36Sopenharmony_ci	};
191862306a36Sopenharmony_ci	int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d1));
192362306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
192462306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
192762306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
193062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	/* Denies linking because of reparenting. */
193362306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
193462306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
193562306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
193662306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
193762306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
193862306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
194162306a36Sopenharmony_ci	ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	/* Prepares for next unlinks. */
194462306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d2));
194562306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d3));
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
194862306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
194962306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
195062306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	/* Checks that linkind doesn't require the ability to delete a file. */
195362306a36Sopenharmony_ci	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
195462306a36Sopenharmony_ci	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
195562306a36Sopenharmony_ci}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_cistatic int test_rename(const char *const oldpath, const char *const newpath)
195862306a36Sopenharmony_ci{
195962306a36Sopenharmony_ci	if (rename(oldpath, newpath))
196062306a36Sopenharmony_ci		return errno;
196162306a36Sopenharmony_ci	return 0;
196262306a36Sopenharmony_ci}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_cistatic int test_exchange(const char *const oldpath, const char *const newpath)
196562306a36Sopenharmony_ci{
196662306a36Sopenharmony_ci	if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE))
196762306a36Sopenharmony_ci		return errno;
196862306a36Sopenharmony_ci	return 0;
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ciTEST_F_FORK(layout1, rename_file)
197262306a36Sopenharmony_ci{
197362306a36Sopenharmony_ci	const struct rule rules[] = {
197462306a36Sopenharmony_ci		{
197562306a36Sopenharmony_ci			.path = dir_s1d3,
197662306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
197762306a36Sopenharmony_ci		},
197862306a36Sopenharmony_ci		{
197962306a36Sopenharmony_ci			.path = dir_s2d2,
198062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
198162306a36Sopenharmony_ci		},
198262306a36Sopenharmony_ci		{},
198362306a36Sopenharmony_ci	};
198462306a36Sopenharmony_ci	const int ruleset_fd =
198562306a36Sopenharmony_ci		create_ruleset(_metadata, rules[0].access, rules);
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
199262306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	/*
199562306a36Sopenharmony_ci	 * Tries to replace a file, from a directory that allows file removal,
199662306a36Sopenharmony_ci	 * but to a different directory (which also allows file removal).
199762306a36Sopenharmony_ci	 */
199862306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3));
199962306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
200062306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3,
200162306a36Sopenharmony_ci				RENAME_EXCHANGE));
200262306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
200362306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
200462306a36Sopenharmony_ci				RENAME_EXCHANGE));
200562306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	/*
200862306a36Sopenharmony_ci	 * Tries to replace a file, from a directory that denies file removal,
200962306a36Sopenharmony_ci	 * to a different directory (which allows file removal).
201062306a36Sopenharmony_ci	 */
201162306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
201262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
201362306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3,
201462306a36Sopenharmony_ci				RENAME_EXCHANGE));
201562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
201662306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3,
201762306a36Sopenharmony_ci				RENAME_EXCHANGE));
201862306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	/* Exchanges files and directories that partially allow removal. */
202162306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1,
202262306a36Sopenharmony_ci				RENAME_EXCHANGE));
202362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
202462306a36Sopenharmony_ci	/* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */
202562306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1));
202662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
202762306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2,
202862306a36Sopenharmony_ci				RENAME_EXCHANGE));
202962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
203062306a36Sopenharmony_ci	/* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */
203162306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
203262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci	/* Renames files with different parents. */
203562306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
203662306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
203762306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
203862306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
203962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	/* Exchanges and renames files with same parent. */
204262306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3,
204362306a36Sopenharmony_ci			       RENAME_EXCHANGE));
204462306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3));
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	/* Exchanges files and directories with same parent, twice. */
204762306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
204862306a36Sopenharmony_ci			       RENAME_EXCHANGE));
204962306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
205062306a36Sopenharmony_ci			       RENAME_EXCHANGE));
205162306a36Sopenharmony_ci}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ciTEST_F_FORK(layout1, rename_dir)
205462306a36Sopenharmony_ci{
205562306a36Sopenharmony_ci	const struct rule rules[] = {
205662306a36Sopenharmony_ci		{
205762306a36Sopenharmony_ci			.path = dir_s1d2,
205862306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
205962306a36Sopenharmony_ci		},
206062306a36Sopenharmony_ci		{
206162306a36Sopenharmony_ci			.path = dir_s2d1,
206262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
206362306a36Sopenharmony_ci		},
206462306a36Sopenharmony_ci		{},
206562306a36Sopenharmony_ci	};
206662306a36Sopenharmony_ci	const int ruleset_fd =
206762306a36Sopenharmony_ci		create_ruleset(_metadata, rules[0].access, rules);
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	/* Empties dir_s1d3 to allow renaming. */
207262306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
207362306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d3));
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
207662306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	/* Exchanges and renames directory to a different parent. */
207962306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
208062306a36Sopenharmony_ci				RENAME_EXCHANGE));
208162306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
208262306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3));
208362306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
208462306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
208562306a36Sopenharmony_ci				RENAME_EXCHANGE));
208662306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	/*
208962306a36Sopenharmony_ci	 * Exchanges directory to the same parent, which doesn't allow
209062306a36Sopenharmony_ci	 * directory removal.
209162306a36Sopenharmony_ci	 */
209262306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1,
209362306a36Sopenharmony_ci				RENAME_EXCHANGE));
209462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
209562306a36Sopenharmony_ci	/* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */
209662306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1));
209762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
209862306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2,
209962306a36Sopenharmony_ci				RENAME_EXCHANGE));
210062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
210162306a36Sopenharmony_ci	/* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */
210262306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
210362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	/*
210662306a36Sopenharmony_ci	 * Exchanges and renames directory to the same parent, which allows
210762306a36Sopenharmony_ci	 * directory removal.
210862306a36Sopenharmony_ci	 */
210962306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2,
211062306a36Sopenharmony_ci			       RENAME_EXCHANGE));
211162306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(dir_s1d3));
211262306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
211362306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3));
211462306a36Sopenharmony_ci	ASSERT_EQ(0, rmdir(dir_s1d3));
211562306a36Sopenharmony_ci}
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_refer)
211862306a36Sopenharmony_ci{
211962306a36Sopenharmony_ci	const struct rule layer1[] = {
212062306a36Sopenharmony_ci		{
212162306a36Sopenharmony_ci			.path = dir_s1d2,
212262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
212362306a36Sopenharmony_ci		},
212462306a36Sopenharmony_ci		{
212562306a36Sopenharmony_ci			.path = dir_s2d2,
212662306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
212762306a36Sopenharmony_ci		},
212862306a36Sopenharmony_ci		{},
212962306a36Sopenharmony_ci	};
213062306a36Sopenharmony_ci	int ruleset_fd =
213162306a36Sopenharmony_ci		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
213462306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
213562306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1));
213862306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
213962306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2));
214062306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
214162306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));
214262306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1));
214562306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
214662306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2));
214762306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
214862306a36Sopenharmony_ci	/*
214962306a36Sopenharmony_ci	 * Moving should only be allowed when the source and the destination
215062306a36Sopenharmony_ci	 * parent directory have REFER.
215162306a36Sopenharmony_ci	 */
215262306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3));
215362306a36Sopenharmony_ci	ASSERT_EQ(ENOTEMPTY, errno);
215462306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s2d3));
215562306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s2d3));
215662306a36Sopenharmony_ci	ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3));
215762306a36Sopenharmony_ci}
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci/* Checks renames beneath dir_s1d1. */
216062306a36Sopenharmony_cistatic void refer_denied_by_default(struct __test_metadata *const _metadata,
216162306a36Sopenharmony_ci				    const struct rule layer1[],
216262306a36Sopenharmony_ci				    const int layer1_err,
216362306a36Sopenharmony_ci				    const struct rule layer2[])
216462306a36Sopenharmony_ci{
216562306a36Sopenharmony_ci	int ruleset_fd;
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
217062306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
217162306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
217262306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	/*
217562306a36Sopenharmony_ci	 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to
217662306a36Sopenharmony_ci	 * layer1_err), then it allows some different-parent renames and links.
217762306a36Sopenharmony_ci	 */
217862306a36Sopenharmony_ci	ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2));
217962306a36Sopenharmony_ci	if (layer1_err == 0)
218062306a36Sopenharmony_ci		ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1));
218162306a36Sopenharmony_ci	ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2));
218262306a36Sopenharmony_ci	ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1));
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
218562306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
218662306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
218762306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	/*
219062306a36Sopenharmony_ci	 * Now, either the first or the second layer does not handle
219162306a36Sopenharmony_ci	 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent
219262306a36Sopenharmony_ci	 * renames and links are denied, thus making the layer handling
219362306a36Sopenharmony_ci	 * LANDLOCK_ACCESS_FS_REFER null and void.
219462306a36Sopenharmony_ci	 */
219562306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2));
219662306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2));
219762306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1));
219862306a36Sopenharmony_ci}
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ciconst struct rule layer_dir_s1d1_refer[] = {
220162306a36Sopenharmony_ci	{
220262306a36Sopenharmony_ci		.path = dir_s1d1,
220362306a36Sopenharmony_ci		.access = LANDLOCK_ACCESS_FS_REFER,
220462306a36Sopenharmony_ci	},
220562306a36Sopenharmony_ci	{},
220662306a36Sopenharmony_ci};
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ciconst struct rule layer_dir_s1d1_execute[] = {
220962306a36Sopenharmony_ci	{
221062306a36Sopenharmony_ci		/* Matches a parent directory. */
221162306a36Sopenharmony_ci		.path = dir_s1d1,
221262306a36Sopenharmony_ci		.access = LANDLOCK_ACCESS_FS_EXECUTE,
221362306a36Sopenharmony_ci	},
221462306a36Sopenharmony_ci	{},
221562306a36Sopenharmony_ci};
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ciconst struct rule layer_dir_s2d1_execute[] = {
221862306a36Sopenharmony_ci	{
221962306a36Sopenharmony_ci		/* Does not match a parent directory. */
222062306a36Sopenharmony_ci		.path = dir_s2d1,
222162306a36Sopenharmony_ci		.access = LANDLOCK_ACCESS_FS_EXECUTE,
222262306a36Sopenharmony_ci	},
222362306a36Sopenharmony_ci	{},
222462306a36Sopenharmony_ci};
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci/*
222762306a36Sopenharmony_ci * Tests precedence over renames: denied by default for different parent
222862306a36Sopenharmony_ci * directories, *with* a rule matching a parent directory, but not directly
222962306a36Sopenharmony_ci * denying access (with MAKE_REG nor REMOVE).
223062306a36Sopenharmony_ci */
223162306a36Sopenharmony_ciTEST_F_FORK(layout1, refer_denied_by_default1)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
223462306a36Sopenharmony_ci				layer_dir_s1d1_execute);
223562306a36Sopenharmony_ci}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci/*
223862306a36Sopenharmony_ci * Same test but this time turning around the ABI version order: the first
223962306a36Sopenharmony_ci * layer does not handle LANDLOCK_ACCESS_FS_REFER.
224062306a36Sopenharmony_ci */
224162306a36Sopenharmony_ciTEST_F_FORK(layout1, refer_denied_by_default2)
224262306a36Sopenharmony_ci{
224362306a36Sopenharmony_ci	refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV,
224462306a36Sopenharmony_ci				layer_dir_s1d1_refer);
224562306a36Sopenharmony_ci}
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci/*
224862306a36Sopenharmony_ci * Tests precedence over renames: denied by default for different parent
224962306a36Sopenharmony_ci * directories, *without* a rule matching a parent directory, but not directly
225062306a36Sopenharmony_ci * denying access (with MAKE_REG nor REMOVE).
225162306a36Sopenharmony_ci */
225262306a36Sopenharmony_ciTEST_F_FORK(layout1, refer_denied_by_default3)
225362306a36Sopenharmony_ci{
225462306a36Sopenharmony_ci	refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
225562306a36Sopenharmony_ci				layer_dir_s2d1_execute);
225662306a36Sopenharmony_ci}
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci/*
225962306a36Sopenharmony_ci * Same test but this time turning around the ABI version order: the first
226062306a36Sopenharmony_ci * layer does not handle LANDLOCK_ACCESS_FS_REFER.
226162306a36Sopenharmony_ci */
226262306a36Sopenharmony_ciTEST_F_FORK(layout1, refer_denied_by_default4)
226362306a36Sopenharmony_ci{
226462306a36Sopenharmony_ci	refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV,
226562306a36Sopenharmony_ci				layer_dir_s1d1_refer);
226662306a36Sopenharmony_ci}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_link)
226962306a36Sopenharmony_ci{
227062306a36Sopenharmony_ci	const struct rule layer1[] = {
227162306a36Sopenharmony_ci		{
227262306a36Sopenharmony_ci			.path = dir_s1d2,
227362306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
227462306a36Sopenharmony_ci		},
227562306a36Sopenharmony_ci		{
227662306a36Sopenharmony_ci			.path = dir_s1d3,
227762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
227862306a36Sopenharmony_ci		},
227962306a36Sopenharmony_ci		{
228062306a36Sopenharmony_ci			.path = dir_s2d2,
228162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
228262306a36Sopenharmony_ci		},
228362306a36Sopenharmony_ci		{
228462306a36Sopenharmony_ci			.path = dir_s2d3,
228562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
228662306a36Sopenharmony_ci		},
228762306a36Sopenharmony_ci		{},
228862306a36Sopenharmony_ci	};
228962306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(
229062306a36Sopenharmony_ci		_metadata,
229162306a36Sopenharmony_ci		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
229462306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
229562306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d1));
229862306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
229962306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	/* Denies linking because of missing MAKE_REG. */
230262306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
230362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
230462306a36Sopenharmony_ci	/* Denies linking because of missing source and destination REFER. */
230562306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
230662306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
230762306a36Sopenharmony_ci	/* Denies linking because of missing source REFER. */
230862306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3));
230962306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	/* Denies linking because of missing MAKE_REG. */
231262306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1));
231362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
231462306a36Sopenharmony_ci	/* Denies linking because of missing destination REFER. */
231562306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2));
231662306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	/* Allows linking because of REFER and MAKE_REG. */
231962306a36Sopenharmony_ci	ASSERT_EQ(0, link(file1_s2d2, file1_s1d3));
232062306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s2d2));
232162306a36Sopenharmony_ci	/* Reverse linking denied because of missing MAKE_REG. */
232262306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2));
232362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
232462306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s2d3));
232562306a36Sopenharmony_ci	/* Checks reverse linking. */
232662306a36Sopenharmony_ci	ASSERT_EQ(0, link(file1_s1d3, file1_s2d3));
232762306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	/*
233062306a36Sopenharmony_ci	 * This is OK for a file link, but it should not be allowed for a
233162306a36Sopenharmony_ci	 * directory rename (because of the superset of access rights.
233262306a36Sopenharmony_ci	 */
233362306a36Sopenharmony_ci	ASSERT_EQ(0, link(file1_s2d3, file1_s1d3));
233462306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
233762306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
233862306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
233962306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
234262306a36Sopenharmony_ci	ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
234362306a36Sopenharmony_ci}
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_rename)
234662306a36Sopenharmony_ci{
234762306a36Sopenharmony_ci	/* Same rules as for reparent_link. */
234862306a36Sopenharmony_ci	const struct rule layer1[] = {
234962306a36Sopenharmony_ci		{
235062306a36Sopenharmony_ci			.path = dir_s1d2,
235162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
235262306a36Sopenharmony_ci		},
235362306a36Sopenharmony_ci		{
235462306a36Sopenharmony_ci			.path = dir_s1d3,
235562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
235662306a36Sopenharmony_ci		},
235762306a36Sopenharmony_ci		{
235862306a36Sopenharmony_ci			.path = dir_s2d2,
235962306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
236062306a36Sopenharmony_ci		},
236162306a36Sopenharmony_ci		{
236262306a36Sopenharmony_ci			.path = dir_s2d3,
236362306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
236462306a36Sopenharmony_ci		},
236562306a36Sopenharmony_ci		{},
236662306a36Sopenharmony_ci	};
236762306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(
236862306a36Sopenharmony_ci		_metadata,
236962306a36Sopenharmony_ci		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
237262306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
237362306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
237662306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	/* Denies renaming because of missing MAKE_REG. */
237962306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1,
238062306a36Sopenharmony_ci				RENAME_EXCHANGE));
238162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
238262306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1,
238362306a36Sopenharmony_ci				RENAME_EXCHANGE));
238462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
238562306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d1));
238662306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
238762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
238862306a36Sopenharmony_ci	/* Even denies same file exchange. */
238962306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1,
239062306a36Sopenharmony_ci				RENAME_EXCHANGE));
239162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	/* Denies renaming because of missing source and destination REFER. */
239462306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2));
239562306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
239662306a36Sopenharmony_ci	/*
239762306a36Sopenharmony_ci	 * Denies renaming because of missing MAKE_REG, source and destination
239862306a36Sopenharmony_ci	 * REFER.
239962306a36Sopenharmony_ci	 */
240062306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1,
240162306a36Sopenharmony_ci				RENAME_EXCHANGE));
240262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
240362306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1,
240462306a36Sopenharmony_ci				RENAME_EXCHANGE));
240562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	/* Denies renaming because of missing source REFER. */
240862306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
240962306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
241062306a36Sopenharmony_ci	/* Denies renaming because of missing MAKE_REG. */
241162306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3,
241262306a36Sopenharmony_ci				RENAME_EXCHANGE));
241362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	/* Denies renaming because of missing MAKE_REG. */
241662306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1));
241762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
241862306a36Sopenharmony_ci	/* Denies renaming because of missing destination REFER*/
241962306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
242062306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci	/* Denies exchange because of one missing MAKE_REG. */
242362306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3,
242462306a36Sopenharmony_ci				RENAME_EXCHANGE));
242562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
242662306a36Sopenharmony_ci	/* Allows renaming because of REFER and MAKE_REG. */
242762306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3));
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci	/* Reverse renaming denied because of missing MAKE_REG. */
243062306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2));
243162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
243262306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s2d3));
243362306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	/* Tests reverse renaming. */
243662306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
243762306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3,
243862306a36Sopenharmony_ci			       RENAME_EXCHANGE));
243962306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	/*
244262306a36Sopenharmony_ci	 * This is OK for a file rename, but it should not be allowed for a
244362306a36Sopenharmony_ci	 * directory rename (because of the superset of access rights).
244462306a36Sopenharmony_ci	 */
244562306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
244662306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	/*
244962306a36Sopenharmony_ci	 * Tests superset restrictions applied to directories.  Not only the
245062306a36Sopenharmony_ci	 * dir_s2d3's parent (dir_s2d2) should be taken into account but also
245162306a36Sopenharmony_ci	 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right
245262306a36Sopenharmony_ci	 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided
245362306a36Sopenharmony_ci	 * directly by the moved dir_s2d3.
245462306a36Sopenharmony_ci	 */
245562306a36Sopenharmony_ci	ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3));
245662306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3));
245762306a36Sopenharmony_ci	/*
245862306a36Sopenharmony_ci	 * The first rename is allowed but not the exchange because dir_s1d3's
245962306a36Sopenharmony_ci	 * parent (dir_s1d2) doesn't have REFER.
246062306a36Sopenharmony_ci	 */
246162306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
246262306a36Sopenharmony_ci				RENAME_EXCHANGE));
246362306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
246462306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3,
246562306a36Sopenharmony_ci				RENAME_EXCHANGE));
246662306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
246762306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3));
246862306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3));
247162306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
247262306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2));
247362306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	/* Renaming in the same directory is always allowed. */
247662306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2));
247762306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3));
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
248062306a36Sopenharmony_ci	/* Denies because of missing source MAKE_REG and destination REFER. */
248162306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2));
248262306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
248562306a36Sopenharmony_ci	/* Denies because of missing source MAKE_REG and REFER. */
248662306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3));
248762306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
248862306a36Sopenharmony_ci}
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_cistatic void
249162306a36Sopenharmony_cireparent_exdev_layers_enforce1(struct __test_metadata *const _metadata)
249262306a36Sopenharmony_ci{
249362306a36Sopenharmony_ci	const struct rule layer1[] = {
249462306a36Sopenharmony_ci		{
249562306a36Sopenharmony_ci			.path = dir_s1d2,
249662306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
249762306a36Sopenharmony_ci		},
249862306a36Sopenharmony_ci		{
249962306a36Sopenharmony_ci			/* Interesting for the layer2 tests. */
250062306a36Sopenharmony_ci			.path = dir_s1d3,
250162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
250262306a36Sopenharmony_ci		},
250362306a36Sopenharmony_ci		{
250462306a36Sopenharmony_ci			.path = dir_s2d2,
250562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
250662306a36Sopenharmony_ci		},
250762306a36Sopenharmony_ci		{
250862306a36Sopenharmony_ci			.path = dir_s2d3,
250962306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
251062306a36Sopenharmony_ci		},
251162306a36Sopenharmony_ci		{},
251262306a36Sopenharmony_ci	};
251362306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(
251462306a36Sopenharmony_ci		_metadata,
251562306a36Sopenharmony_ci		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
251862306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
251962306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
252062306a36Sopenharmony_ci}
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_cistatic void
252362306a36Sopenharmony_cireparent_exdev_layers_enforce2(struct __test_metadata *const _metadata)
252462306a36Sopenharmony_ci{
252562306a36Sopenharmony_ci	const struct rule layer2[] = {
252662306a36Sopenharmony_ci		{
252762306a36Sopenharmony_ci			.path = dir_s2d3,
252862306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_DIR,
252962306a36Sopenharmony_ci		},
253062306a36Sopenharmony_ci		{},
253162306a36Sopenharmony_ci	};
253262306a36Sopenharmony_ci	/*
253362306a36Sopenharmony_ci	 * Same checks as before but with a second layer and a new MAKE_DIR
253462306a36Sopenharmony_ci	 * rule (and no explicit handling of REFER).
253562306a36Sopenharmony_ci	 */
253662306a36Sopenharmony_ci	const int ruleset_fd =
253762306a36Sopenharmony_ci		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2);
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
254062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
254162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
254262306a36Sopenharmony_ci}
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_rename1)
254562306a36Sopenharmony_ci{
254662306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s2d2));
254762306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s2d3));
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci	reparent_exdev_layers_enforce1(_metadata);
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	/*
255262306a36Sopenharmony_ci	 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock
255362306a36Sopenharmony_ci	 * because it doesn't inherit new access rights.
255462306a36Sopenharmony_ci	 */
255562306a36Sopenharmony_ci	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
255662306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	/*
255962306a36Sopenharmony_ci	 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it
256062306a36Sopenharmony_ci	 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is
256162306a36Sopenharmony_ci	 * already allowed for dir_s1d3.
256262306a36Sopenharmony_ci	 */
256362306a36Sopenharmony_ci	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3));
256462306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3));
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	/*
256762306a36Sopenharmony_ci	 * However, moving the file1_s1d3 file below dir_s2d3 is allowed
256862306a36Sopenharmony_ci	 * because it cannot inherit MAKE_REG right (which is dedicated to
256962306a36Sopenharmony_ci	 * directories).
257062306a36Sopenharmony_ci	 */
257162306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci	reparent_exdev_layers_enforce2(_metadata);
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	/*
257662306a36Sopenharmony_ci	 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because
257762306a36Sopenharmony_ci	 * MAKE_DIR is not tied to dir_s2d2.
257862306a36Sopenharmony_ci	 */
257962306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2));
258062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	/*
258362306a36Sopenharmony_ci	 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it
258462306a36Sopenharmony_ci	 * would grants MAKE_REG and MAKE_DIR rights to it.
258562306a36Sopenharmony_ci	 */
258662306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
258762306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	/*
259062306a36Sopenharmony_ci	 * Moving the file2_s1d3 file below dir_s2d3 is denied because the
259162306a36Sopenharmony_ci	 * second layer does not handle REFER, which is always denied by
259262306a36Sopenharmony_ci	 * default.
259362306a36Sopenharmony_ci	 */
259462306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3));
259562306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
259662306a36Sopenharmony_ci}
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_rename2)
259962306a36Sopenharmony_ci{
260062306a36Sopenharmony_ci	reparent_exdev_layers_enforce1(_metadata);
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	/* Checks EACCES predominance over EXDEV. */
260362306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
260462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
260562306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2));
260662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
260762306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
260862306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
260962306a36Sopenharmony_ci	/* Modify layout! */
261062306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3));
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	/* Without REFER source. */
261362306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
261462306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
261562306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
261662306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	reparent_exdev_layers_enforce2(_metadata);
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	/* Checks EACCES predominance over EXDEV. */
262162306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
262262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
262362306a36Sopenharmony_ci	/* Checks with actual file2_s1d2. */
262462306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2));
262562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
262662306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
262762306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
262862306a36Sopenharmony_ci	/*
262962306a36Sopenharmony_ci	 * Modifying the layout is now denied because the second layer does not
263062306a36Sopenharmony_ci	 * handle REFER, which is always denied by default.
263162306a36Sopenharmony_ci	 */
263262306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
263362306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_ci	/* Without REFER source, EACCES wins over EXDEV. */
263662306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
263762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
263862306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
263962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
264062306a36Sopenharmony_ci}
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_exchange1)
264362306a36Sopenharmony_ci{
264462306a36Sopenharmony_ci	const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 =
264562306a36Sopenharmony_ci							       file2_s2d3;
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
264862306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
264962306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s2d3));
265062306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	reparent_exdev_layers_enforce1(_metadata);
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci	/* Error predominance with file exchange: returns EXDEV and EACCES. */
265562306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
265662306a36Sopenharmony_ci				RENAME_EXCHANGE));
265762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
265862306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
265962306a36Sopenharmony_ci				RENAME_EXCHANGE));
266062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci	/*
266362306a36Sopenharmony_ci	 * Checks with directories which creation could be allowed, but denied
266462306a36Sopenharmony_ci	 * because of access rights that would be inherited.
266562306a36Sopenharmony_ci	 */
266662306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
266762306a36Sopenharmony_ci				dir_file2_s2d3, RENAME_EXCHANGE));
266862306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
266962306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
267062306a36Sopenharmony_ci				dir_file1_s1d2, RENAME_EXCHANGE));
267162306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
267262306a36Sopenharmony_ci
267362306a36Sopenharmony_ci	/* Checks with same access rights. */
267462306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
267562306a36Sopenharmony_ci			       RENAME_EXCHANGE));
267662306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
267762306a36Sopenharmony_ci			       RENAME_EXCHANGE));
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	/* Checks with different (child-only) access rights. */
268062306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
268162306a36Sopenharmony_ci			       RENAME_EXCHANGE));
268262306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
268362306a36Sopenharmony_ci			       RENAME_EXCHANGE));
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	/*
268662306a36Sopenharmony_ci	 * Checks that exchange between file and directory are consistent.
268762306a36Sopenharmony_ci	 *
268862306a36Sopenharmony_ci	 * Moving a file (file1_s2d2) to a directory which only grants more
268962306a36Sopenharmony_ci	 * directory-related access rights is allowed, and at the same time
269062306a36Sopenharmony_ci	 * moving a directory (dir_file2_s2d3) to another directory which
269162306a36Sopenharmony_ci	 * grants less access rights is allowed too.
269262306a36Sopenharmony_ci	 *
269362306a36Sopenharmony_ci	 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments.
269462306a36Sopenharmony_ci	 */
269562306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
269662306a36Sopenharmony_ci			       RENAME_EXCHANGE));
269762306a36Sopenharmony_ci	/*
269862306a36Sopenharmony_ci	 * However, moving back the directory is denied because it would get
269962306a36Sopenharmony_ci	 * more access rights than the current state and because file creation
270062306a36Sopenharmony_ci	 * is forbidden (in dir_s2d2).
270162306a36Sopenharmony_ci	 */
270262306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
270362306a36Sopenharmony_ci				RENAME_EXCHANGE));
270462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
270562306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
270662306a36Sopenharmony_ci				RENAME_EXCHANGE));
270762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	reparent_exdev_layers_enforce2(_metadata);
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	/* Error predominance with file exchange: returns EXDEV and EACCES. */
271262306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
271362306a36Sopenharmony_ci				RENAME_EXCHANGE));
271462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
271562306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
271662306a36Sopenharmony_ci				RENAME_EXCHANGE));
271762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	/* Checks with directories which creation is now denied. */
272062306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
272162306a36Sopenharmony_ci				dir_file2_s2d3, RENAME_EXCHANGE));
272262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
272362306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
272462306a36Sopenharmony_ci				dir_file1_s1d2, RENAME_EXCHANGE));
272562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	/* Checks with different (child-only) access rights. */
272862306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
272962306a36Sopenharmony_ci				RENAME_EXCHANGE));
273062306a36Sopenharmony_ci	/* Denied because of MAKE_DIR. */
273162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
273262306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
273362306a36Sopenharmony_ci				RENAME_EXCHANGE));
273462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci	/* Checks with different (child-only) access rights. */
273762306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
273862306a36Sopenharmony_ci				RENAME_EXCHANGE));
273962306a36Sopenharmony_ci	/* Denied because of MAKE_DIR. */
274062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
274162306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
274262306a36Sopenharmony_ci				RENAME_EXCHANGE));
274362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	/* See layout1.reparent_exdev_layers_exchange2 for complement. */
274662306a36Sopenharmony_ci}
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_exchange2)
274962306a36Sopenharmony_ci{
275062306a36Sopenharmony_ci	const char *const dir_file2_s2d3 = file2_s2d3;
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s2d3));
275362306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	reparent_exdev_layers_enforce1(_metadata);
275662306a36Sopenharmony_ci	reparent_exdev_layers_enforce2(_metadata);
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	/* Checks that exchange between file and directory are consistent. */
275962306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
276062306a36Sopenharmony_ci				RENAME_EXCHANGE));
276162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
276262306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
276362306a36Sopenharmony_ci				RENAME_EXCHANGE));
276462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
276562306a36Sopenharmony_ci}
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_exchange3)
276862306a36Sopenharmony_ci{
276962306a36Sopenharmony_ci	const char *const dir_file2_s2d3 = file2_s2d3;
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s2d3));
277262306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci	reparent_exdev_layers_enforce1(_metadata);
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci	/*
277762306a36Sopenharmony_ci	 * Checks that exchange between file and directory are consistent,
277862306a36Sopenharmony_ci	 * including with inverted arguments (see
277962306a36Sopenharmony_ci	 * layout1.reparent_exdev_layers_exchange1).
278062306a36Sopenharmony_ci	 */
278162306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
278262306a36Sopenharmony_ci			       RENAME_EXCHANGE));
278362306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
278462306a36Sopenharmony_ci				RENAME_EXCHANGE));
278562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
278662306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
278762306a36Sopenharmony_ci				RENAME_EXCHANGE));
278862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
278962306a36Sopenharmony_ci}
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_remove)
279262306a36Sopenharmony_ci{
279362306a36Sopenharmony_ci	const struct rule layer1[] = {
279462306a36Sopenharmony_ci		{
279562306a36Sopenharmony_ci			.path = dir_s1d1,
279662306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER |
279762306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_REMOVE_DIR,
279862306a36Sopenharmony_ci		},
279962306a36Sopenharmony_ci		{
280062306a36Sopenharmony_ci			.path = dir_s1d2,
280162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
280262306a36Sopenharmony_ci		},
280362306a36Sopenharmony_ci		{
280462306a36Sopenharmony_ci			.path = dir_s2d1,
280562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER |
280662306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_REMOVE_FILE,
280762306a36Sopenharmony_ci		},
280862306a36Sopenharmony_ci		{},
280962306a36Sopenharmony_ci	};
281062306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(
281162306a36Sopenharmony_ci		_metadata,
281262306a36Sopenharmony_ci		LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR |
281362306a36Sopenharmony_ci			LANDLOCK_ACCESS_FS_REMOVE_FILE,
281462306a36Sopenharmony_ci		layer1);
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
281762306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
281862306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	/* Access denied because of wrong/swapped remove file/dir. */
282162306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2));
282262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
282362306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1));
282462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
282562306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2,
282662306a36Sopenharmony_ci				RENAME_EXCHANGE));
282762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
282862306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3,
282962306a36Sopenharmony_ci				RENAME_EXCHANGE));
283062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	/* Access allowed thanks to the matching rights. */
283362306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2));
283462306a36Sopenharmony_ci	ASSERT_EQ(EISDIR, errno);
283562306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1));
283662306a36Sopenharmony_ci	ASSERT_EQ(ENOTDIR, errno);
283762306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
283862306a36Sopenharmony_ci	ASSERT_EQ(ENOTDIR, errno);
283962306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s2d1));
284062306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
284162306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d3));
284262306a36Sopenharmony_ci	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1));
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	/* Effectively removes a file and a directory by exchanging them. */
284562306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
284662306a36Sopenharmony_ci	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
284762306a36Sopenharmony_ci			       RENAME_EXCHANGE));
284862306a36Sopenharmony_ci	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
284962306a36Sopenharmony_ci				RENAME_EXCHANGE));
285062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
285162306a36Sopenharmony_ci}
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_dom_superset)
285462306a36Sopenharmony_ci{
285562306a36Sopenharmony_ci	const struct rule layer1[] = {
285662306a36Sopenharmony_ci		{
285762306a36Sopenharmony_ci			.path = dir_s1d2,
285862306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
285962306a36Sopenharmony_ci		},
286062306a36Sopenharmony_ci		{
286162306a36Sopenharmony_ci			.path = file1_s1d2,
286262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_EXECUTE,
286362306a36Sopenharmony_ci		},
286462306a36Sopenharmony_ci		{
286562306a36Sopenharmony_ci			.path = dir_s1d3,
286662306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_SOCK |
286762306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_EXECUTE,
286862306a36Sopenharmony_ci		},
286962306a36Sopenharmony_ci		{
287062306a36Sopenharmony_ci			.path = dir_s2d2,
287162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER |
287262306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_EXECUTE |
287362306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_MAKE_SOCK,
287462306a36Sopenharmony_ci		},
287562306a36Sopenharmony_ci		{
287662306a36Sopenharmony_ci			.path = dir_s2d3,
287762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
287862306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_MAKE_FIFO,
287962306a36Sopenharmony_ci		},
288062306a36Sopenharmony_ci		{},
288162306a36Sopenharmony_ci	};
288262306a36Sopenharmony_ci	int ruleset_fd = create_ruleset(_metadata,
288362306a36Sopenharmony_ci					LANDLOCK_ACCESS_FS_REFER |
288462306a36Sopenharmony_ci						LANDLOCK_ACCESS_FS_EXECUTE |
288562306a36Sopenharmony_ci						LANDLOCK_ACCESS_FS_MAKE_SOCK |
288662306a36Sopenharmony_ci						LANDLOCK_ACCESS_FS_READ_FILE |
288762306a36Sopenharmony_ci						LANDLOCK_ACCESS_FS_MAKE_FIFO,
288862306a36Sopenharmony_ci					layer1);
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
289162306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
289262306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1));
289562306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
289662306a36Sopenharmony_ci	/*
289762306a36Sopenharmony_ci	 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE
289862306a36Sopenharmony_ci	 * access right.
289962306a36Sopenharmony_ci	 */
290062306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3));
290162306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
290262306a36Sopenharmony_ci	/*
290362306a36Sopenharmony_ci	 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a
290462306a36Sopenharmony_ci	 * superset of access rights compared to dir_s1d2, because file1_s1d2
290562306a36Sopenharmony_ci	 * already has these access rights anyway.
290662306a36Sopenharmony_ci	 */
290762306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2));
290862306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2));
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
291162306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
291262306a36Sopenharmony_ci	/*
291362306a36Sopenharmony_ci	 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access
291462306a36Sopenharmony_ci	 * right.
291562306a36Sopenharmony_ci	 */
291662306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
291762306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
291862306a36Sopenharmony_ci	/*
291962306a36Sopenharmony_ci	 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset
292062306a36Sopenharmony_ci	 * of access rights compared to dir_s1d2, because dir_s1d3 already has
292162306a36Sopenharmony_ci	 * these access rights anyway.
292262306a36Sopenharmony_ci	 */
292362306a36Sopenharmony_ci	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
292462306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci	/*
292762306a36Sopenharmony_ci	 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back
292862306a36Sopenharmony_ci	 * will be denied because the new inherited access rights from dir_s1d2
292962306a36Sopenharmony_ci	 * will be less than the destination (original) dir_s2d3.  This is a
293062306a36Sopenharmony_ci	 * sinkhole scenario where we cannot move back files or directories.
293162306a36Sopenharmony_ci	 */
293262306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2));
293362306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
293462306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
293562306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d2));
293662306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s2d3));
293762306a36Sopenharmony_ci	/*
293862306a36Sopenharmony_ci	 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and
293962306a36Sopenharmony_ci	 * MAKE_SOCK which were inherited from dir_s1d3.
294062306a36Sopenharmony_ci	 */
294162306a36Sopenharmony_ci	ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2));
294262306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3));
294362306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
294462306a36Sopenharmony_ci}
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ciTEST_F_FORK(layout1, remove_dir)
294762306a36Sopenharmony_ci{
294862306a36Sopenharmony_ci	const struct rule rules[] = {
294962306a36Sopenharmony_ci		{
295062306a36Sopenharmony_ci			.path = dir_s1d2,
295162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
295262306a36Sopenharmony_ci		},
295362306a36Sopenharmony_ci		{},
295462306a36Sopenharmony_ci	};
295562306a36Sopenharmony_ci	const int ruleset_fd =
295662306a36Sopenharmony_ci		create_ruleset(_metadata, rules[0].access, rules);
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d1));
296162306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
296262306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
296362306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d3));
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
296662306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	ASSERT_EQ(0, rmdir(dir_s1d3));
296962306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
297062306a36Sopenharmony_ci	ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	/* dir_s1d2 itself cannot be removed. */
297362306a36Sopenharmony_ci	ASSERT_EQ(-1, rmdir(dir_s1d2));
297462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
297562306a36Sopenharmony_ci	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR));
297662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
297762306a36Sopenharmony_ci	ASSERT_EQ(-1, rmdir(dir_s1d1));
297862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
297962306a36Sopenharmony_ci	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR));
298062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
298162306a36Sopenharmony_ci}
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ciTEST_F_FORK(layout1, remove_file)
298462306a36Sopenharmony_ci{
298562306a36Sopenharmony_ci	const struct rule rules[] = {
298662306a36Sopenharmony_ci		{
298762306a36Sopenharmony_ci			.path = dir_s1d2,
298862306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
298962306a36Sopenharmony_ci		},
299062306a36Sopenharmony_ci		{},
299162306a36Sopenharmony_ci	};
299262306a36Sopenharmony_ci	const int ruleset_fd =
299362306a36Sopenharmony_ci		create_ruleset(_metadata, rules[0].access, rules);
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
299662306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
299762306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ci	ASSERT_EQ(-1, unlink(file1_s1d1));
300062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
300162306a36Sopenharmony_ci	ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0));
300262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
300362306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
300462306a36Sopenharmony_ci	ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0));
300562306a36Sopenharmony_ci}
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_cistatic void test_make_file(struct __test_metadata *const _metadata,
300862306a36Sopenharmony_ci			   const __u64 access, const mode_t mode,
300962306a36Sopenharmony_ci			   const dev_t dev)
301062306a36Sopenharmony_ci{
301162306a36Sopenharmony_ci	const struct rule rules[] = {
301262306a36Sopenharmony_ci		{
301362306a36Sopenharmony_ci			.path = dir_s1d2,
301462306a36Sopenharmony_ci			.access = access,
301562306a36Sopenharmony_ci		},
301662306a36Sopenharmony_ci		{},
301762306a36Sopenharmony_ci	};
301862306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(_metadata, access, rules);
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
302162306a36Sopenharmony_ci
302262306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d1));
302362306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d1));
302462306a36Sopenharmony_ci	ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev))
302562306a36Sopenharmony_ci	{
302662306a36Sopenharmony_ci		TH_LOG("Failed to make file \"%s\": %s", file2_s1d1,
302762306a36Sopenharmony_ci		       strerror(errno));
302862306a36Sopenharmony_ci	};
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
303162306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d2));
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
303462306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d3));
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
303762306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci	ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev));
304062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
304162306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
304262306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
304362306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
304462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
304562306a36Sopenharmony_ci
304662306a36Sopenharmony_ci	ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev))
304762306a36Sopenharmony_ci	{
304862306a36Sopenharmony_ci		TH_LOG("Failed to make file \"%s\": %s", file1_s1d2,
304962306a36Sopenharmony_ci		       strerror(errno));
305062306a36Sopenharmony_ci	};
305162306a36Sopenharmony_ci	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
305262306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d2));
305362306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
305462306a36Sopenharmony_ci
305562306a36Sopenharmony_ci	ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev));
305662306a36Sopenharmony_ci	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
305762306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d3));
305862306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
305962306a36Sopenharmony_ci}
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ciTEST_F_FORK(layout1, make_char)
306262306a36Sopenharmony_ci{
306362306a36Sopenharmony_ci	/* Creates a /dev/null device. */
306462306a36Sopenharmony_ci	set_cap(_metadata, CAP_MKNOD);
306562306a36Sopenharmony_ci	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR,
306662306a36Sopenharmony_ci		       makedev(1, 3));
306762306a36Sopenharmony_ci}
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ciTEST_F_FORK(layout1, make_block)
307062306a36Sopenharmony_ci{
307162306a36Sopenharmony_ci	/* Creates a /dev/loop0 device. */
307262306a36Sopenharmony_ci	set_cap(_metadata, CAP_MKNOD);
307362306a36Sopenharmony_ci	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK,
307462306a36Sopenharmony_ci		       makedev(7, 0));
307562306a36Sopenharmony_ci}
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ciTEST_F_FORK(layout1, make_reg_1)
307862306a36Sopenharmony_ci{
307962306a36Sopenharmony_ci	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);
308062306a36Sopenharmony_ci}
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ciTEST_F_FORK(layout1, make_reg_2)
308362306a36Sopenharmony_ci{
308462306a36Sopenharmony_ci	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);
308562306a36Sopenharmony_ci}
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ciTEST_F_FORK(layout1, make_sock)
308862306a36Sopenharmony_ci{
308962306a36Sopenharmony_ci	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);
309062306a36Sopenharmony_ci}
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_ciTEST_F_FORK(layout1, make_fifo)
309362306a36Sopenharmony_ci{
309462306a36Sopenharmony_ci	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);
309562306a36Sopenharmony_ci}
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_ciTEST_F_FORK(layout1, make_sym)
309862306a36Sopenharmony_ci{
309962306a36Sopenharmony_ci	const struct rule rules[] = {
310062306a36Sopenharmony_ci		{
310162306a36Sopenharmony_ci			.path = dir_s1d2,
310262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_SYM,
310362306a36Sopenharmony_ci		},
310462306a36Sopenharmony_ci		{},
310562306a36Sopenharmony_ci	};
310662306a36Sopenharmony_ci	const int ruleset_fd =
310762306a36Sopenharmony_ci		create_ruleset(_metadata, rules[0].access, rules);
310862306a36Sopenharmony_ci
310962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d1));
311262306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d1));
311362306a36Sopenharmony_ci	ASSERT_EQ(0, symlink("none", file2_s1d1));
311462306a36Sopenharmony_ci
311562306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
311662306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d2));
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
311962306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d3));
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
312262306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci	ASSERT_EQ(-1, symlink("none", file1_s1d1));
312562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
312662306a36Sopenharmony_ci	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
312762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
312862306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
312962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
313062306a36Sopenharmony_ci
313162306a36Sopenharmony_ci	ASSERT_EQ(0, symlink("none", file1_s1d2));
313262306a36Sopenharmony_ci	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
313362306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d2));
313462306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	ASSERT_EQ(0, symlink("none", file1_s1d3));
313762306a36Sopenharmony_ci	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
313862306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file2_s1d3));
313962306a36Sopenharmony_ci	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
314062306a36Sopenharmony_ci}
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ciTEST_F_FORK(layout1, make_dir)
314362306a36Sopenharmony_ci{
314462306a36Sopenharmony_ci	const struct rule rules[] = {
314562306a36Sopenharmony_ci		{
314662306a36Sopenharmony_ci			.path = dir_s1d2,
314762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_MAKE_DIR,
314862306a36Sopenharmony_ci		},
314962306a36Sopenharmony_ci		{},
315062306a36Sopenharmony_ci	};
315162306a36Sopenharmony_ci	const int ruleset_fd =
315262306a36Sopenharmony_ci		create_ruleset(_metadata, rules[0].access, rules);
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d1));
315762306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
315862306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d3));
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
316162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	/* Uses file_* as directory names. */
316462306a36Sopenharmony_ci	ASSERT_EQ(-1, mkdir(file1_s1d1, 0700));
316562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
316662306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
316762306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(file1_s1d3, 0700));
316862306a36Sopenharmony_ci}
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_cistatic int open_proc_fd(struct __test_metadata *const _metadata, const int fd,
317162306a36Sopenharmony_ci			const int open_flags)
317262306a36Sopenharmony_ci{
317362306a36Sopenharmony_ci	static const char path_template[] = "/proc/self/fd/%d";
317462306a36Sopenharmony_ci	char procfd_path[sizeof(path_template) + 10];
317562306a36Sopenharmony_ci	const int procfd_path_size =
317662306a36Sopenharmony_ci		snprintf(procfd_path, sizeof(procfd_path), path_template, fd);
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	ASSERT_LT(procfd_path_size, sizeof(procfd_path));
317962306a36Sopenharmony_ci	return open(procfd_path, open_flags);
318062306a36Sopenharmony_ci}
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ciTEST_F_FORK(layout1, proc_unlinked_file)
318362306a36Sopenharmony_ci{
318462306a36Sopenharmony_ci	const struct rule rules[] = {
318562306a36Sopenharmony_ci		{
318662306a36Sopenharmony_ci			.path = file1_s1d2,
318762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
318862306a36Sopenharmony_ci		},
318962306a36Sopenharmony_ci		{},
319062306a36Sopenharmony_ci	};
319162306a36Sopenharmony_ci	int reg_fd, proc_fd;
319262306a36Sopenharmony_ci	const int ruleset_fd = create_ruleset(
319362306a36Sopenharmony_ci		_metadata,
319462306a36Sopenharmony_ci		LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
319562306a36Sopenharmony_ci		rules);
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
319862306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
319962306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
320062306a36Sopenharmony_ci
320162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
320262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
320362306a36Sopenharmony_ci	reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC);
320462306a36Sopenharmony_ci	ASSERT_LE(0, reg_fd);
320562306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file1_s1d2));
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC);
320862306a36Sopenharmony_ci	ASSERT_LE(0, proc_fd);
320962306a36Sopenharmony_ci	ASSERT_EQ(0, close(proc_fd));
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC);
321262306a36Sopenharmony_ci	ASSERT_EQ(-1, proc_fd)
321362306a36Sopenharmony_ci	{
321462306a36Sopenharmony_ci		TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd,
321562306a36Sopenharmony_ci		       strerror(errno));
321662306a36Sopenharmony_ci	}
321762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, errno);
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci	ASSERT_EQ(0, close(reg_fd));
322062306a36Sopenharmony_ci}
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ciTEST_F_FORK(layout1, proc_pipe)
322362306a36Sopenharmony_ci{
322462306a36Sopenharmony_ci	int proc_fd;
322562306a36Sopenharmony_ci	int pipe_fds[2];
322662306a36Sopenharmony_ci	char buf = '\0';
322762306a36Sopenharmony_ci	const struct rule rules[] = {
322862306a36Sopenharmony_ci		{
322962306a36Sopenharmony_ci			.path = dir_s1d2,
323062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
323162306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
323262306a36Sopenharmony_ci		},
323362306a36Sopenharmony_ci		{},
323462306a36Sopenharmony_ci	};
323562306a36Sopenharmony_ci	/* Limits read and write access to files tied to the filesystem. */
323662306a36Sopenharmony_ci	const int ruleset_fd =
323762306a36Sopenharmony_ci		create_ruleset(_metadata, rules[0].access, rules);
323862306a36Sopenharmony_ci
323962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
324062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
324162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	/* Checks enforcement for normal files. */
324462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
324562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	/* Checks access to pipes through FD. */
324862306a36Sopenharmony_ci	ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC));
324962306a36Sopenharmony_ci	ASSERT_EQ(1, write(pipe_fds[1], ".", 1))
325062306a36Sopenharmony_ci	{
325162306a36Sopenharmony_ci		TH_LOG("Failed to write in pipe: %s", strerror(errno));
325262306a36Sopenharmony_ci	}
325362306a36Sopenharmony_ci	ASSERT_EQ(1, read(pipe_fds[0], &buf, 1));
325462306a36Sopenharmony_ci	ASSERT_EQ('.', buf);
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci	/* Checks write access to pipe through /proc/self/fd . */
325762306a36Sopenharmony_ci	proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC);
325862306a36Sopenharmony_ci	ASSERT_LE(0, proc_fd);
325962306a36Sopenharmony_ci	ASSERT_EQ(1, write(proc_fd, ".", 1))
326062306a36Sopenharmony_ci	{
326162306a36Sopenharmony_ci		TH_LOG("Failed to write through /proc/self/fd/%d: %s",
326262306a36Sopenharmony_ci		       pipe_fds[1], strerror(errno));
326362306a36Sopenharmony_ci	}
326462306a36Sopenharmony_ci	ASSERT_EQ(0, close(proc_fd));
326562306a36Sopenharmony_ci
326662306a36Sopenharmony_ci	/* Checks read access to pipe through /proc/self/fd . */
326762306a36Sopenharmony_ci	proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC);
326862306a36Sopenharmony_ci	ASSERT_LE(0, proc_fd);
326962306a36Sopenharmony_ci	buf = '\0';
327062306a36Sopenharmony_ci	ASSERT_EQ(1, read(proc_fd, &buf, 1))
327162306a36Sopenharmony_ci	{
327262306a36Sopenharmony_ci		TH_LOG("Failed to read through /proc/self/fd/%d: %s",
327362306a36Sopenharmony_ci		       pipe_fds[1], strerror(errno));
327462306a36Sopenharmony_ci	}
327562306a36Sopenharmony_ci	ASSERT_EQ(0, close(proc_fd));
327662306a36Sopenharmony_ci
327762306a36Sopenharmony_ci	ASSERT_EQ(0, close(pipe_fds[0]));
327862306a36Sopenharmony_ci	ASSERT_EQ(0, close(pipe_fds[1]));
327962306a36Sopenharmony_ci}
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci/* Invokes truncate(2) and returns its errno or 0. */
328262306a36Sopenharmony_cistatic int test_truncate(const char *const path)
328362306a36Sopenharmony_ci{
328462306a36Sopenharmony_ci	if (truncate(path, 10) < 0)
328562306a36Sopenharmony_ci		return errno;
328662306a36Sopenharmony_ci	return 0;
328762306a36Sopenharmony_ci}
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_ci/*
329062306a36Sopenharmony_ci * Invokes creat(2) and returns its errno or 0.
329162306a36Sopenharmony_ci * Closes the opened file descriptor on success.
329262306a36Sopenharmony_ci */
329362306a36Sopenharmony_cistatic int test_creat(const char *const path)
329462306a36Sopenharmony_ci{
329562306a36Sopenharmony_ci	int fd = creat(path, 0600);
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_ci	if (fd < 0)
329862306a36Sopenharmony_ci		return errno;
329962306a36Sopenharmony_ci
330062306a36Sopenharmony_ci	/*
330162306a36Sopenharmony_ci	 * Mixing error codes from close(2) and creat(2) should not lead to any
330262306a36Sopenharmony_ci	 * (access type) confusion for this test.
330362306a36Sopenharmony_ci	 */
330462306a36Sopenharmony_ci	if (close(fd) < 0)
330562306a36Sopenharmony_ci		return errno;
330662306a36Sopenharmony_ci	return 0;
330762306a36Sopenharmony_ci}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci/*
331062306a36Sopenharmony_ci * Exercises file truncation when it's not restricted,
331162306a36Sopenharmony_ci * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed.
331262306a36Sopenharmony_ci */
331362306a36Sopenharmony_ciTEST_F_FORK(layout1, truncate_unhandled)
331462306a36Sopenharmony_ci{
331562306a36Sopenharmony_ci	const char *const file_r = file1_s1d1;
331662306a36Sopenharmony_ci	const char *const file_w = file2_s1d1;
331762306a36Sopenharmony_ci	const char *const file_none = file1_s1d2;
331862306a36Sopenharmony_ci	const struct rule rules[] = {
331962306a36Sopenharmony_ci		{
332062306a36Sopenharmony_ci			.path = file_r,
332162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
332262306a36Sopenharmony_ci		},
332362306a36Sopenharmony_ci		{
332462306a36Sopenharmony_ci			.path = file_w,
332562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
332662306a36Sopenharmony_ci		},
332762306a36Sopenharmony_ci		/* Implicitly: No rights for file_none. */
332862306a36Sopenharmony_ci		{},
332962306a36Sopenharmony_ci	};
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_ci	const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
333262306a36Sopenharmony_ci			      LANDLOCK_ACCESS_FS_WRITE_FILE;
333362306a36Sopenharmony_ci	int ruleset_fd;
333462306a36Sopenharmony_ci
333562306a36Sopenharmony_ci	/* Enable Landlock. */
333662306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, handled, rules);
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
333962306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
334062306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci	/*
334362306a36Sopenharmony_ci	 * Checks read right: truncate and open with O_TRUNC work, unless the
334462306a36Sopenharmony_ci	 * file is attempted to be opened for writing.
334562306a36Sopenharmony_ci	 */
334662306a36Sopenharmony_ci	EXPECT_EQ(0, test_truncate(file_r));
334762306a36Sopenharmony_ci	EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC));
334862306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC));
334962306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_creat(file_r));
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci	/*
335262306a36Sopenharmony_ci	 * Checks write right: truncate and open with O_TRUNC work, unless the
335362306a36Sopenharmony_ci	 * file is attempted to be opened for reading.
335462306a36Sopenharmony_ci	 */
335562306a36Sopenharmony_ci	EXPECT_EQ(0, test_truncate(file_w));
335662306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC));
335762306a36Sopenharmony_ci	EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC));
335862306a36Sopenharmony_ci	EXPECT_EQ(0, test_creat(file_w));
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_ci	/*
336162306a36Sopenharmony_ci	 * Checks "no rights" case: truncate works but all open attempts fail,
336262306a36Sopenharmony_ci	 * including creat.
336362306a36Sopenharmony_ci	 */
336462306a36Sopenharmony_ci	EXPECT_EQ(0, test_truncate(file_none));
336562306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
336662306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
336762306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_creat(file_none));
336862306a36Sopenharmony_ci}
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ciTEST_F_FORK(layout1, truncate)
337162306a36Sopenharmony_ci{
337262306a36Sopenharmony_ci	const char *const file_rwt = file1_s1d1;
337362306a36Sopenharmony_ci	const char *const file_rw = file2_s1d1;
337462306a36Sopenharmony_ci	const char *const file_rt = file1_s1d2;
337562306a36Sopenharmony_ci	const char *const file_t = file2_s1d2;
337662306a36Sopenharmony_ci	const char *const file_none = file1_s1d3;
337762306a36Sopenharmony_ci	const char *const dir_t = dir_s2d1;
337862306a36Sopenharmony_ci	const char *const file_in_dir_t = file1_s2d1;
337962306a36Sopenharmony_ci	const char *const dir_w = dir_s3d1;
338062306a36Sopenharmony_ci	const char *const file_in_dir_w = file1_s3d1;
338162306a36Sopenharmony_ci	const struct rule rules[] = {
338262306a36Sopenharmony_ci		{
338362306a36Sopenharmony_ci			.path = file_rwt,
338462306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
338562306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE |
338662306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_TRUNCATE,
338762306a36Sopenharmony_ci		},
338862306a36Sopenharmony_ci		{
338962306a36Sopenharmony_ci			.path = file_rw,
339062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
339162306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
339262306a36Sopenharmony_ci		},
339362306a36Sopenharmony_ci		{
339462306a36Sopenharmony_ci			.path = file_rt,
339562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
339662306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_TRUNCATE,
339762306a36Sopenharmony_ci		},
339862306a36Sopenharmony_ci		{
339962306a36Sopenharmony_ci			.path = file_t,
340062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_TRUNCATE,
340162306a36Sopenharmony_ci		},
340262306a36Sopenharmony_ci		/* Implicitly: No access rights for file_none. */
340362306a36Sopenharmony_ci		{
340462306a36Sopenharmony_ci			.path = dir_t,
340562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_TRUNCATE,
340662306a36Sopenharmony_ci		},
340762306a36Sopenharmony_ci		{
340862306a36Sopenharmony_ci			.path = dir_w,
340962306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
341062306a36Sopenharmony_ci		},
341162306a36Sopenharmony_ci		{},
341262306a36Sopenharmony_ci	};
341362306a36Sopenharmony_ci	const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
341462306a36Sopenharmony_ci			      LANDLOCK_ACCESS_FS_WRITE_FILE |
341562306a36Sopenharmony_ci			      LANDLOCK_ACCESS_FS_TRUNCATE;
341662306a36Sopenharmony_ci	int ruleset_fd;
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci	/* Enable Landlock. */
341962306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, handled, rules);
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
342262306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
342362306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci	/* Checks read, write and truncate rights: truncation works. */
342662306a36Sopenharmony_ci	EXPECT_EQ(0, test_truncate(file_rwt));
342762306a36Sopenharmony_ci	EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC));
342862306a36Sopenharmony_ci	EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC));
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_ci	/* Checks read and write rights: no truncate variant works. */
343162306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_truncate(file_rw));
343262306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC));
343362306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC));
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci	/*
343662306a36Sopenharmony_ci	 * Checks read and truncate rights: truncation works.
343762306a36Sopenharmony_ci	 *
343862306a36Sopenharmony_ci	 * Note: Files can get truncated using open() even with O_RDONLY.
343962306a36Sopenharmony_ci	 */
344062306a36Sopenharmony_ci	EXPECT_EQ(0, test_truncate(file_rt));
344162306a36Sopenharmony_ci	EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC));
344262306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC));
344362306a36Sopenharmony_ci
344462306a36Sopenharmony_ci	/* Checks truncate right: truncate works, but can't open file. */
344562306a36Sopenharmony_ci	EXPECT_EQ(0, test_truncate(file_t));
344662306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC));
344762306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC));
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci	/* Checks "no rights" case: No form of truncation works. */
345062306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_truncate(file_none));
345162306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
345262306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	/*
345562306a36Sopenharmony_ci	 * Checks truncate right on directory: truncate works on contained
345662306a36Sopenharmony_ci	 * files.
345762306a36Sopenharmony_ci	 */
345862306a36Sopenharmony_ci	EXPECT_EQ(0, test_truncate(file_in_dir_t));
345962306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC));
346062306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC));
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci	/*
346362306a36Sopenharmony_ci	 * Checks creat in dir_w: This requires the truncate right when
346462306a36Sopenharmony_ci	 * overwriting an existing file, but does not require it when the file
346562306a36Sopenharmony_ci	 * is new.
346662306a36Sopenharmony_ci	 */
346762306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_creat(file_in_dir_w));
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci	ASSERT_EQ(0, unlink(file_in_dir_w));
347062306a36Sopenharmony_ci	EXPECT_EQ(0, test_creat(file_in_dir_w));
347162306a36Sopenharmony_ci}
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci/* Invokes ftruncate(2) and returns its errno or 0. */
347462306a36Sopenharmony_cistatic int test_ftruncate(int fd)
347562306a36Sopenharmony_ci{
347662306a36Sopenharmony_ci	if (ftruncate(fd, 10) < 0)
347762306a36Sopenharmony_ci		return errno;
347862306a36Sopenharmony_ci	return 0;
347962306a36Sopenharmony_ci}
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ciTEST_F_FORK(layout1, ftruncate)
348262306a36Sopenharmony_ci{
348362306a36Sopenharmony_ci	/*
348462306a36Sopenharmony_ci	 * This test opens a new file descriptor at different stages of
348562306a36Sopenharmony_ci	 * Landlock restriction:
348662306a36Sopenharmony_ci	 *
348762306a36Sopenharmony_ci	 * without restriction:                    ftruncate works
348862306a36Sopenharmony_ci	 * something else but truncate restricted: ftruncate works
348962306a36Sopenharmony_ci	 * truncate restricted and permitted:      ftruncate works
349062306a36Sopenharmony_ci	 * truncate restricted and not permitted:  ftruncate fails
349162306a36Sopenharmony_ci	 *
349262306a36Sopenharmony_ci	 * Whether this works or not is expected to depend on the time when the
349362306a36Sopenharmony_ci	 * FD was opened, not to depend on the time when ftruncate() was
349462306a36Sopenharmony_ci	 * called.
349562306a36Sopenharmony_ci	 */
349662306a36Sopenharmony_ci	const char *const path = file1_s1d1;
349762306a36Sopenharmony_ci	const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE |
349862306a36Sopenharmony_ci			       LANDLOCK_ACCESS_FS_WRITE_FILE;
349962306a36Sopenharmony_ci	const struct rule layer1[] = {
350062306a36Sopenharmony_ci		{
350162306a36Sopenharmony_ci			.path = path,
350262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
350362306a36Sopenharmony_ci		},
350462306a36Sopenharmony_ci		{},
350562306a36Sopenharmony_ci	};
350662306a36Sopenharmony_ci	const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE;
350762306a36Sopenharmony_ci	const struct rule layer2[] = {
350862306a36Sopenharmony_ci		{
350962306a36Sopenharmony_ci			.path = path,
351062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_TRUNCATE,
351162306a36Sopenharmony_ci		},
351262306a36Sopenharmony_ci		{},
351362306a36Sopenharmony_ci	};
351462306a36Sopenharmony_ci	const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE |
351562306a36Sopenharmony_ci			       LANDLOCK_ACCESS_FS_WRITE_FILE;
351662306a36Sopenharmony_ci	const struct rule layer3[] = {
351762306a36Sopenharmony_ci		{
351862306a36Sopenharmony_ci			.path = path,
351962306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
352062306a36Sopenharmony_ci		},
352162306a36Sopenharmony_ci		{},
352262306a36Sopenharmony_ci	};
352362306a36Sopenharmony_ci	int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd;
352462306a36Sopenharmony_ci
352562306a36Sopenharmony_ci	fd_layer0 = open(path, O_WRONLY);
352662306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd_layer0));
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, handled1, layer1);
352962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
353062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
353162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
353262306a36Sopenharmony_ci
353362306a36Sopenharmony_ci	fd_layer1 = open(path, O_WRONLY);
353462306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd_layer0));
353562306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd_layer1));
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, handled2, layer2);
353862306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
353962306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
354062306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci	fd_layer2 = open(path, O_WRONLY);
354362306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd_layer0));
354462306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd_layer1));
354562306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd_layer2));
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, handled3, layer3);
354862306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
354962306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
355062306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	fd_layer3 = open(path, O_WRONLY);
355362306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd_layer0));
355462306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd_layer1));
355562306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd_layer2));
355662306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_ftruncate(fd_layer3));
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci	ASSERT_EQ(0, close(fd_layer0));
355962306a36Sopenharmony_ci	ASSERT_EQ(0, close(fd_layer1));
356062306a36Sopenharmony_ci	ASSERT_EQ(0, close(fd_layer2));
356162306a36Sopenharmony_ci	ASSERT_EQ(0, close(fd_layer3));
356262306a36Sopenharmony_ci}
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_ci/* clang-format off */
356562306a36Sopenharmony_ciFIXTURE(ftruncate) {};
356662306a36Sopenharmony_ci/* clang-format on */
356762306a36Sopenharmony_ci
356862306a36Sopenharmony_ciFIXTURE_SETUP(ftruncate)
356962306a36Sopenharmony_ci{
357062306a36Sopenharmony_ci	prepare_layout(_metadata);
357162306a36Sopenharmony_ci	create_file(_metadata, file1_s1d1);
357262306a36Sopenharmony_ci}
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ciFIXTURE_TEARDOWN(ftruncate)
357562306a36Sopenharmony_ci{
357662306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(file1_s1d1));
357762306a36Sopenharmony_ci	cleanup_layout(_metadata);
357862306a36Sopenharmony_ci}
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ciFIXTURE_VARIANT(ftruncate)
358162306a36Sopenharmony_ci{
358262306a36Sopenharmony_ci	const __u64 handled;
358362306a36Sopenharmony_ci	const __u64 permitted;
358462306a36Sopenharmony_ci	const int expected_open_result;
358562306a36Sopenharmony_ci	const int expected_ftruncate_result;
358662306a36Sopenharmony_ci};
358762306a36Sopenharmony_ci
358862306a36Sopenharmony_ci/* clang-format off */
358962306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, w_w) {
359062306a36Sopenharmony_ci	/* clang-format on */
359162306a36Sopenharmony_ci	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE,
359262306a36Sopenharmony_ci	.permitted = LANDLOCK_ACCESS_FS_WRITE_FILE,
359362306a36Sopenharmony_ci	.expected_open_result = 0,
359462306a36Sopenharmony_ci	.expected_ftruncate_result = 0,
359562306a36Sopenharmony_ci};
359662306a36Sopenharmony_ci
359762306a36Sopenharmony_ci/* clang-format off */
359862306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, t_t) {
359962306a36Sopenharmony_ci	/* clang-format on */
360062306a36Sopenharmony_ci	.handled = LANDLOCK_ACCESS_FS_TRUNCATE,
360162306a36Sopenharmony_ci	.permitted = LANDLOCK_ACCESS_FS_TRUNCATE,
360262306a36Sopenharmony_ci	.expected_open_result = 0,
360362306a36Sopenharmony_ci	.expected_ftruncate_result = 0,
360462306a36Sopenharmony_ci};
360562306a36Sopenharmony_ci
360662306a36Sopenharmony_ci/* clang-format off */
360762306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, wt_w) {
360862306a36Sopenharmony_ci	/* clang-format on */
360962306a36Sopenharmony_ci	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
361062306a36Sopenharmony_ci	.permitted = LANDLOCK_ACCESS_FS_WRITE_FILE,
361162306a36Sopenharmony_ci	.expected_open_result = 0,
361262306a36Sopenharmony_ci	.expected_ftruncate_result = EACCES,
361362306a36Sopenharmony_ci};
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci/* clang-format off */
361662306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, wt_wt) {
361762306a36Sopenharmony_ci	/* clang-format on */
361862306a36Sopenharmony_ci	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
361962306a36Sopenharmony_ci	.permitted = LANDLOCK_ACCESS_FS_WRITE_FILE |
362062306a36Sopenharmony_ci		     LANDLOCK_ACCESS_FS_TRUNCATE,
362162306a36Sopenharmony_ci	.expected_open_result = 0,
362262306a36Sopenharmony_ci	.expected_ftruncate_result = 0,
362362306a36Sopenharmony_ci};
362462306a36Sopenharmony_ci
362562306a36Sopenharmony_ci/* clang-format off */
362662306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, wt_t) {
362762306a36Sopenharmony_ci	/* clang-format on */
362862306a36Sopenharmony_ci	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
362962306a36Sopenharmony_ci	.permitted = LANDLOCK_ACCESS_FS_TRUNCATE,
363062306a36Sopenharmony_ci	.expected_open_result = EACCES,
363162306a36Sopenharmony_ci};
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ciTEST_F_FORK(ftruncate, open_and_ftruncate)
363462306a36Sopenharmony_ci{
363562306a36Sopenharmony_ci	const char *const path = file1_s1d1;
363662306a36Sopenharmony_ci	const struct rule rules[] = {
363762306a36Sopenharmony_ci		{
363862306a36Sopenharmony_ci			.path = path,
363962306a36Sopenharmony_ci			.access = variant->permitted,
364062306a36Sopenharmony_ci		},
364162306a36Sopenharmony_ci		{},
364262306a36Sopenharmony_ci	};
364362306a36Sopenharmony_ci	int fd, ruleset_fd;
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci	/* Enable Landlock. */
364662306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
364762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
364862306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
364962306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	fd = open(path, O_WRONLY);
365262306a36Sopenharmony_ci	EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
365362306a36Sopenharmony_ci	if (fd >= 0) {
365462306a36Sopenharmony_ci		EXPECT_EQ(variant->expected_ftruncate_result,
365562306a36Sopenharmony_ci			  test_ftruncate(fd));
365662306a36Sopenharmony_ci		ASSERT_EQ(0, close(fd));
365762306a36Sopenharmony_ci	}
365862306a36Sopenharmony_ci}
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ciTEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)
366162306a36Sopenharmony_ci{
366262306a36Sopenharmony_ci	int child, fd, status;
366362306a36Sopenharmony_ci	int socket_fds[2];
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci	ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
366662306a36Sopenharmony_ci				socket_fds));
366762306a36Sopenharmony_ci
366862306a36Sopenharmony_ci	child = fork();
366962306a36Sopenharmony_ci	ASSERT_LE(0, child);
367062306a36Sopenharmony_ci	if (child == 0) {
367162306a36Sopenharmony_ci		/*
367262306a36Sopenharmony_ci		 * Enables Landlock in the child process, open a file descriptor
367362306a36Sopenharmony_ci		 * where truncation is forbidden and send it to the
367462306a36Sopenharmony_ci		 * non-landlocked parent process.
367562306a36Sopenharmony_ci		 */
367662306a36Sopenharmony_ci		const char *const path = file1_s1d1;
367762306a36Sopenharmony_ci		const struct rule rules[] = {
367862306a36Sopenharmony_ci			{
367962306a36Sopenharmony_ci				.path = path,
368062306a36Sopenharmony_ci				.access = variant->permitted,
368162306a36Sopenharmony_ci			},
368262306a36Sopenharmony_ci			{},
368362306a36Sopenharmony_ci		};
368462306a36Sopenharmony_ci		int fd, ruleset_fd;
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci		ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
368762306a36Sopenharmony_ci		ASSERT_LE(0, ruleset_fd);
368862306a36Sopenharmony_ci		enforce_ruleset(_metadata, ruleset_fd);
368962306a36Sopenharmony_ci		ASSERT_EQ(0, close(ruleset_fd));
369062306a36Sopenharmony_ci
369162306a36Sopenharmony_ci		fd = open(path, O_WRONLY);
369262306a36Sopenharmony_ci		ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci		if (fd >= 0) {
369562306a36Sopenharmony_ci			ASSERT_EQ(0, send_fd(socket_fds[0], fd));
369662306a36Sopenharmony_ci			ASSERT_EQ(0, close(fd));
369762306a36Sopenharmony_ci		}
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci		ASSERT_EQ(0, close(socket_fds[0]));
370062306a36Sopenharmony_ci
370162306a36Sopenharmony_ci		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
370262306a36Sopenharmony_ci		return;
370362306a36Sopenharmony_ci	}
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_ci	if (variant->expected_open_result == 0) {
370662306a36Sopenharmony_ci		fd = recv_fd(socket_fds[1]);
370762306a36Sopenharmony_ci		ASSERT_LE(0, fd);
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_ci		EXPECT_EQ(variant->expected_ftruncate_result,
371062306a36Sopenharmony_ci			  test_ftruncate(fd));
371162306a36Sopenharmony_ci		ASSERT_EQ(0, close(fd));
371262306a36Sopenharmony_ci	}
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci	ASSERT_EQ(child, waitpid(child, &status, 0));
371562306a36Sopenharmony_ci	ASSERT_EQ(1, WIFEXITED(status));
371662306a36Sopenharmony_ci	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
371762306a36Sopenharmony_ci
371862306a36Sopenharmony_ci	ASSERT_EQ(0, close(socket_fds[0]));
371962306a36Sopenharmony_ci	ASSERT_EQ(0, close(socket_fds[1]));
372062306a36Sopenharmony_ci}
372162306a36Sopenharmony_ci
372262306a36Sopenharmony_ciTEST(memfd_ftruncate)
372362306a36Sopenharmony_ci{
372462306a36Sopenharmony_ci	int fd;
372562306a36Sopenharmony_ci
372662306a36Sopenharmony_ci	fd = memfd_create("name", MFD_CLOEXEC);
372762306a36Sopenharmony_ci	ASSERT_LE(0, fd);
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	/*
373062306a36Sopenharmony_ci	 * Checks that ftruncate is permitted on file descriptors that are
373162306a36Sopenharmony_ci	 * created in ways other than open(2).
373262306a36Sopenharmony_ci	 */
373362306a36Sopenharmony_ci	EXPECT_EQ(0, test_ftruncate(fd));
373462306a36Sopenharmony_ci
373562306a36Sopenharmony_ci	ASSERT_EQ(0, close(fd));
373662306a36Sopenharmony_ci}
373762306a36Sopenharmony_ci
373862306a36Sopenharmony_ci/* clang-format off */
373962306a36Sopenharmony_ciFIXTURE(layout1_bind) {};
374062306a36Sopenharmony_ci/* clang-format on */
374162306a36Sopenharmony_ci
374262306a36Sopenharmony_ciFIXTURE_SETUP(layout1_bind)
374362306a36Sopenharmony_ci{
374462306a36Sopenharmony_ci	prepare_layout(_metadata);
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	create_layout1(_metadata);
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
374962306a36Sopenharmony_ci	ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
375062306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
375162306a36Sopenharmony_ci}
375262306a36Sopenharmony_ci
375362306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout1_bind)
375462306a36Sopenharmony_ci{
375562306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
375662306a36Sopenharmony_ci	EXPECT_EQ(0, umount(dir_s2d2));
375762306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
375862306a36Sopenharmony_ci
375962306a36Sopenharmony_ci	remove_layout1(_metadata);
376062306a36Sopenharmony_ci
376162306a36Sopenharmony_ci	cleanup_layout(_metadata);
376262306a36Sopenharmony_ci}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_cistatic const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";
376562306a36Sopenharmony_cistatic const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_ci/*
376862306a36Sopenharmony_ci * layout1_bind hierarchy:
376962306a36Sopenharmony_ci *
377062306a36Sopenharmony_ci * tmp
377162306a36Sopenharmony_ci * ├── s1d1
377262306a36Sopenharmony_ci * │   ├── f1
377362306a36Sopenharmony_ci * │   ├── f2
377462306a36Sopenharmony_ci * │   └── s1d2
377562306a36Sopenharmony_ci * │       ├── f1
377662306a36Sopenharmony_ci * │       ├── f2
377762306a36Sopenharmony_ci * │       └── s1d3
377862306a36Sopenharmony_ci * │           ├── f1
377962306a36Sopenharmony_ci * │           └── f2
378062306a36Sopenharmony_ci * ├── s2d1
378162306a36Sopenharmony_ci * │   ├── f1
378262306a36Sopenharmony_ci * │   └── s2d2
378362306a36Sopenharmony_ci * │       ├── f1
378462306a36Sopenharmony_ci * │       ├── f2
378562306a36Sopenharmony_ci * │       └── s1d3
378662306a36Sopenharmony_ci * │           ├── f1
378762306a36Sopenharmony_ci * │           └── f2
378862306a36Sopenharmony_ci * └── s3d1
378962306a36Sopenharmony_ci *     └── s3d2
379062306a36Sopenharmony_ci *         └── s3d3
379162306a36Sopenharmony_ci */
379262306a36Sopenharmony_ci
379362306a36Sopenharmony_ciTEST_F_FORK(layout1_bind, no_restriction)
379462306a36Sopenharmony_ci{
379562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
379662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
379762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
379862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
379962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
380062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
380162306a36Sopenharmony_ci
380262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
380362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
380462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
380562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
380662306a36Sopenharmony_ci	ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY));
380762306a36Sopenharmony_ci	ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY));
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY));
381062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
381362306a36Sopenharmony_ci}
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_ciTEST_F_FORK(layout1_bind, same_content_same_file)
381662306a36Sopenharmony_ci{
381762306a36Sopenharmony_ci	/*
381862306a36Sopenharmony_ci	 * Sets access right on parent directories of both source and
381962306a36Sopenharmony_ci	 * destination mount points.
382062306a36Sopenharmony_ci	 */
382162306a36Sopenharmony_ci	const struct rule layer1_parent[] = {
382262306a36Sopenharmony_ci		{
382362306a36Sopenharmony_ci			.path = dir_s1d1,
382462306a36Sopenharmony_ci			.access = ACCESS_RO,
382562306a36Sopenharmony_ci		},
382662306a36Sopenharmony_ci		{
382762306a36Sopenharmony_ci			.path = dir_s2d1,
382862306a36Sopenharmony_ci			.access = ACCESS_RW,
382962306a36Sopenharmony_ci		},
383062306a36Sopenharmony_ci		{},
383162306a36Sopenharmony_ci	};
383262306a36Sopenharmony_ci	/*
383362306a36Sopenharmony_ci	 * Sets access rights on the same bind-mounted directories.  The result
383462306a36Sopenharmony_ci	 * should be ACCESS_RW for both directories, but not both hierarchies
383562306a36Sopenharmony_ci	 * because of the first layer.
383662306a36Sopenharmony_ci	 */
383762306a36Sopenharmony_ci	const struct rule layer2_mount_point[] = {
383862306a36Sopenharmony_ci		{
383962306a36Sopenharmony_ci			.path = dir_s1d2,
384062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
384162306a36Sopenharmony_ci		},
384262306a36Sopenharmony_ci		{
384362306a36Sopenharmony_ci			.path = dir_s2d2,
384462306a36Sopenharmony_ci			.access = ACCESS_RW,
384562306a36Sopenharmony_ci		},
384662306a36Sopenharmony_ci		{},
384762306a36Sopenharmony_ci	};
384862306a36Sopenharmony_ci	/* Only allow read-access to the s1d3 hierarchies. */
384962306a36Sopenharmony_ci	const struct rule layer3_source[] = {
385062306a36Sopenharmony_ci		{
385162306a36Sopenharmony_ci			.path = dir_s1d3,
385262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
385362306a36Sopenharmony_ci		},
385462306a36Sopenharmony_ci		{},
385562306a36Sopenharmony_ci	};
385662306a36Sopenharmony_ci	/* Removes all access rights. */
385762306a36Sopenharmony_ci	const struct rule layer4_destination[] = {
385862306a36Sopenharmony_ci		{
385962306a36Sopenharmony_ci			.path = bind_file1_s1d3,
386062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,
386162306a36Sopenharmony_ci		},
386262306a36Sopenharmony_ci		{},
386362306a36Sopenharmony_ci	};
386462306a36Sopenharmony_ci	int ruleset_fd;
386562306a36Sopenharmony_ci
386662306a36Sopenharmony_ci	/* Sets rules for the parent directories. */
386762306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent);
386862306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
386962306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
387062306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ci	/* Checks source hierarchy. */
387362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
387462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
387562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
387662306a36Sopenharmony_ci
387762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
387862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
387962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
388062306a36Sopenharmony_ci
388162306a36Sopenharmony_ci	/* Checks destination hierarchy. */
388262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR));
388362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
388462306a36Sopenharmony_ci
388562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
388662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
388762306a36Sopenharmony_ci
388862306a36Sopenharmony_ci	/* Sets rules for the mount points. */
388962306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point);
389062306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
389162306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
389262306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	/* Checks source hierarchy. */
389562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
389662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
389762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
390062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
390162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
390262306a36Sopenharmony_ci
390362306a36Sopenharmony_ci	/* Checks destination hierarchy. */
390462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY));
390562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY));
390662306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
390762306a36Sopenharmony_ci
390862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
390962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
391062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci	/* Sets a (shared) rule only on the source. */
391362306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source);
391462306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
391562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
391662306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
391762306a36Sopenharmony_ci
391862306a36Sopenharmony_ci	/* Checks source hierarchy. */
391962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
392062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
392162306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
392262306a36Sopenharmony_ci
392362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
392462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
392562306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
392662306a36Sopenharmony_ci
392762306a36Sopenharmony_ci	/* Checks destination hierarchy. */
392862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY));
392962306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY));
393062306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
393362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
393462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci	/* Sets a (shared) rule only on the destination. */
393762306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination);
393862306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
393962306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
394062306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
394162306a36Sopenharmony_ci
394262306a36Sopenharmony_ci	/* Checks source hierarchy. */
394362306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
394462306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci	/* Checks destination hierarchy. */
394762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY));
394862306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
394962306a36Sopenharmony_ci}
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_ciTEST_F_FORK(layout1_bind, reparent_cross_mount)
395262306a36Sopenharmony_ci{
395362306a36Sopenharmony_ci	const struct rule layer1[] = {
395462306a36Sopenharmony_ci		{
395562306a36Sopenharmony_ci			/* dir_s2d1 is beneath the dir_s2d2 mount point. */
395662306a36Sopenharmony_ci			.path = dir_s2d1,
395762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_REFER,
395862306a36Sopenharmony_ci		},
395962306a36Sopenharmony_ci		{
396062306a36Sopenharmony_ci			.path = bind_dir_s1d3,
396162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_EXECUTE,
396262306a36Sopenharmony_ci		},
396362306a36Sopenharmony_ci		{},
396462306a36Sopenharmony_ci	};
396562306a36Sopenharmony_ci	int ruleset_fd = create_ruleset(
396662306a36Sopenharmony_ci		_metadata,
396762306a36Sopenharmony_ci		LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1);
396862306a36Sopenharmony_ci
396962306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
397062306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
397162306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
397262306a36Sopenharmony_ci
397362306a36Sopenharmony_ci	/* Checks basic denied move. */
397462306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2));
397562306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
397662306a36Sopenharmony_ci
397762306a36Sopenharmony_ci	/* Checks real cross-mount move (Landlock is not involved). */
397862306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2));
397962306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_ci	/* Checks move that will give more accesses. */
398262306a36Sopenharmony_ci	ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3));
398362306a36Sopenharmony_ci	ASSERT_EQ(EXDEV, errno);
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_ci	/* Checks legitimate downgrade move. */
398662306a36Sopenharmony_ci	ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2));
398762306a36Sopenharmony_ci}
398862306a36Sopenharmony_ci
398962306a36Sopenharmony_ci#define LOWER_BASE TMP_DIR "/lower"
399062306a36Sopenharmony_ci#define LOWER_DATA LOWER_BASE "/data"
399162306a36Sopenharmony_cistatic const char lower_fl1[] = LOWER_DATA "/fl1";
399262306a36Sopenharmony_cistatic const char lower_dl1[] = LOWER_DATA "/dl1";
399362306a36Sopenharmony_cistatic const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";
399462306a36Sopenharmony_cistatic const char lower_fo1[] = LOWER_DATA "/fo1";
399562306a36Sopenharmony_cistatic const char lower_do1[] = LOWER_DATA "/do1";
399662306a36Sopenharmony_cistatic const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";
399762306a36Sopenharmony_cistatic const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";
399862306a36Sopenharmony_ci
399962306a36Sopenharmony_cistatic const char (*lower_base_files[])[] = {
400062306a36Sopenharmony_ci	&lower_fl1,
400162306a36Sopenharmony_ci	&lower_fo1,
400262306a36Sopenharmony_ci	NULL,
400362306a36Sopenharmony_ci};
400462306a36Sopenharmony_cistatic const char (*lower_base_directories[])[] = {
400562306a36Sopenharmony_ci	&lower_dl1,
400662306a36Sopenharmony_ci	&lower_do1,
400762306a36Sopenharmony_ci	NULL,
400862306a36Sopenharmony_ci};
400962306a36Sopenharmony_cistatic const char (*lower_sub_files[])[] = {
401062306a36Sopenharmony_ci	&lower_dl1_fl2,
401162306a36Sopenharmony_ci	&lower_do1_fo2,
401262306a36Sopenharmony_ci	&lower_do1_fl3,
401362306a36Sopenharmony_ci	NULL,
401462306a36Sopenharmony_ci};
401562306a36Sopenharmony_ci
401662306a36Sopenharmony_ci#define UPPER_BASE TMP_DIR "/upper"
401762306a36Sopenharmony_ci#define UPPER_DATA UPPER_BASE "/data"
401862306a36Sopenharmony_ci#define UPPER_WORK UPPER_BASE "/work"
401962306a36Sopenharmony_cistatic const char upper_fu1[] = UPPER_DATA "/fu1";
402062306a36Sopenharmony_cistatic const char upper_du1[] = UPPER_DATA "/du1";
402162306a36Sopenharmony_cistatic const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";
402262306a36Sopenharmony_cistatic const char upper_fo1[] = UPPER_DATA "/fo1";
402362306a36Sopenharmony_cistatic const char upper_do1[] = UPPER_DATA "/do1";
402462306a36Sopenharmony_cistatic const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";
402562306a36Sopenharmony_cistatic const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";
402662306a36Sopenharmony_ci
402762306a36Sopenharmony_cistatic const char (*upper_base_files[])[] = {
402862306a36Sopenharmony_ci	&upper_fu1,
402962306a36Sopenharmony_ci	&upper_fo1,
403062306a36Sopenharmony_ci	NULL,
403162306a36Sopenharmony_ci};
403262306a36Sopenharmony_cistatic const char (*upper_base_directories[])[] = {
403362306a36Sopenharmony_ci	&upper_du1,
403462306a36Sopenharmony_ci	&upper_do1,
403562306a36Sopenharmony_ci	NULL,
403662306a36Sopenharmony_ci};
403762306a36Sopenharmony_cistatic const char (*upper_sub_files[])[] = {
403862306a36Sopenharmony_ci	&upper_du1_fu2,
403962306a36Sopenharmony_ci	&upper_do1_fo2,
404062306a36Sopenharmony_ci	&upper_do1_fu3,
404162306a36Sopenharmony_ci	NULL,
404262306a36Sopenharmony_ci};
404362306a36Sopenharmony_ci
404462306a36Sopenharmony_ci#define MERGE_BASE TMP_DIR "/merge"
404562306a36Sopenharmony_ci#define MERGE_DATA MERGE_BASE "/data"
404662306a36Sopenharmony_cistatic const char merge_fl1[] = MERGE_DATA "/fl1";
404762306a36Sopenharmony_cistatic const char merge_dl1[] = MERGE_DATA "/dl1";
404862306a36Sopenharmony_cistatic const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";
404962306a36Sopenharmony_cistatic const char merge_fu1[] = MERGE_DATA "/fu1";
405062306a36Sopenharmony_cistatic const char merge_du1[] = MERGE_DATA "/du1";
405162306a36Sopenharmony_cistatic const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";
405262306a36Sopenharmony_cistatic const char merge_fo1[] = MERGE_DATA "/fo1";
405362306a36Sopenharmony_cistatic const char merge_do1[] = MERGE_DATA "/do1";
405462306a36Sopenharmony_cistatic const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";
405562306a36Sopenharmony_cistatic const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";
405662306a36Sopenharmony_cistatic const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";
405762306a36Sopenharmony_ci
405862306a36Sopenharmony_cistatic const char (*merge_base_files[])[] = {
405962306a36Sopenharmony_ci	&merge_fl1,
406062306a36Sopenharmony_ci	&merge_fu1,
406162306a36Sopenharmony_ci	&merge_fo1,
406262306a36Sopenharmony_ci	NULL,
406362306a36Sopenharmony_ci};
406462306a36Sopenharmony_cistatic const char (*merge_base_directories[])[] = {
406562306a36Sopenharmony_ci	&merge_dl1,
406662306a36Sopenharmony_ci	&merge_du1,
406762306a36Sopenharmony_ci	&merge_do1,
406862306a36Sopenharmony_ci	NULL,
406962306a36Sopenharmony_ci};
407062306a36Sopenharmony_cistatic const char (*merge_sub_files[])[] = {
407162306a36Sopenharmony_ci	&merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2,
407262306a36Sopenharmony_ci	&merge_do1_fl3, &merge_do1_fu3, NULL,
407362306a36Sopenharmony_ci};
407462306a36Sopenharmony_ci
407562306a36Sopenharmony_ci/*
407662306a36Sopenharmony_ci * layout2_overlay hierarchy:
407762306a36Sopenharmony_ci *
407862306a36Sopenharmony_ci * tmp
407962306a36Sopenharmony_ci * ├── lower
408062306a36Sopenharmony_ci * │   └── data
408162306a36Sopenharmony_ci * │       ├── dl1
408262306a36Sopenharmony_ci * │       │   └── fl2
408362306a36Sopenharmony_ci * │       ├── do1
408462306a36Sopenharmony_ci * │       │   ├── fl3
408562306a36Sopenharmony_ci * │       │   └── fo2
408662306a36Sopenharmony_ci * │       ├── fl1
408762306a36Sopenharmony_ci * │       └── fo1
408862306a36Sopenharmony_ci * ├── merge
408962306a36Sopenharmony_ci * │   └── data
409062306a36Sopenharmony_ci * │       ├── dl1
409162306a36Sopenharmony_ci * │       │   └── fl2
409262306a36Sopenharmony_ci * │       ├── do1
409362306a36Sopenharmony_ci * │       │   ├── fl3
409462306a36Sopenharmony_ci * │       │   ├── fo2
409562306a36Sopenharmony_ci * │       │   └── fu3
409662306a36Sopenharmony_ci * │       ├── du1
409762306a36Sopenharmony_ci * │       │   └── fu2
409862306a36Sopenharmony_ci * │       ├── fl1
409962306a36Sopenharmony_ci * │       ├── fo1
410062306a36Sopenharmony_ci * │       └── fu1
410162306a36Sopenharmony_ci * └── upper
410262306a36Sopenharmony_ci *     ├── data
410362306a36Sopenharmony_ci *     │   ├── do1
410462306a36Sopenharmony_ci *     │   │   ├── fo2
410562306a36Sopenharmony_ci *     │   │   └── fu3
410662306a36Sopenharmony_ci *     │   ├── du1
410762306a36Sopenharmony_ci *     │   │   └── fu2
410862306a36Sopenharmony_ci *     │   ├── fo1
410962306a36Sopenharmony_ci *     │   └── fu1
411062306a36Sopenharmony_ci *     └── work
411162306a36Sopenharmony_ci *         └── work
411262306a36Sopenharmony_ci */
411362306a36Sopenharmony_ci
411462306a36Sopenharmony_ciFIXTURE(layout2_overlay)
411562306a36Sopenharmony_ci{
411662306a36Sopenharmony_ci	bool skip_test;
411762306a36Sopenharmony_ci};
411862306a36Sopenharmony_ci
411962306a36Sopenharmony_ciFIXTURE_SETUP(layout2_overlay)
412062306a36Sopenharmony_ci{
412162306a36Sopenharmony_ci	if (!supports_filesystem("overlay")) {
412262306a36Sopenharmony_ci		self->skip_test = true;
412362306a36Sopenharmony_ci		SKIP(return, "overlayfs is not supported (setup)");
412462306a36Sopenharmony_ci	}
412562306a36Sopenharmony_ci
412662306a36Sopenharmony_ci	prepare_layout(_metadata);
412762306a36Sopenharmony_ci
412862306a36Sopenharmony_ci	create_directory(_metadata, LOWER_BASE);
412962306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
413062306a36Sopenharmony_ci	/* Creates tmpfs mount points to get deterministic overlayfs. */
413162306a36Sopenharmony_ci	ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE));
413262306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
413362306a36Sopenharmony_ci	create_file(_metadata, lower_fl1);
413462306a36Sopenharmony_ci	create_file(_metadata, lower_dl1_fl2);
413562306a36Sopenharmony_ci	create_file(_metadata, lower_fo1);
413662306a36Sopenharmony_ci	create_file(_metadata, lower_do1_fo2);
413762306a36Sopenharmony_ci	create_file(_metadata, lower_do1_fl3);
413862306a36Sopenharmony_ci
413962306a36Sopenharmony_ci	create_directory(_metadata, UPPER_BASE);
414062306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
414162306a36Sopenharmony_ci	ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE));
414262306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
414362306a36Sopenharmony_ci	create_file(_metadata, upper_fu1);
414462306a36Sopenharmony_ci	create_file(_metadata, upper_du1_fu2);
414562306a36Sopenharmony_ci	create_file(_metadata, upper_fo1);
414662306a36Sopenharmony_ci	create_file(_metadata, upper_do1_fo2);
414762306a36Sopenharmony_ci	create_file(_metadata, upper_do1_fu3);
414862306a36Sopenharmony_ci	ASSERT_EQ(0, mkdir(UPPER_WORK, 0700));
414962306a36Sopenharmony_ci
415062306a36Sopenharmony_ci	create_directory(_metadata, MERGE_DATA);
415162306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
415262306a36Sopenharmony_ci	set_cap(_metadata, CAP_DAC_OVERRIDE);
415362306a36Sopenharmony_ci	ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0,
415462306a36Sopenharmony_ci			   "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA
415562306a36Sopenharmony_ci			   ",workdir=" UPPER_WORK));
415662306a36Sopenharmony_ci	clear_cap(_metadata, CAP_DAC_OVERRIDE);
415762306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
415862306a36Sopenharmony_ci}
415962306a36Sopenharmony_ci
416062306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout2_overlay)
416162306a36Sopenharmony_ci{
416262306a36Sopenharmony_ci	if (self->skip_test)
416362306a36Sopenharmony_ci		SKIP(return, "overlayfs is not supported (teardown)");
416462306a36Sopenharmony_ci
416562306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(lower_do1_fl3));
416662306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(lower_dl1_fl2));
416762306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(lower_fl1));
416862306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(lower_do1_fo2));
416962306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(lower_fo1));
417062306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
417162306a36Sopenharmony_ci	EXPECT_EQ(0, umount(LOWER_BASE));
417262306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
417362306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(LOWER_BASE));
417462306a36Sopenharmony_ci
417562306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(upper_do1_fu3));
417662306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(upper_du1_fu2));
417762306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(upper_fu1));
417862306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(upper_do1_fo2));
417962306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(upper_fo1));
418062306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(UPPER_WORK "/work"));
418162306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
418262306a36Sopenharmony_ci	EXPECT_EQ(0, umount(UPPER_BASE));
418362306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
418462306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(UPPER_BASE));
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
418762306a36Sopenharmony_ci	EXPECT_EQ(0, umount(MERGE_DATA));
418862306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
418962306a36Sopenharmony_ci	EXPECT_EQ(0, remove_path(MERGE_DATA));
419062306a36Sopenharmony_ci
419162306a36Sopenharmony_ci	cleanup_layout(_metadata);
419262306a36Sopenharmony_ci}
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_ciTEST_F_FORK(layout2_overlay, no_restriction)
419562306a36Sopenharmony_ci{
419662306a36Sopenharmony_ci	if (self->skip_test)
419762306a36Sopenharmony_ci		SKIP(return, "overlayfs is not supported (test)");
419862306a36Sopenharmony_ci
419962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
420062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
420162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));
420262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY));
420362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(lower_do1, O_RDONLY));
420462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY));
420562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY));
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY));
420862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(upper_du1, O_RDONLY));
420962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY));
421062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY));
421162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(upper_do1, O_RDONLY));
421262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY));
421362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY));
421462306a36Sopenharmony_ci
421562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY));
421662306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY));
421762306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY));
421862306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY));
421962306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_du1, O_RDONLY));
422062306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY));
422162306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY));
422262306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_do1, O_RDONLY));
422362306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY));
422462306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY));
422562306a36Sopenharmony_ci	ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY));
422662306a36Sopenharmony_ci}
422762306a36Sopenharmony_ci
422862306a36Sopenharmony_ci#define for_each_path(path_list, path_entry, i)               \
422962306a36Sopenharmony_ci	for (i = 0, path_entry = *path_list[i]; path_list[i]; \
423062306a36Sopenharmony_ci	     path_entry = *path_list[++i])
423162306a36Sopenharmony_ci
423262306a36Sopenharmony_ciTEST_F_FORK(layout2_overlay, same_content_different_file)
423362306a36Sopenharmony_ci{
423462306a36Sopenharmony_ci	/* Sets access right on parent directories of both layers. */
423562306a36Sopenharmony_ci	const struct rule layer1_base[] = {
423662306a36Sopenharmony_ci		{
423762306a36Sopenharmony_ci			.path = LOWER_BASE,
423862306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
423962306a36Sopenharmony_ci		},
424062306a36Sopenharmony_ci		{
424162306a36Sopenharmony_ci			.path = UPPER_BASE,
424262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
424362306a36Sopenharmony_ci		},
424462306a36Sopenharmony_ci		{
424562306a36Sopenharmony_ci			.path = MERGE_BASE,
424662306a36Sopenharmony_ci			.access = ACCESS_RW,
424762306a36Sopenharmony_ci		},
424862306a36Sopenharmony_ci		{},
424962306a36Sopenharmony_ci	};
425062306a36Sopenharmony_ci	const struct rule layer2_data[] = {
425162306a36Sopenharmony_ci		{
425262306a36Sopenharmony_ci			.path = LOWER_DATA,
425362306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
425462306a36Sopenharmony_ci		},
425562306a36Sopenharmony_ci		{
425662306a36Sopenharmony_ci			.path = UPPER_DATA,
425762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
425862306a36Sopenharmony_ci		},
425962306a36Sopenharmony_ci		{
426062306a36Sopenharmony_ci			.path = MERGE_DATA,
426162306a36Sopenharmony_ci			.access = ACCESS_RW,
426262306a36Sopenharmony_ci		},
426362306a36Sopenharmony_ci		{},
426462306a36Sopenharmony_ci	};
426562306a36Sopenharmony_ci	/* Sets access right on directories inside both layers. */
426662306a36Sopenharmony_ci	const struct rule layer3_subdirs[] = {
426762306a36Sopenharmony_ci		{
426862306a36Sopenharmony_ci			.path = lower_dl1,
426962306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
427062306a36Sopenharmony_ci		},
427162306a36Sopenharmony_ci		{
427262306a36Sopenharmony_ci			.path = lower_do1,
427362306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
427462306a36Sopenharmony_ci		},
427562306a36Sopenharmony_ci		{
427662306a36Sopenharmony_ci			.path = upper_du1,
427762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
427862306a36Sopenharmony_ci		},
427962306a36Sopenharmony_ci		{
428062306a36Sopenharmony_ci			.path = upper_do1,
428162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
428262306a36Sopenharmony_ci		},
428362306a36Sopenharmony_ci		{
428462306a36Sopenharmony_ci			.path = merge_dl1,
428562306a36Sopenharmony_ci			.access = ACCESS_RW,
428662306a36Sopenharmony_ci		},
428762306a36Sopenharmony_ci		{
428862306a36Sopenharmony_ci			.path = merge_du1,
428962306a36Sopenharmony_ci			.access = ACCESS_RW,
429062306a36Sopenharmony_ci		},
429162306a36Sopenharmony_ci		{
429262306a36Sopenharmony_ci			.path = merge_do1,
429362306a36Sopenharmony_ci			.access = ACCESS_RW,
429462306a36Sopenharmony_ci		},
429562306a36Sopenharmony_ci		{},
429662306a36Sopenharmony_ci	};
429762306a36Sopenharmony_ci	/* Tighten access rights to the files. */
429862306a36Sopenharmony_ci	const struct rule layer4_files[] = {
429962306a36Sopenharmony_ci		{
430062306a36Sopenharmony_ci			.path = lower_dl1_fl2,
430162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
430262306a36Sopenharmony_ci		},
430362306a36Sopenharmony_ci		{
430462306a36Sopenharmony_ci			.path = lower_do1_fo2,
430562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
430662306a36Sopenharmony_ci		},
430762306a36Sopenharmony_ci		{
430862306a36Sopenharmony_ci			.path = lower_do1_fl3,
430962306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
431062306a36Sopenharmony_ci		},
431162306a36Sopenharmony_ci		{
431262306a36Sopenharmony_ci			.path = upper_du1_fu2,
431362306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
431462306a36Sopenharmony_ci		},
431562306a36Sopenharmony_ci		{
431662306a36Sopenharmony_ci			.path = upper_do1_fo2,
431762306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
431862306a36Sopenharmony_ci		},
431962306a36Sopenharmony_ci		{
432062306a36Sopenharmony_ci			.path = upper_do1_fu3,
432162306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
432262306a36Sopenharmony_ci		},
432362306a36Sopenharmony_ci		{
432462306a36Sopenharmony_ci			.path = merge_dl1_fl2,
432562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
432662306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
432762306a36Sopenharmony_ci		},
432862306a36Sopenharmony_ci		{
432962306a36Sopenharmony_ci			.path = merge_du1_fu2,
433062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
433162306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
433262306a36Sopenharmony_ci		},
433362306a36Sopenharmony_ci		{
433462306a36Sopenharmony_ci			.path = merge_do1_fo2,
433562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
433662306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
433762306a36Sopenharmony_ci		},
433862306a36Sopenharmony_ci		{
433962306a36Sopenharmony_ci			.path = merge_do1_fl3,
434062306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
434162306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
434262306a36Sopenharmony_ci		},
434362306a36Sopenharmony_ci		{
434462306a36Sopenharmony_ci			.path = merge_do1_fu3,
434562306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
434662306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
434762306a36Sopenharmony_ci		},
434862306a36Sopenharmony_ci		{},
434962306a36Sopenharmony_ci	};
435062306a36Sopenharmony_ci	const struct rule layer5_merge_only[] = {
435162306a36Sopenharmony_ci		{
435262306a36Sopenharmony_ci			.path = MERGE_DATA,
435362306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE |
435462306a36Sopenharmony_ci				  LANDLOCK_ACCESS_FS_WRITE_FILE,
435562306a36Sopenharmony_ci		},
435662306a36Sopenharmony_ci		{},
435762306a36Sopenharmony_ci	};
435862306a36Sopenharmony_ci	int ruleset_fd;
435962306a36Sopenharmony_ci	size_t i;
436062306a36Sopenharmony_ci	const char *path_entry;
436162306a36Sopenharmony_ci
436262306a36Sopenharmony_ci	if (self->skip_test)
436362306a36Sopenharmony_ci		SKIP(return, "overlayfs is not supported (test)");
436462306a36Sopenharmony_ci
436562306a36Sopenharmony_ci	/* Sets rules on base directories (i.e. outside overlay scope). */
436662306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
436762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
436862306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
436962306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
437062306a36Sopenharmony_ci
437162306a36Sopenharmony_ci	/* Checks lower layer. */
437262306a36Sopenharmony_ci	for_each_path(lower_base_files, path_entry, i) {
437362306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
437462306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
437562306a36Sopenharmony_ci	}
437662306a36Sopenharmony_ci	for_each_path(lower_base_directories, path_entry, i) {
437762306a36Sopenharmony_ci		ASSERT_EQ(EACCES,
437862306a36Sopenharmony_ci			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
437962306a36Sopenharmony_ci	}
438062306a36Sopenharmony_ci	for_each_path(lower_sub_files, path_entry, i) {
438162306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
438262306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
438362306a36Sopenharmony_ci	}
438462306a36Sopenharmony_ci	/* Checks upper layer. */
438562306a36Sopenharmony_ci	for_each_path(upper_base_files, path_entry, i) {
438662306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
438762306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
438862306a36Sopenharmony_ci	}
438962306a36Sopenharmony_ci	for_each_path(upper_base_directories, path_entry, i) {
439062306a36Sopenharmony_ci		ASSERT_EQ(EACCES,
439162306a36Sopenharmony_ci			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
439262306a36Sopenharmony_ci	}
439362306a36Sopenharmony_ci	for_each_path(upper_sub_files, path_entry, i) {
439462306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
439562306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
439662306a36Sopenharmony_ci	}
439762306a36Sopenharmony_ci	/*
439862306a36Sopenharmony_ci	 * Checks that access rights are independent from the lower and upper
439962306a36Sopenharmony_ci	 * layers: write access to upper files viewed through the merge point
440062306a36Sopenharmony_ci	 * is still allowed, and write access to lower file viewed (and copied)
440162306a36Sopenharmony_ci	 * through the merge point is still allowed.
440262306a36Sopenharmony_ci	 */
440362306a36Sopenharmony_ci	for_each_path(merge_base_files, path_entry, i) {
440462306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
440562306a36Sopenharmony_ci	}
440662306a36Sopenharmony_ci	for_each_path(merge_base_directories, path_entry, i) {
440762306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
440862306a36Sopenharmony_ci	}
440962306a36Sopenharmony_ci	for_each_path(merge_sub_files, path_entry, i) {
441062306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
441162306a36Sopenharmony_ci	}
441262306a36Sopenharmony_ci
441362306a36Sopenharmony_ci	/* Sets rules on data directories (i.e. inside overlay scope). */
441462306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data);
441562306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
441662306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
441762306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_ci	/* Checks merge. */
442062306a36Sopenharmony_ci	for_each_path(merge_base_files, path_entry, i) {
442162306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
442262306a36Sopenharmony_ci	}
442362306a36Sopenharmony_ci	for_each_path(merge_base_directories, path_entry, i) {
442462306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
442562306a36Sopenharmony_ci	}
442662306a36Sopenharmony_ci	for_each_path(merge_sub_files, path_entry, i) {
442762306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
442862306a36Sopenharmony_ci	}
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	/* Same checks with tighter rules. */
443162306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs);
443262306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
443362306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
443462306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
443562306a36Sopenharmony_ci
443662306a36Sopenharmony_ci	/* Checks changes for lower layer. */
443762306a36Sopenharmony_ci	for_each_path(lower_base_files, path_entry, i) {
443862306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
443962306a36Sopenharmony_ci	}
444062306a36Sopenharmony_ci	/* Checks changes for upper layer. */
444162306a36Sopenharmony_ci	for_each_path(upper_base_files, path_entry, i) {
444262306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
444362306a36Sopenharmony_ci	}
444462306a36Sopenharmony_ci	/* Checks all merge accesses. */
444562306a36Sopenharmony_ci	for_each_path(merge_base_files, path_entry, i) {
444662306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
444762306a36Sopenharmony_ci	}
444862306a36Sopenharmony_ci	for_each_path(merge_base_directories, path_entry, i) {
444962306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
445062306a36Sopenharmony_ci	}
445162306a36Sopenharmony_ci	for_each_path(merge_sub_files, path_entry, i) {
445262306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
445362306a36Sopenharmony_ci	}
445462306a36Sopenharmony_ci
445562306a36Sopenharmony_ci	/* Sets rules directly on overlayed files. */
445662306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files);
445762306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
445862306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
445962306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
446062306a36Sopenharmony_ci
446162306a36Sopenharmony_ci	/* Checks unchanged accesses on lower layer. */
446262306a36Sopenharmony_ci	for_each_path(lower_sub_files, path_entry, i) {
446362306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
446462306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
446562306a36Sopenharmony_ci	}
446662306a36Sopenharmony_ci	/* Checks unchanged accesses on upper layer. */
446762306a36Sopenharmony_ci	for_each_path(upper_sub_files, path_entry, i) {
446862306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
446962306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
447062306a36Sopenharmony_ci	}
447162306a36Sopenharmony_ci	/* Checks all merge accesses. */
447262306a36Sopenharmony_ci	for_each_path(merge_base_files, path_entry, i) {
447362306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
447462306a36Sopenharmony_ci	}
447562306a36Sopenharmony_ci	for_each_path(merge_base_directories, path_entry, i) {
447662306a36Sopenharmony_ci		ASSERT_EQ(EACCES,
447762306a36Sopenharmony_ci			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
447862306a36Sopenharmony_ci	}
447962306a36Sopenharmony_ci	for_each_path(merge_sub_files, path_entry, i) {
448062306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
448162306a36Sopenharmony_ci	}
448262306a36Sopenharmony_ci
448362306a36Sopenharmony_ci	/* Only allowes access to the merge hierarchy. */
448462306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only);
448562306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
448662306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
448762306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
448862306a36Sopenharmony_ci
448962306a36Sopenharmony_ci	/* Checks new accesses on lower layer. */
449062306a36Sopenharmony_ci	for_each_path(lower_sub_files, path_entry, i) {
449162306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
449262306a36Sopenharmony_ci	}
449362306a36Sopenharmony_ci	/* Checks new accesses on upper layer. */
449462306a36Sopenharmony_ci	for_each_path(upper_sub_files, path_entry, i) {
449562306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
449662306a36Sopenharmony_ci	}
449762306a36Sopenharmony_ci	/* Checks all merge accesses. */
449862306a36Sopenharmony_ci	for_each_path(merge_base_files, path_entry, i) {
449962306a36Sopenharmony_ci		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
450062306a36Sopenharmony_ci	}
450162306a36Sopenharmony_ci	for_each_path(merge_base_directories, path_entry, i) {
450262306a36Sopenharmony_ci		ASSERT_EQ(EACCES,
450362306a36Sopenharmony_ci			  test_open(path_entry, O_RDONLY | O_DIRECTORY));
450462306a36Sopenharmony_ci	}
450562306a36Sopenharmony_ci	for_each_path(merge_sub_files, path_entry, i) {
450662306a36Sopenharmony_ci		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
450762306a36Sopenharmony_ci	}
450862306a36Sopenharmony_ci}
450962306a36Sopenharmony_ci
451062306a36Sopenharmony_ciFIXTURE(layout3_fs)
451162306a36Sopenharmony_ci{
451262306a36Sopenharmony_ci	bool has_created_dir;
451362306a36Sopenharmony_ci	bool has_created_file;
451462306a36Sopenharmony_ci	char *dir_path;
451562306a36Sopenharmony_ci	bool skip_test;
451662306a36Sopenharmony_ci};
451762306a36Sopenharmony_ci
451862306a36Sopenharmony_ciFIXTURE_VARIANT(layout3_fs)
451962306a36Sopenharmony_ci{
452062306a36Sopenharmony_ci	const struct mnt_opt mnt;
452162306a36Sopenharmony_ci	const char *const file_path;
452262306a36Sopenharmony_ci	unsigned int cwd_fs_magic;
452362306a36Sopenharmony_ci};
452462306a36Sopenharmony_ci
452562306a36Sopenharmony_ci/* clang-format off */
452662306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, tmpfs) {
452762306a36Sopenharmony_ci	/* clang-format on */
452862306a36Sopenharmony_ci	.mnt = {
452962306a36Sopenharmony_ci		.type = "tmpfs",
453062306a36Sopenharmony_ci		.data = MNT_TMP_DATA,
453162306a36Sopenharmony_ci	},
453262306a36Sopenharmony_ci	.file_path = file1_s1d1,
453362306a36Sopenharmony_ci};
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, ramfs) {
453662306a36Sopenharmony_ci	.mnt = {
453762306a36Sopenharmony_ci		.type = "ramfs",
453862306a36Sopenharmony_ci		.data = "mode=700",
453962306a36Sopenharmony_ci	},
454062306a36Sopenharmony_ci	.file_path = TMP_DIR "/dir/file",
454162306a36Sopenharmony_ci};
454262306a36Sopenharmony_ci
454362306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, cgroup2) {
454462306a36Sopenharmony_ci	.mnt = {
454562306a36Sopenharmony_ci		.type = "cgroup2",
454662306a36Sopenharmony_ci	},
454762306a36Sopenharmony_ci	.file_path = TMP_DIR "/test/cgroup.procs",
454862306a36Sopenharmony_ci};
454962306a36Sopenharmony_ci
455062306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, proc) {
455162306a36Sopenharmony_ci	.mnt = {
455262306a36Sopenharmony_ci		.type = "proc",
455362306a36Sopenharmony_ci	},
455462306a36Sopenharmony_ci	.file_path = TMP_DIR "/self/status",
455562306a36Sopenharmony_ci};
455662306a36Sopenharmony_ci
455762306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, sysfs) {
455862306a36Sopenharmony_ci	.mnt = {
455962306a36Sopenharmony_ci		.type = "sysfs",
456062306a36Sopenharmony_ci	},
456162306a36Sopenharmony_ci	.file_path = TMP_DIR "/kernel/notes",
456262306a36Sopenharmony_ci};
456362306a36Sopenharmony_ci
456462306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, hostfs) {
456562306a36Sopenharmony_ci	.mnt = {
456662306a36Sopenharmony_ci		.source = TMP_DIR,
456762306a36Sopenharmony_ci		.flags = MS_BIND,
456862306a36Sopenharmony_ci	},
456962306a36Sopenharmony_ci	.file_path = TMP_DIR "/dir/file",
457062306a36Sopenharmony_ci	.cwd_fs_magic = HOSTFS_SUPER_MAGIC,
457162306a36Sopenharmony_ci};
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_ciFIXTURE_SETUP(layout3_fs)
457462306a36Sopenharmony_ci{
457562306a36Sopenharmony_ci	struct stat statbuf;
457662306a36Sopenharmony_ci	const char *slash;
457762306a36Sopenharmony_ci	size_t dir_len;
457862306a36Sopenharmony_ci
457962306a36Sopenharmony_ci	if (!supports_filesystem(variant->mnt.type) ||
458062306a36Sopenharmony_ci	    !cwd_matches_fs(variant->cwd_fs_magic)) {
458162306a36Sopenharmony_ci		self->skip_test = true;
458262306a36Sopenharmony_ci		SKIP(return, "this filesystem is not supported (setup)");
458362306a36Sopenharmony_ci	}
458462306a36Sopenharmony_ci
458562306a36Sopenharmony_ci	slash = strrchr(variant->file_path, '/');
458662306a36Sopenharmony_ci	ASSERT_NE(slash, NULL);
458762306a36Sopenharmony_ci	dir_len = (size_t)slash - (size_t)variant->file_path;
458862306a36Sopenharmony_ci	ASSERT_LT(0, dir_len);
458962306a36Sopenharmony_ci	self->dir_path = malloc(dir_len + 1);
459062306a36Sopenharmony_ci	self->dir_path[dir_len] = '\0';
459162306a36Sopenharmony_ci	strncpy(self->dir_path, variant->file_path, dir_len);
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_ci	prepare_layout_opt(_metadata, &variant->mnt);
459462306a36Sopenharmony_ci
459562306a36Sopenharmony_ci	/* Creates directory when required. */
459662306a36Sopenharmony_ci	if (stat(self->dir_path, &statbuf)) {
459762306a36Sopenharmony_ci		set_cap(_metadata, CAP_DAC_OVERRIDE);
459862306a36Sopenharmony_ci		EXPECT_EQ(0, mkdir(self->dir_path, 0700))
459962306a36Sopenharmony_ci		{
460062306a36Sopenharmony_ci			TH_LOG("Failed to create directory \"%s\": %s",
460162306a36Sopenharmony_ci			       self->dir_path, strerror(errno));
460262306a36Sopenharmony_ci			free(self->dir_path);
460362306a36Sopenharmony_ci			self->dir_path = NULL;
460462306a36Sopenharmony_ci		}
460562306a36Sopenharmony_ci		self->has_created_dir = true;
460662306a36Sopenharmony_ci		clear_cap(_metadata, CAP_DAC_OVERRIDE);
460762306a36Sopenharmony_ci	}
460862306a36Sopenharmony_ci
460962306a36Sopenharmony_ci	/* Creates file when required. */
461062306a36Sopenharmony_ci	if (stat(variant->file_path, &statbuf)) {
461162306a36Sopenharmony_ci		int fd;
461262306a36Sopenharmony_ci
461362306a36Sopenharmony_ci		set_cap(_metadata, CAP_DAC_OVERRIDE);
461462306a36Sopenharmony_ci		fd = creat(variant->file_path, 0600);
461562306a36Sopenharmony_ci		EXPECT_LE(0, fd)
461662306a36Sopenharmony_ci		{
461762306a36Sopenharmony_ci			TH_LOG("Failed to create file \"%s\": %s",
461862306a36Sopenharmony_ci			       variant->file_path, strerror(errno));
461962306a36Sopenharmony_ci		}
462062306a36Sopenharmony_ci		EXPECT_EQ(0, close(fd));
462162306a36Sopenharmony_ci		self->has_created_file = true;
462262306a36Sopenharmony_ci		clear_cap(_metadata, CAP_DAC_OVERRIDE);
462362306a36Sopenharmony_ci	}
462462306a36Sopenharmony_ci}
462562306a36Sopenharmony_ci
462662306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout3_fs)
462762306a36Sopenharmony_ci{
462862306a36Sopenharmony_ci	if (self->skip_test)
462962306a36Sopenharmony_ci		SKIP(return, "this filesystem is not supported (teardown)");
463062306a36Sopenharmony_ci
463162306a36Sopenharmony_ci	if (self->has_created_file) {
463262306a36Sopenharmony_ci		set_cap(_metadata, CAP_DAC_OVERRIDE);
463362306a36Sopenharmony_ci		/*
463462306a36Sopenharmony_ci		 * Don't check for error because the file might already
463562306a36Sopenharmony_ci		 * have been removed (cf. release_inode test).
463662306a36Sopenharmony_ci		 */
463762306a36Sopenharmony_ci		unlink(variant->file_path);
463862306a36Sopenharmony_ci		clear_cap(_metadata, CAP_DAC_OVERRIDE);
463962306a36Sopenharmony_ci	}
464062306a36Sopenharmony_ci
464162306a36Sopenharmony_ci	if (self->has_created_dir) {
464262306a36Sopenharmony_ci		set_cap(_metadata, CAP_DAC_OVERRIDE);
464362306a36Sopenharmony_ci		/*
464462306a36Sopenharmony_ci		 * Don't check for error because the directory might already
464562306a36Sopenharmony_ci		 * have been removed (cf. release_inode test).
464662306a36Sopenharmony_ci		 */
464762306a36Sopenharmony_ci		rmdir(self->dir_path);
464862306a36Sopenharmony_ci		clear_cap(_metadata, CAP_DAC_OVERRIDE);
464962306a36Sopenharmony_ci	}
465062306a36Sopenharmony_ci	free(self->dir_path);
465162306a36Sopenharmony_ci	self->dir_path = NULL;
465262306a36Sopenharmony_ci
465362306a36Sopenharmony_ci	cleanup_layout(_metadata);
465462306a36Sopenharmony_ci}
465562306a36Sopenharmony_ci
465662306a36Sopenharmony_cistatic void layer3_fs_tag_inode(struct __test_metadata *const _metadata,
465762306a36Sopenharmony_ci				FIXTURE_DATA(layout3_fs) * self,
465862306a36Sopenharmony_ci				const FIXTURE_VARIANT(layout3_fs) * variant,
465962306a36Sopenharmony_ci				const char *const rule_path)
466062306a36Sopenharmony_ci{
466162306a36Sopenharmony_ci	const struct rule layer1_allow_read_file[] = {
466262306a36Sopenharmony_ci		{
466362306a36Sopenharmony_ci			.path = rule_path,
466462306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_FILE,
466562306a36Sopenharmony_ci		},
466662306a36Sopenharmony_ci		{},
466762306a36Sopenharmony_ci	};
466862306a36Sopenharmony_ci	const struct landlock_ruleset_attr layer2_deny_everything_attr = {
466962306a36Sopenharmony_ci		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
467062306a36Sopenharmony_ci	};
467162306a36Sopenharmony_ci	const char *const dev_null_path = "/dev/null";
467262306a36Sopenharmony_ci	int ruleset_fd;
467362306a36Sopenharmony_ci
467462306a36Sopenharmony_ci	if (self->skip_test)
467562306a36Sopenharmony_ci		SKIP(return, "this filesystem is not supported (test)");
467662306a36Sopenharmony_ci
467762306a36Sopenharmony_ci	/* Checks without Landlock. */
467862306a36Sopenharmony_ci	EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
467962306a36Sopenharmony_ci	EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
468062306a36Sopenharmony_ci
468162306a36Sopenharmony_ci	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
468262306a36Sopenharmony_ci				    layer1_allow_read_file);
468362306a36Sopenharmony_ci	EXPECT_LE(0, ruleset_fd);
468462306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
468562306a36Sopenharmony_ci	EXPECT_EQ(0, close(ruleset_fd));
468662306a36Sopenharmony_ci
468762306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
468862306a36Sopenharmony_ci	EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
468962306a36Sopenharmony_ci
469062306a36Sopenharmony_ci	/* Forbids directory reading. */
469162306a36Sopenharmony_ci	ruleset_fd =
469262306a36Sopenharmony_ci		landlock_create_ruleset(&layer2_deny_everything_attr,
469362306a36Sopenharmony_ci					sizeof(layer2_deny_everything_attr), 0);
469462306a36Sopenharmony_ci	EXPECT_LE(0, ruleset_fd);
469562306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
469662306a36Sopenharmony_ci	EXPECT_EQ(0, close(ruleset_fd));
469762306a36Sopenharmony_ci
469862306a36Sopenharmony_ci	/* Checks with Landlock and forbidden access. */
469962306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
470062306a36Sopenharmony_ci	EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
470162306a36Sopenharmony_ci}
470262306a36Sopenharmony_ci
470362306a36Sopenharmony_ci/* Matrix of tests to check file hierarchy evaluation. */
470462306a36Sopenharmony_ci
470562306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, tag_inode_dir_parent)
470662306a36Sopenharmony_ci{
470762306a36Sopenharmony_ci	/* The current directory must not be the root for this test. */
470862306a36Sopenharmony_ci	layer3_fs_tag_inode(_metadata, self, variant, ".");
470962306a36Sopenharmony_ci}
471062306a36Sopenharmony_ci
471162306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, tag_inode_dir_mnt)
471262306a36Sopenharmony_ci{
471362306a36Sopenharmony_ci	layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR);
471462306a36Sopenharmony_ci}
471562306a36Sopenharmony_ci
471662306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, tag_inode_dir_child)
471762306a36Sopenharmony_ci{
471862306a36Sopenharmony_ci	layer3_fs_tag_inode(_metadata, self, variant, self->dir_path);
471962306a36Sopenharmony_ci}
472062306a36Sopenharmony_ci
472162306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, tag_inode_file)
472262306a36Sopenharmony_ci{
472362306a36Sopenharmony_ci	layer3_fs_tag_inode(_metadata, self, variant, variant->file_path);
472462306a36Sopenharmony_ci}
472562306a36Sopenharmony_ci
472662306a36Sopenharmony_ci/* Light version of layout1.release_inodes */
472762306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, release_inodes)
472862306a36Sopenharmony_ci{
472962306a36Sopenharmony_ci	const struct rule layer1[] = {
473062306a36Sopenharmony_ci		{
473162306a36Sopenharmony_ci			.path = TMP_DIR,
473262306a36Sopenharmony_ci			.access = LANDLOCK_ACCESS_FS_READ_DIR,
473362306a36Sopenharmony_ci		},
473462306a36Sopenharmony_ci		{},
473562306a36Sopenharmony_ci	};
473662306a36Sopenharmony_ci	int ruleset_fd;
473762306a36Sopenharmony_ci
473862306a36Sopenharmony_ci	if (self->skip_test)
473962306a36Sopenharmony_ci		SKIP(return, "this filesystem is not supported (test)");
474062306a36Sopenharmony_ci
474162306a36Sopenharmony_ci	/* Clean up for the teardown to not fail. */
474262306a36Sopenharmony_ci	if (self->has_created_file)
474362306a36Sopenharmony_ci		EXPECT_EQ(0, remove_path(variant->file_path));
474462306a36Sopenharmony_ci
474562306a36Sopenharmony_ci	if (self->has_created_dir)
474662306a36Sopenharmony_ci		/* Don't check for error because of cgroup specificities. */
474762306a36Sopenharmony_ci		remove_path(self->dir_path);
474862306a36Sopenharmony_ci
474962306a36Sopenharmony_ci	ruleset_fd =
475062306a36Sopenharmony_ci		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);
475162306a36Sopenharmony_ci	ASSERT_LE(0, ruleset_fd);
475262306a36Sopenharmony_ci
475362306a36Sopenharmony_ci	/* Unmount the filesystem while it is being used by a ruleset. */
475462306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
475562306a36Sopenharmony_ci	ASSERT_EQ(0, umount(TMP_DIR));
475662306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
475762306a36Sopenharmony_ci
475862306a36Sopenharmony_ci	/* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */
475962306a36Sopenharmony_ci	set_cap(_metadata, CAP_SYS_ADMIN);
476062306a36Sopenharmony_ci	ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR));
476162306a36Sopenharmony_ci	clear_cap(_metadata, CAP_SYS_ADMIN);
476262306a36Sopenharmony_ci
476362306a36Sopenharmony_ci	enforce_ruleset(_metadata, ruleset_fd);
476462306a36Sopenharmony_ci	ASSERT_EQ(0, close(ruleset_fd));
476562306a36Sopenharmony_ci
476662306a36Sopenharmony_ci	/* Checks that access to the new mount point is denied. */
476762306a36Sopenharmony_ci	ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY));
476862306a36Sopenharmony_ci}
476962306a36Sopenharmony_ci
477062306a36Sopenharmony_ciTEST_HARNESS_MAIN
4771