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 
14 static 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 
34 static 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 
139 out:
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 
149 void 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