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;
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 	/* set pinning path of other map and re-pin all */
129 	map = bpf_object__find_map_by_name(obj, "nopinmap");
130 	if (CHECK(!map, "find map", "NULL map"))
131 		goto out;
132 
133 	err = bpf_map__set_pin_path(map, custpinpath);
134 	if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
135 		goto out;
136 
137 	/* should only pin the one unpinned map */
138 	err = bpf_object__pin_maps(obj, NULL);
139 	if (CHECK(err, "pin maps", "err %d errno %d\n", err, errno))
140 		goto out;
141 
142 	/* check that nopinmap was pinned at the custom path */
143 	err = stat(custpinpath, &statbuf);
144 	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
145 		goto out;
146 
147 	/* remove the custom pin path to re-test it with auto-pinning below */
148 	err = unlink(custpinpath);
149 	if (CHECK(err, "unlink custpinpath", "err %d errno %d\n", err, errno))
150 		goto out;
151 
152 	err = rmdir(custpath);
153 	if (CHECK(err, "rmdir custpindir", "err %d errno %d\n", err, errno))
154 		goto out;
155 
156 	bpf_object__close(obj);
157 
158 	/* open the valid object file again */
159 	obj = bpf_object__open_file(file, NULL);
160 	err = libbpf_get_error(obj);
161 	if (CHECK(err, "default open", "err %d errno %d\n", err, errno)) {
162 		obj = NULL;
163 		goto out;
164 	}
165 
166 	/* set pin paths so that nopinmap2 will attempt to reuse the map at
167 	 * pinpath (which will fail), but not before pinmap has already been
168 	 * reused
169 	 */
170 	bpf_object__for_each_map(map, obj) {
171 		if (!strcmp(bpf_map__name(map), "nopinmap"))
172 			err = bpf_map__set_pin_path(map, nopinpath2);
173 		else if (!strcmp(bpf_map__name(map), "nopinmap2"))
174 			err = bpf_map__set_pin_path(map, pinpath);
175 		else
176 			continue;
177 
178 		if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
179 			goto out;
180 	}
181 
182 	/* should fail because of map parameter mismatch */
183 	err = bpf_object__load(obj);
184 	if (CHECK(err != -EINVAL, "param mismatch load", "err %d errno %d\n", err, errno))
185 		goto out;
186 
187 	/* nopinmap2 should have been pinned and cleaned up again */
188 	err = stat(nopinpath2, &statbuf);
189 	if (CHECK(!err || errno != ENOENT, "stat nopinpath2",
190 		  "err %d errno %d\n", err, errno))
191 		goto out;
192 
193 	/* pinmap should still be there */
194 	err = stat(pinpath, &statbuf);
195 	if (CHECK(err, "stat pinpath", "err %d errno %d\n", err, errno))
196 		goto out;
197 
198 	bpf_object__close(obj);
199 
200 	/* test auto-pinning at custom path with open opt */
201 	obj = bpf_object__open_file(file, &opts);
202 	if (CHECK_FAIL(libbpf_get_error(obj))) {
203 		obj = NULL;
204 		goto out;
205 	}
206 
207 	err = bpf_object__load(obj);
208 	if (CHECK(err, "custom load", "err %d errno %d\n", err, errno))
209 		goto out;
210 
211 	/* check that pinmap was pinned at the custom path */
212 	err = stat(custpinpath, &statbuf);
213 	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
214 		goto out;
215 
216 out:
217 	unlink(pinpath);
218 	unlink(nopinpath);
219 	unlink(nopinpath2);
220 	unlink(custpinpath);
221 	rmdir(custpath);
222 	if (obj)
223 		bpf_object__close(obj);
224 }
225