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