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