1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <test_progs.h>
7 
8 __u32 get_map_id(struct bpf_object *obj, const char *name)
9 {
10 	struct bpf_map_info map_info = {};
11 	__u32 map_info_len, duration = 0;
12 	struct bpf_map *map;
13 	int err;
14 
15 	map_info_len = sizeof(map_info);
16 
17 	map = bpf_object__find_map_by_name(obj, name);
18 	if (CHECK(!map, "find map", "NULL map"))
19 		return 0;
20 
21 	err = bpf_obj_get_info_by_fd(bpf_map__fd(map),
22 				     &map_info, &map_info_len);
23 	CHECK(err, "get map info", "err %d errno %d", err, errno);
24 	return map_info.id;
25 }
26 
27 void test_pinning(void)
28 {
29 	const char *file_invalid = "./test_pinning_invalid.o";
30 	const char *custpinpath = "/sys/fs/bpf/custom/pinmap";
31 	const char *nopinpath = "/sys/fs/bpf/nopinmap";
32 	const char *nopinpath2 = "/sys/fs/bpf/nopinmap2";
33 	const char *custpath = "/sys/fs/bpf/custom";
34 	const char *pinpath = "/sys/fs/bpf/pinmap";
35 	const char *file = "./test_pinning.o";
36 	__u32 map_id, map_id2, duration = 0;
37 	struct stat statbuf = {};
38 	struct bpf_object *obj;
39 	struct bpf_map *map;
40 	int err, map_fd;
41 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
42 		.pin_root_path = custpath,
43 	);
44 
45 	/* check that opening fails with invalid pinning value in map def */
46 	obj = bpf_object__open_file(file_invalid, NULL);
47 	err = libbpf_get_error(obj);
48 	if (CHECK(err != -EINVAL, "invalid open", "err %d errno %d\n", err, errno)) {
49 		obj = NULL;
50 		goto out;
51 	}
52 
53 	/* open the valid object file  */
54 	obj = bpf_object__open_file(file, NULL);
55 	err = libbpf_get_error(obj);
56 	if (CHECK(err, "default open", "err %d errno %d\n", err, errno)) {
57 		obj = NULL;
58 		goto out;
59 	}
60 
61 	err = bpf_object__load(obj);
62 	if (CHECK(err, "default load", "err %d errno %d\n", err, errno))
63 		goto out;
64 
65 	/* check that pinmap was pinned */
66 	err = stat(pinpath, &statbuf);
67 	if (CHECK(err, "stat pinpath", "err %d errno %d\n", err, errno))
68 		goto out;
69 
70 	/* check that nopinmap was *not* pinned */
71 	err = stat(nopinpath, &statbuf);
72 	if (CHECK(!err || errno != ENOENT, "stat nopinpath",
73 		  "err %d errno %d\n", err, errno))
74 		goto out;
75 
76 	/* check that nopinmap2 was *not* pinned */
77 	err = stat(nopinpath2, &statbuf);
78 	if (CHECK(!err || errno != ENOENT, "stat nopinpath2",
79 		  "err %d errno %d\n", err, errno))
80 		goto out;
81 
82 	map_id = get_map_id(obj, "pinmap");
83 	if (!map_id)
84 		goto out;
85 
86 	bpf_object__close(obj);
87 
88 	obj = bpf_object__open_file(file, NULL);
89 	if (CHECK_FAIL(libbpf_get_error(obj))) {
90 		obj = NULL;
91 		goto out;
92 	}
93 
94 	err = bpf_object__load(obj);
95 	if (CHECK(err, "default load", "err %d errno %d\n", err, errno))
96 		goto out;
97 
98 	/* check that same map ID was reused for second load */
99 	map_id2 = get_map_id(obj, "pinmap");
100 	if (CHECK(map_id != map_id2, "check reuse",
101 		  "err %d errno %d id %d id2 %d\n", err, errno, map_id, map_id2))
102 		goto out;
103 
104 	/* should be no-op to re-pin same map */
105 	map = bpf_object__find_map_by_name(obj, "pinmap");
106 	if (CHECK(!map, "find map", "NULL map"))
107 		goto out;
108 
109 	err = bpf_map__pin(map, NULL);
110 	if (CHECK(err, "re-pin map", "err %d errno %d\n", err, errno))
111 		goto out;
112 
113 	/* but error to pin at different location */
114 	err = bpf_map__pin(map, "/sys/fs/bpf/other");
115 	if (CHECK(!err, "pin map different", "err %d errno %d\n", err, errno))
116 		goto out;
117 
118 	/* unpin maps with a pin_path set */
119 	err = bpf_object__unpin_maps(obj, NULL);
120 	if (CHECK(err, "unpin maps", "err %d errno %d\n", err, errno))
121 		goto out;
122 
123 	/* and re-pin them... */
124 	err = bpf_object__pin_maps(obj, NULL);
125 	if (CHECK(err, "pin maps", "err %d errno %d\n", err, errno))
126 		goto out;
127 
128 	/* get pinning path */
129 	if (!ASSERT_STREQ(bpf_map__pin_path(map), pinpath, "get pin path"))
130 		goto out;
131 
132 	/* set pinning path of other map and re-pin all */
133 	map = bpf_object__find_map_by_name(obj, "nopinmap");
134 	if (CHECK(!map, "find map", "NULL map"))
135 		goto out;
136 
137 	err = bpf_map__set_pin_path(map, custpinpath);
138 	if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
139 		goto out;
140 
141 	/* get pinning path after set */
142 	if (!ASSERT_STREQ(bpf_map__pin_path(map), custpinpath,
143 			  "get pin path after set"))
144 		goto out;
145 
146 	/* should only pin the one unpinned map */
147 	err = bpf_object__pin_maps(obj, NULL);
148 	if (CHECK(err, "pin maps", "err %d errno %d\n", err, errno))
149 		goto out;
150 
151 	/* check that nopinmap was pinned at the custom path */
152 	err = stat(custpinpath, &statbuf);
153 	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
154 		goto out;
155 
156 	/* remove the custom pin path to re-test it with auto-pinning below */
157 	err = unlink(custpinpath);
158 	if (CHECK(err, "unlink custpinpath", "err %d errno %d\n", err, errno))
159 		goto out;
160 
161 	err = rmdir(custpath);
162 	if (CHECK(err, "rmdir custpindir", "err %d errno %d\n", err, errno))
163 		goto out;
164 
165 	bpf_object__close(obj);
166 
167 	/* open the valid object file again */
168 	obj = bpf_object__open_file(file, NULL);
169 	err = libbpf_get_error(obj);
170 	if (CHECK(err, "default open", "err %d errno %d\n", err, errno)) {
171 		obj = NULL;
172 		goto out;
173 	}
174 
175 	/* set pin paths so that nopinmap2 will attempt to reuse the map at
176 	 * pinpath (which will fail), but not before pinmap has already been
177 	 * reused
178 	 */
179 	bpf_object__for_each_map(map, obj) {
180 		if (!strcmp(bpf_map__name(map), "nopinmap"))
181 			err = bpf_map__set_pin_path(map, nopinpath2);
182 		else if (!strcmp(bpf_map__name(map), "nopinmap2"))
183 			err = bpf_map__set_pin_path(map, pinpath);
184 		else
185 			continue;
186 
187 		if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
188 			goto out;
189 	}
190 
191 	/* should fail because of map parameter mismatch */
192 	err = bpf_object__load(obj);
193 	if (CHECK(err != -EINVAL, "param mismatch load", "err %d errno %d\n", err, errno))
194 		goto out;
195 
196 	/* nopinmap2 should have been pinned and cleaned up again */
197 	err = stat(nopinpath2, &statbuf);
198 	if (CHECK(!err || errno != ENOENT, "stat nopinpath2",
199 		  "err %d errno %d\n", err, errno))
200 		goto out;
201 
202 	/* pinmap should still be there */
203 	err = stat(pinpath, &statbuf);
204 	if (CHECK(err, "stat pinpath", "err %d errno %d\n", err, errno))
205 		goto out;
206 
207 	bpf_object__close(obj);
208 
209 	/* test auto-pinning at custom path with open opt */
210 	obj = bpf_object__open_file(file, &opts);
211 	if (CHECK_FAIL(libbpf_get_error(obj))) {
212 		obj = NULL;
213 		goto out;
214 	}
215 
216 	err = bpf_object__load(obj);
217 	if (CHECK(err, "custom load", "err %d errno %d\n", err, errno))
218 		goto out;
219 
220 	/* check that pinmap was pinned at the custom path */
221 	err = stat(custpinpath, &statbuf);
222 	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
223 		goto out;
224 
225 	/* remove the custom pin path to re-test it with reuse fd below */
226 	err = unlink(custpinpath);
227 	if (CHECK(err, "unlink custpinpath", "err %d errno %d\n", err, errno))
228 		goto out;
229 
230 	err = rmdir(custpath);
231 	if (CHECK(err, "rmdir custpindir", "err %d errno %d\n", err, errno))
232 		goto out;
233 
234 	bpf_object__close(obj);
235 
236 	/* test pinning at custom path with reuse fd */
237 	obj = bpf_object__open_file(file, NULL);
238 	err = libbpf_get_error(obj);
239 	if (CHECK(err, "default open", "err %d errno %d\n", err, errno)) {
240 		obj = NULL;
241 		goto out;
242 	}
243 
244 	map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(__u32),
245 				sizeof(__u64), 1, 0);
246 	if (CHECK(map_fd < 0, "create pinmap manually", "fd %d\n", map_fd))
247 		goto out;
248 
249 	map = bpf_object__find_map_by_name(obj, "pinmap");
250 	if (CHECK(!map, "find map", "NULL map"))
251 		goto close_map_fd;
252 
253 	err = bpf_map__reuse_fd(map, map_fd);
254 	if (CHECK(err, "reuse pinmap fd", "err %d errno %d\n", err, errno))
255 		goto close_map_fd;
256 
257 	err = bpf_map__set_pin_path(map, custpinpath);
258 	if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
259 		goto close_map_fd;
260 
261 	err = bpf_object__load(obj);
262 	if (CHECK(err, "custom load", "err %d errno %d\n", err, errno))
263 		goto close_map_fd;
264 
265 	/* check that pinmap was pinned at the custom path */
266 	err = stat(custpinpath, &statbuf);
267 	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
268 		goto close_map_fd;
269 
270 close_map_fd:
271 	close(map_fd);
272 out:
273 	unlink(pinpath);
274 	unlink(nopinpath);
275 	unlink(nopinpath2);
276 	unlink(custpinpath);
277 	rmdir(custpath);
278 	if (obj)
279 		bpf_object__close(obj);
280 }
281