1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2020 Facebook */
3#define _GNU_SOURCE
4#include <stdio.h>
5#include <sched.h>
6#include <sys/mount.h>
7#include <sys/stat.h>
8#include <sys/types.h>
9#include <test_progs.h>
10
11/* TDIR must be in a location we can create a directory in. */
12#define TDIR "/tmp/test_bpffs_testdir"
13
14static int read_iter(char *file)
15{
16	/* 1024 should be enough to get contiguous 4 "iter" letters at some point */
17	char buf[1024];
18	int fd, len;
19
20	fd = open(file, 0);
21	if (fd < 0)
22		return -1;
23	while ((len = read(fd, buf, sizeof(buf))) > 0) {
24		buf[sizeof(buf) - 1] = '\0';
25		if (strstr(buf, "iter")) {
26			close(fd);
27			return 0;
28		}
29	}
30	close(fd);
31	return -1;
32}
33
34static int fn(void)
35{
36	struct stat a, b, c;
37	int err, map;
38
39	err = unshare(CLONE_NEWNS);
40	if (!ASSERT_OK(err, "unshare"))
41		goto out;
42
43	err = mount("", "/", "", MS_REC | MS_PRIVATE, NULL);
44	if (!ASSERT_OK(err, "mount /"))
45		goto out;
46
47	err =  mkdir(TDIR, 0777);
48	/* If the directory already exists we can carry on. It may be left over
49	 * from a previous run.
50	 */
51	if ((err && errno != EEXIST) && !ASSERT_OK(err, "mkdir " TDIR))
52		goto out;
53
54	err = mount("none", TDIR, "tmpfs", 0, NULL);
55	if (!ASSERT_OK(err, "mount tmpfs"))
56		goto out;
57
58	err = mkdir(TDIR "/fs1", 0777);
59	if (!ASSERT_OK(err, "mkdir " TDIR "/fs1"))
60		goto out;
61	err = mkdir(TDIR "/fs2", 0777);
62	if (!ASSERT_OK(err, "mkdir " TDIR "/fs2"))
63		goto out;
64
65	err = mount("bpf", TDIR "/fs1", "bpf", 0, NULL);
66	if (!ASSERT_OK(err, "mount bpffs " TDIR "/fs1"))
67		goto out;
68	err = mount("bpf", TDIR "/fs2", "bpf", 0, NULL);
69	if (!ASSERT_OK(err, "mount bpffs " TDIR "/fs2"))
70		goto out;
71
72	err = read_iter(TDIR "/fs1/maps.debug");
73	if (!ASSERT_OK(err, "reading " TDIR "/fs1/maps.debug"))
74		goto out;
75	err = read_iter(TDIR "/fs2/progs.debug");
76	if (!ASSERT_OK(err, "reading " TDIR "/fs2/progs.debug"))
77		goto out;
78
79	err = mkdir(TDIR "/fs1/a", 0777);
80	if (!ASSERT_OK(err, "creating " TDIR "/fs1/a"))
81		goto out;
82	err = mkdir(TDIR "/fs1/a/1", 0777);
83	if (!ASSERT_OK(err, "creating " TDIR "/fs1/a/1"))
84		goto out;
85	err = mkdir(TDIR "/fs1/b", 0777);
86	if (!ASSERT_OK(err, "creating " TDIR "/fs1/b"))
87		goto out;
88
89	map = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 4, 1, NULL);
90	if (!ASSERT_GT(map, 0, "create_map(ARRAY)"))
91		goto out;
92	err = bpf_obj_pin(map, TDIR "/fs1/c");
93	if (!ASSERT_OK(err, "pin map"))
94		goto out;
95	close(map);
96
97	/* Check that RENAME_EXCHANGE works for directories. */
98	err = stat(TDIR "/fs1/a", &a);
99	if (!ASSERT_OK(err, "stat(" TDIR "/fs1/a)"))
100		goto out;
101	err = renameat2(0, TDIR "/fs1/a", 0, TDIR "/fs1/b", RENAME_EXCHANGE);
102	if (!ASSERT_OK(err, "renameat2(/fs1/a, /fs1/b, RENAME_EXCHANGE)"))
103		goto out;
104	err = stat(TDIR "/fs1/b", &b);
105	if (!ASSERT_OK(err, "stat(" TDIR "/fs1/b)"))
106		goto out;
107	if (!ASSERT_EQ(a.st_ino, b.st_ino, "b should have a's inode"))
108		goto out;
109	err = access(TDIR "/fs1/b/1", F_OK);
110	if (!ASSERT_OK(err, "access(" TDIR "/fs1/b/1)"))
111		goto out;
112
113	/* Check that RENAME_EXCHANGE works for mixed file types. */
114	err = stat(TDIR "/fs1/c", &c);
115	if (!ASSERT_OK(err, "stat(" TDIR "/fs1/map)"))
116		goto out;
117	err = renameat2(0, TDIR "/fs1/c", 0, TDIR "/fs1/b", RENAME_EXCHANGE);
118	if (!ASSERT_OK(err, "renameat2(/fs1/c, /fs1/b, RENAME_EXCHANGE)"))
119		goto out;
120	err = stat(TDIR "/fs1/b", &b);
121	if (!ASSERT_OK(err, "stat(" TDIR "/fs1/b)"))
122		goto out;
123	if (!ASSERT_EQ(c.st_ino, b.st_ino, "b should have c's inode"))
124		goto out;
125	err = access(TDIR "/fs1/c/1", F_OK);
126	if (!ASSERT_OK(err, "access(" TDIR "/fs1/c/1)"))
127		goto out;
128
129	/* Check that RENAME_NOREPLACE works. */
130	err = renameat2(0, TDIR "/fs1/b", 0, TDIR "/fs1/a", RENAME_NOREPLACE);
131	if (!ASSERT_ERR(err, "renameat2(RENAME_NOREPLACE)")) {
132		err = -EINVAL;
133		goto out;
134	}
135	err = access(TDIR "/fs1/b", F_OK);
136	if (!ASSERT_OK(err, "access(" TDIR "/fs1/b)"))
137		goto out;
138
139out:
140	umount(TDIR "/fs1");
141	umount(TDIR "/fs2");
142	rmdir(TDIR "/fs1");
143	rmdir(TDIR "/fs2");
144	umount(TDIR);
145	rmdir(TDIR);
146	exit(err);
147}
148
149void test_test_bpffs(void)
150{
151	int err, duration = 0, status = 0;
152	pid_t pid;
153
154	pid = fork();
155	if (CHECK(pid == -1, "clone", "clone failed %d", errno))
156		return;
157	if (pid == 0)
158		fn();
159	err = waitpid(pid, &status, 0);
160	if (CHECK(err == -1 && errno != ECHILD, "waitpid", "failed %d", errno))
161		return;
162	if (CHECK(WEXITSTATUS(status), "bpffs test ", "failed %d", WEXITSTATUS(status)))
163		return;
164}
165