• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 // Copyright (c) 2022 Google LLC
3 
4 #include "test_fuse_bpf.h"
5 
6 SEC("test_readdir_redact")
7 /* return FUSE_BPF_BACKING to use backing fs, 0 to pass to usermode */
readdir_test(struct fuse_bpf_args * fa)8 int readdir_test(struct fuse_bpf_args *fa)
9 {
10 	switch (fa->opcode) {
11 	case FUSE_READDIR | FUSE_PREFILTER: {
12 		const struct fuse_read_in *fri = fa->in_args[0].value;
13 
14 		bpf_printk("readdir %d", fri->fh);
15 		return FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
16 	}
17 
18 	case FUSE_READDIR | FUSE_POSTFILTER: {
19 		const struct fuse_read_in *fri = fa->in_args[0].value;
20 
21 		bpf_printk("readdir postfilter %x", fri->fh);
22 		return FUSE_BPF_USER_FILTER;
23 	}
24 
25 	default:
26 		bpf_printk("opcode %d", fa->opcode);
27 		return FUSE_BPF_BACKING;
28 	}
29 }
30 
31 SEC("test_partial")
32 /* return FUSE_BPF_BACKING to use backing fs, 0 to pass to usermode */
partial_test(struct fuse_bpf_args * fa)33 int partial_test(struct fuse_bpf_args *fa)
34 {
35 	switch (fa->opcode) {
36 	case FUSE_LOOKUP | FUSE_PREFILTER: {
37 		/* real and partial use backing file */
38 		const char *name = fa->in_args[0].value;
39 		bool backing = false;
40 
41 		if (strcmp(name, "real") == 0 || strcmp(name, "partial") == 0)
42 			backing = true;
43 
44 		if (strcmp(name, "dir") == 0)
45 			backing = true;
46 		if (strcmp(name, "dir2") == 0)
47 			backing = true;
48 
49 		if (strcmp(name, "file1") == 0)
50 			backing = true;
51 		if (strcmp(name, "file2") == 0)
52 			backing = true;
53 
54 		bpf_printk("lookup %s %d", name, backing);
55 		return backing ? FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER : 0;
56 	}
57 
58 	case FUSE_LOOKUP | FUSE_POSTFILTER: {
59 		const char *name = fa->in_args[0].value;
60 		struct fuse_entry_out *feo = fa->out_args[0].value;
61 
62 		if (strcmp(name, "real") == 0)
63 			feo->nodeid = 5;
64 		else if (strcmp(name, "partial") == 0)
65 			feo->nodeid = 6;
66 
67 		bpf_printk("post-lookup %s %d", name, feo->nodeid);
68 		return 0;
69 	}
70 
71 	case FUSE_ACCESS | FUSE_PREFILTER: {
72 		bpf_printk("Access: %d", fa->nodeid);
73 		return FUSE_BPF_BACKING;
74 	}
75 
76 	case FUSE_CREATE | FUSE_PREFILTER:
77 		bpf_printk("Create: %d", fa->nodeid);
78 		return FUSE_BPF_BACKING;
79 
80 	case FUSE_MKNOD | FUSE_PREFILTER: {
81 		const struct fuse_mknod_in *fmi = fa->in_args[0].value;
82 		const char *name = fa->in_args[1].value;
83 
84 		bpf_printk("mknod %s %x %x", name, fmi->rdev | fmi->mode, fmi->umask);
85 		return FUSE_BPF_BACKING;
86 	}
87 
88 	case FUSE_MKDIR | FUSE_PREFILTER: {
89 		const struct fuse_mkdir_in *fmi = fa->in_args[0].value;
90 		const char *name = fa->in_args[1].value;
91 
92 		bpf_printk("mkdir %s %x %x", name, fmi->mode, fmi->umask);
93 		return FUSE_BPF_BACKING;
94 	}
95 
96 	case FUSE_RMDIR | FUSE_PREFILTER: {
97 		const char *name = fa->in_args[0].value;
98 
99 		bpf_printk("rmdir %s", name);
100 		return FUSE_BPF_BACKING;
101 	}
102 
103 	case FUSE_RENAME | FUSE_PREFILTER: {
104 		const char *oldname = fa->in_args[1].value;
105 		const char *newname = fa->in_args[2].value;
106 
107 		bpf_printk("rename from %s", oldname);
108 		bpf_printk("rename to %s", newname);
109 		return FUSE_BPF_BACKING;
110 	}
111 
112 	case FUSE_RENAME2 | FUSE_PREFILTER: {
113 		const struct fuse_rename2_in *fri = fa->in_args[0].value;
114 		uint32_t flags = fri->flags;
115 		const char *oldname = fa->in_args[1].value;
116 		const char *newname = fa->in_args[2].value;
117 
118 		bpf_printk("rename(%x) from %s", flags, oldname);
119 		bpf_printk("rename to %s", newname);
120 		return FUSE_BPF_BACKING;
121 	}
122 
123 	case FUSE_UNLINK | FUSE_PREFILTER: {
124 		const char *name = fa->in_args[0].value;
125 
126 		bpf_printk("unlink %s", name);
127 		return FUSE_BPF_BACKING;
128 	}
129 
130 	case FUSE_LINK | FUSE_PREFILTER: {
131 		const struct fuse_link_in *fli = fa->in_args[0].value;
132 		const char *link_name = fa->in_args[1].value;
133 
134 		bpf_printk("link %d %s", fli->oldnodeid, link_name);
135 		return FUSE_BPF_BACKING;
136 	}
137 
138 	case FUSE_SYMLINK | FUSE_PREFILTER: {
139 		const char *link_name = fa->in_args[0].value;
140 		const char *link_dest = fa->in_args[1].value;
141 
142 		bpf_printk("symlink from %s", link_name);
143 		bpf_printk("symlink to %s", link_dest);
144 		return FUSE_BPF_BACKING;
145 	}
146 
147 	case FUSE_READLINK | FUSE_PREFILTER: {
148 		const char *link_name = fa->in_args[0].value;
149 
150 		bpf_printk("readlink from", link_name);
151 		return FUSE_BPF_BACKING;
152 	}
153 
154 	case FUSE_OPEN | FUSE_PREFILTER: {
155 		int backing = 0;
156 
157 		switch (fa->nodeid) {
158 		case 5:
159 			backing = FUSE_BPF_BACKING;
160 			break;
161 
162 		case 6:
163 			backing = FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
164 			break;
165 
166 		default:
167 			break;
168 		}
169 
170 		bpf_printk("open %d %d", fa->nodeid, backing);
171 		return backing;
172 	}
173 
174 	case FUSE_OPEN | FUSE_POSTFILTER:
175 		bpf_printk("open postfilter");
176 		return FUSE_BPF_USER_FILTER;
177 
178 	case FUSE_READ | FUSE_PREFILTER: {
179 		const struct fuse_read_in *fri = fa->in_args[0].value;
180 
181 		bpf_printk("read %llu %llu", fri->fh, fri->offset);
182 		if (fri->fh == 1 && fri->offset == 0)
183 			return 0;
184 		return FUSE_BPF_BACKING;
185 	}
186 
187 	case FUSE_GETATTR | FUSE_PREFILTER: {
188 		/* real and partial use backing file */
189 		int backing = 0;
190 
191 		switch (fa->nodeid) {
192 		case 1:
193 		case 5:
194 		case 6:
195 		/*
196 		 * TODO: Find better solution
197 		 * Add 100 to stop clang compiling to jump table which bpf hates
198 		 */
199 		case 100:
200 			backing = FUSE_BPF_BACKING;
201 			break;
202 		}
203 
204 		bpf_printk("getattr %d %d", fa->nodeid, backing);
205 		return backing;
206 	}
207 
208 	case FUSE_SETATTR | FUSE_PREFILTER: {
209 		/* real and partial use backing file */
210 		int backing = 0;
211 
212 		switch (fa->nodeid) {
213 		case 1:
214 		case 5:
215 		case 6:
216 		/* TODO See above */
217 		case 100:
218 			backing = FUSE_BPF_BACKING;
219 			break;
220 		}
221 
222 		bpf_printk("setattr %d %d", fa->nodeid, backing);
223 		return backing;
224 	}
225 
226 	case FUSE_OPENDIR | FUSE_PREFILTER: {
227 		int backing = 0;
228 
229 		switch (fa->nodeid) {
230 		case 1:
231 			backing = FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
232 			break;
233 		}
234 
235 		bpf_printk("opendir %d %d", fa->nodeid, backing);
236 		return backing;
237 	}
238 
239 	case FUSE_OPENDIR | FUSE_POSTFILTER: {
240 		struct fuse_open_out *foo = fa->out_args[0].value;
241 
242 		foo->fh = 2;
243 		bpf_printk("opendir postfilter");
244 		return 0;
245 	}
246 
247 	case FUSE_READDIR | FUSE_PREFILTER: {
248 		const struct fuse_read_in *fri = fa->in_args[0].value;
249 		int backing = 0;
250 
251 		if (fri->fh == 2)
252 			backing = FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
253 
254 		bpf_printk("readdir %d %d", fri->fh, backing);
255 		return backing;
256 	}
257 
258 	case FUSE_READDIR | FUSE_POSTFILTER: {
259 		const struct fuse_read_in *fri = fa->in_args[0].value;
260 		int backing = 0;
261 
262 		if (fri->fh == 2)
263 			backing = FUSE_BPF_USER_FILTER | FUSE_BPF_BACKING |
264 				  FUSE_BPF_POST_FILTER;
265 
266 		bpf_printk("readdir postfilter %d %d", fri->fh, backing);
267 		return backing;
268 	}
269 
270 	case FUSE_FLUSH | FUSE_PREFILTER: {
271 		const struct fuse_flush_in *ffi = fa->in_args[0].value;
272 
273 		bpf_printk("Flush %d", ffi->fh);
274 		return FUSE_BPF_BACKING;
275 	}
276 
277 	case FUSE_GETXATTR | FUSE_PREFILTER: {
278 		const struct fuse_flush_in *ffi = fa->in_args[0].value;
279 		const char *name = fa->in_args[1].value;
280 
281 		bpf_printk("getxattr %d %s", ffi->fh, name);
282 		return FUSE_BPF_BACKING;
283 	}
284 
285 	case FUSE_LISTXATTR | FUSE_PREFILTER: {
286 		const struct fuse_flush_in *ffi = fa->in_args[0].value;
287 		const char *name = fa->in_args[1].value;
288 
289 		bpf_printk("listxattr %d %s", ffi->fh, name);
290 		return FUSE_BPF_BACKING;
291 	}
292 
293 	case FUSE_SETXATTR | FUSE_PREFILTER: {
294 		const struct fuse_flush_in *ffi = fa->in_args[0].value;
295 		const char *name = fa->in_args[1].value;
296 		unsigned int size = fa->in_args[2].size;
297 
298 		bpf_printk("setxattr %d %s %u", ffi->fh, name, size);
299 		return FUSE_BPF_BACKING;
300 	}
301 
302 	case FUSE_REMOVEXATTR | FUSE_PREFILTER: {
303 		const char *name = fa->in_args[0].value;
304 
305 		bpf_printk("removexattr %s", name);
306 		return FUSE_BPF_BACKING;
307 	}
308 
309 	case FUSE_CANONICAL_PATH | FUSE_PREFILTER: {
310 		bpf_printk("canonical_path");
311 		return FUSE_BPF_BACKING;
312 	}
313 
314 	case FUSE_STATFS | FUSE_PREFILTER: {
315 		bpf_printk("statfs");
316 		return FUSE_BPF_BACKING;
317 	}
318 
319 	case FUSE_LSEEK | FUSE_PREFILTER: {
320 		const struct fuse_lseek_in *fli = fa->in_args[0].value;
321 
322 		bpf_printk("lseek type:%d, offset:%lld", fli->whence, fli->offset);
323 		return FUSE_BPF_BACKING;
324 	}
325 
326 	default:
327 		bpf_printk("Unknown opcode %d", fa->opcode);
328 		return 0;
329 	}
330 }
331 
332 SEC("test_trace")
333 /* return FUSE_BPF_BACKING to use backing fs, 0 to pass to usermode */
trace_test(struct fuse_bpf_args * fa)334 int trace_test(struct fuse_bpf_args *fa)
335 {
336 	switch (fa->opcode) {
337 	case FUSE_LOOKUP | FUSE_PREFILTER: {
338 		/* real and partial use backing file */
339 		const char *name = fa->in_args[0].value;
340 
341 		bpf_printk("lookup %s", name);
342 		return FUSE_BPF_BACKING;
343 	}
344 
345 	case FUSE_ACCESS | FUSE_PREFILTER: {
346 		bpf_printk("Access: %d", fa->nodeid);
347 		return FUSE_BPF_BACKING;
348 	}
349 
350 	case FUSE_CREATE | FUSE_PREFILTER:
351 		bpf_printk("Create: %d", fa->nodeid);
352 		return FUSE_BPF_BACKING;
353 
354 	case FUSE_MKNOD | FUSE_PREFILTER: {
355 		const struct fuse_mknod_in *fmi = fa->in_args[0].value;
356 		const char *name = fa->in_args[1].value;
357 
358 		bpf_printk("mknod %s %x %x", name, fmi->rdev | fmi->mode, fmi->umask);
359 		return FUSE_BPF_BACKING;
360 	}
361 
362 	case FUSE_MKDIR | FUSE_PREFILTER: {
363 		const struct fuse_mkdir_in *fmi = fa->in_args[0].value;
364 		const char *name = fa->in_args[1].value;
365 
366 		bpf_printk("mkdir %s %x %x", name, fmi->mode, fmi->umask);
367 		return FUSE_BPF_BACKING;
368 	}
369 
370 	case FUSE_RMDIR | FUSE_PREFILTER: {
371 		const char *name = fa->in_args[0].value;
372 
373 		bpf_printk("rmdir %s", name);
374 		return FUSE_BPF_BACKING;
375 	}
376 
377 	case FUSE_RENAME | FUSE_PREFILTER: {
378 		const char *oldname = fa->in_args[1].value;
379 		const char *newname = fa->in_args[2].value;
380 
381 		bpf_printk("rename from %s", oldname);
382 		bpf_printk("rename to %s", newname);
383 		return FUSE_BPF_BACKING;
384 	}
385 
386 	case FUSE_RENAME2 | FUSE_PREFILTER: {
387 		const struct fuse_rename2_in *fri = fa->in_args[0].value;
388 		uint32_t flags = fri->flags;
389 		const char *oldname = fa->in_args[1].value;
390 		const char *newname = fa->in_args[2].value;
391 
392 		bpf_printk("rename(%x) from %s", flags, oldname);
393 		bpf_printk("rename to %s", newname);
394 		return FUSE_BPF_BACKING;
395 	}
396 
397 	case FUSE_UNLINK | FUSE_PREFILTER: {
398 		const char *name = fa->in_args[0].value;
399 
400 		bpf_printk("unlink %s", name);
401 		return FUSE_BPF_BACKING;
402 	}
403 
404 	case FUSE_LINK | FUSE_PREFILTER: {
405 		const struct fuse_link_in *fli = fa->in_args[0].value;
406 		const char *link_name = fa->in_args[1].value;
407 
408 		bpf_printk("link %d %s", fli->oldnodeid, link_name);
409 		return FUSE_BPF_BACKING;
410 	}
411 
412 	case FUSE_SYMLINK | FUSE_PREFILTER: {
413 		const char *link_name = fa->in_args[0].value;
414 		const char *link_dest = fa->in_args[1].value;
415 
416 		bpf_printk("symlink from %s", link_name);
417 		bpf_printk("symlink to %s", link_dest);
418 		return FUSE_BPF_BACKING;
419 	}
420 
421 	case FUSE_READLINK | FUSE_PREFILTER: {
422 		const char *link_name = fa->in_args[0].value;
423 
424 		bpf_printk("readlink from", link_name);
425 		return FUSE_BPF_BACKING;
426 	}
427 
428 	case FUSE_OPEN | FUSE_PREFILTER: {
429 		bpf_printk("open");
430 		return FUSE_BPF_BACKING;
431 	}
432 
433 	case FUSE_OPEN | FUSE_POSTFILTER:
434 		bpf_printk("open postfilter");
435 		return FUSE_BPF_USER_FILTER;
436 
437 	case FUSE_READ | FUSE_PREFILTER: {
438 		const struct fuse_read_in *fri = fa->in_args[0].value;
439 
440 		bpf_printk("read %llu", fri->offset);
441 		return FUSE_BPF_BACKING;
442 	}
443 
444 	case FUSE_GETATTR | FUSE_PREFILTER: {
445 		bpf_printk("getattr");
446 		return FUSE_BPF_BACKING;
447 	}
448 
449 	case FUSE_SETATTR | FUSE_PREFILTER: {
450 		bpf_printk("setattr");
451 		return FUSE_BPF_BACKING;
452 	}
453 
454 	case FUSE_OPENDIR | FUSE_PREFILTER: {
455 		bpf_printk("opendir");
456 		return FUSE_BPF_BACKING;
457 	}
458 
459 	case FUSE_READDIR | FUSE_PREFILTER: {
460 		bpf_printk("readdir");
461 		return FUSE_BPF_BACKING;
462 	}
463 
464 	case FUSE_FLUSH | FUSE_PREFILTER: {
465 		bpf_printk("Flush");
466 		return FUSE_BPF_BACKING;
467 	}
468 
469 	case FUSE_GETXATTR | FUSE_PREFILTER: {
470 		const char *name = fa->in_args[1].value;
471 
472 		bpf_printk("getxattr %s", name);
473 		return FUSE_BPF_BACKING;
474 	}
475 
476 	case FUSE_LISTXATTR | FUSE_PREFILTER: {
477 		const char *name = fa->in_args[1].value;
478 
479 		bpf_printk("listxattr %s", name);
480 		return FUSE_BPF_BACKING;
481 	}
482 
483 	case FUSE_SETXATTR | FUSE_PREFILTER: {
484 		const char *name = fa->in_args[1].value;
485 		unsigned int size = fa->in_args[2].size;
486 
487 		bpf_printk("setxattr %s %u", name, size);
488 		return FUSE_BPF_BACKING;
489 	}
490 
491 	case FUSE_REMOVEXATTR | FUSE_PREFILTER: {
492 		const char *name = fa->in_args[0].value;
493 
494 		bpf_printk("removexattr %s", name);
495 		return FUSE_BPF_BACKING;
496 	}
497 
498 	case FUSE_CANONICAL_PATH | FUSE_PREFILTER: {
499 		bpf_printk("canonical_path");
500 		return FUSE_BPF_BACKING;
501 	}
502 
503 	case FUSE_STATFS | FUSE_PREFILTER: {
504 		bpf_printk("statfs");
505 		return FUSE_BPF_BACKING;
506 	}
507 
508 	case FUSE_LSEEK | FUSE_PREFILTER: {
509 		const struct fuse_lseek_in *fli = fa->in_args[0].value;
510 
511 		bpf_printk("lseek type:%d, offset:%lld", fli->whence, fli->offset);
512 		return FUSE_BPF_BACKING;
513 	}
514 
515 	default:
516 		bpf_printk("Unknown opcode %d", fa->opcode);
517 		return FUSE_BPF_BACKING;
518 	}
519 }
520 
521 SEC("test_hidden")
trace_hidden(struct fuse_bpf_args * fa)522 int trace_hidden(struct fuse_bpf_args *fa)
523 {
524 	switch (fa->opcode) {
525 	case FUSE_LOOKUP | FUSE_PREFILTER: {
526 		const char *name = fa->in_args[0].value;
527 
528 		bpf_printk("Lookup: %s", name);
529 		if (!strcmp(name, "show"))
530 			return FUSE_BPF_BACKING;
531 		if (!strcmp(name, "hide"))
532 			return -ENOENT;
533 
534 		return FUSE_BPF_BACKING;
535 	}
536 
537 	case FUSE_ACCESS | FUSE_PREFILTER: {
538 		bpf_printk("Access: %d", fa->nodeid);
539 		return FUSE_BPF_BACKING;
540 	}
541 
542 	case FUSE_CREATE | FUSE_PREFILTER:
543 		bpf_printk("Create: %d", fa->nodeid);
544 		return FUSE_BPF_BACKING;
545 
546 	case FUSE_WRITE | FUSE_PREFILTER:
547 	// TODO: Clang combines similar printk calls, causing BPF to complain
548 	//	bpf_printk("Write: %d", fa->nodeid);
549 		return FUSE_BPF_BACKING;
550 
551 	case FUSE_FLUSH | FUSE_PREFILTER: {
552 	//	const struct fuse_flush_in *ffi = fa->in_args[0].value;
553 
554 	//	bpf_printk("Flush %d", ffi->fh);
555 		return FUSE_BPF_BACKING;
556 	}
557 
558 	case FUSE_RELEASE | FUSE_PREFILTER: {
559 	//	const struct fuse_release_in *fri = fa->in_args[0].value;
560 
561 	//	bpf_printk("Release %d", fri->fh);
562 		return FUSE_BPF_BACKING;
563 	}
564 
565 	case FUSE_FALLOCATE | FUSE_PREFILTER:
566 	//	bpf_printk("fallocate %d", fa->nodeid);
567 		return FUSE_BPF_BACKING;
568 
569 	case FUSE_CANONICAL_PATH | FUSE_PREFILTER: {
570 		return FUSE_BPF_BACKING;
571 	}
572 	default:
573 		bpf_printk("Unknown opcode: %d", fa->opcode);
574 		return 0;
575 	}
576 }
577 
578 SEC("test_simple")
trace_simple(struct fuse_bpf_args * fa)579 int trace_simple(struct fuse_bpf_args *fa)
580 {
581 	if (fa->opcode & FUSE_PREFILTER)
582 		bpf_printk("prefilter opcode: %d",
583 			   fa->opcode & FUSE_OPCODE_FILTER);
584 	else if (fa->opcode & FUSE_POSTFILTER)
585 		bpf_printk("postfilter opcode: %d",
586 			   fa->opcode & FUSE_OPCODE_FILTER);
587 	else
588 		bpf_printk("*** UNKNOWN *** opcode: %d", fa->opcode);
589 	return FUSE_BPF_BACKING;
590 }
591 
592 SEC("test_passthrough")
trace_daemon(struct fuse_bpf_args * fa)593 int trace_daemon(struct fuse_bpf_args *fa)
594 {
595 	switch (fa->opcode) {
596 	case FUSE_LOOKUP | FUSE_PREFILTER: {
597 		const char *name = fa->in_args[0].value;
598 
599 		bpf_printk("Lookup prefilter: %lx %s", fa->nodeid, name);
600 		return FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
601 	}
602 
603 	case FUSE_LOOKUP | FUSE_POSTFILTER: {
604 		const char *name = fa->in_args[0].value;
605 		struct fuse_entry_bpf_out *febo = fa->out_args[1].value;
606 
607 		bpf_printk("Lookup postfilter: %lx %s %lu", fa->nodeid, name);
608 		febo->bpf_action = FUSE_ACTION_REMOVE;
609 
610 		return FUSE_BPF_USER_FILTER;
611 	}
612 
613 	default:
614 		if (fa->opcode & FUSE_PREFILTER)
615 			bpf_printk("prefilter opcode: %d",
616 				   fa->opcode & FUSE_OPCODE_FILTER);
617 		else if (fa->opcode & FUSE_POSTFILTER)
618 			bpf_printk("postfilter opcode: %d",
619 				   fa->opcode & FUSE_OPCODE_FILTER);
620 		else
621 			bpf_printk("*** UNKNOWN *** opcode: %d", fa->opcode);
622 		return FUSE_BPF_BACKING;
623 	}
624 }
625 
626 SEC("test_error")
627 /* return FUSE_BPF_BACKING to use backing fs, 0 to pass to usermode */
error_test(struct fuse_bpf_args * fa)628 int error_test(struct fuse_bpf_args *fa)
629 {
630 	switch (fa->opcode) {
631 	case FUSE_MKDIR | FUSE_PREFILTER: {
632 		bpf_printk("mkdir");
633 		return FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
634 	}
635 	case FUSE_MKDIR | FUSE_POSTFILTER: {
636 		bpf_printk("mkdir postfilter");
637 		if (fa->error_in == -EEXIST)
638 			return -EPERM;
639 
640 		return 0;
641 	}
642 
643 	case FUSE_LOOKUP | FUSE_PREFILTER: {
644 		const char *name = fa->in_args[0].value;
645 
646 		bpf_printk("lookup prefilter %s", name);
647 		return FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
648 	}
649 	case FUSE_LOOKUP | FUSE_POSTFILTER: {
650 		const char *name = fa->in_args[0].value;
651 
652 		bpf_printk("lookup postfilter %s %d", name, fa->error_in);
653 		if (strcmp(name, "doesnotexist") == 0/* && fa->error_in == -EEXIST*/) {
654 			bpf_printk("lookup postfilter doesnotexist");
655 			return FUSE_BPF_USER_FILTER;
656 		}
657 		bpf_printk("meh");
658 		return 0;
659 	}
660 
661 	default:
662 		if (fa->opcode & FUSE_PREFILTER)
663 			bpf_printk("prefilter opcode: %d",
664 				   fa->opcode & FUSE_OPCODE_FILTER);
665 		else if (fa->opcode & FUSE_POSTFILTER)
666 			bpf_printk("postfilter opcode: %d",
667 				   fa->opcode & FUSE_OPCODE_FILTER);
668 		else
669 			bpf_printk("*** UNKNOWN *** opcode: %d", fa->opcode);
670 		return FUSE_BPF_BACKING;
671 	}
672 }
673 
674 SEC("test_readdirplus")
readdirplus_test(struct fuse_bpf_args * fa)675 int readdirplus_test(struct fuse_bpf_args *fa)
676 {
677 	switch (fa->opcode) {
678 	case FUSE_READDIR | FUSE_PREFILTER: {
679 		return 0;
680 	}
681 	}
682 	return FUSE_BPF_BACKING;
683 }
684 
685 SEC("test_lookup_postfilter")
lookuppostfilter_test(struct fuse_bpf_args * fa)686 int lookuppostfilter_test(struct fuse_bpf_args *fa)
687 {
688 	switch (fa->opcode) {
689 	case FUSE_LOOKUP | FUSE_PREFILTER:
690 		return FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
691 	case FUSE_LOOKUP | FUSE_POSTFILTER:
692 		return FUSE_BPF_USER_FILTER;
693 	default:
694 		return FUSE_BPF_BACKING;
695 	}
696 }
697 
698 SEC("test_create_remove")
createremovebpf_test(struct fuse_bpf_args * fa)699 int createremovebpf_test(struct fuse_bpf_args *fa)
700 {
701 	switch (fa->opcode) {
702 	case FUSE_LOOKUP | FUSE_PREFILTER: {
703 		return FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
704 	}
705 
706 	case FUSE_LOOKUP | FUSE_POSTFILTER: {
707 		struct fuse_entry_bpf_out *febo = fa->out_args[1].value;
708 
709 		febo->bpf_action = FUSE_ACTION_REMOVE;
710 		return 0;
711 	}
712 
713 	case FUSE_OPEN | FUSE_PREFILTER: {
714 		return -EIO;
715 	}
716 
717 	default:
718 		return FUSE_BPF_BACKING;
719 	}
720 }
721 
722 SEC("test_mkdir_remove")
mkdirremovebpf_test(struct fuse_bpf_args * fa)723 int mkdirremovebpf_test(struct fuse_bpf_args *fa)
724 {
725 	switch (fa->opcode) {
726 	case FUSE_LOOKUP | FUSE_PREFILTER: {
727 		return FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
728 	}
729 
730 	case FUSE_LOOKUP | FUSE_POSTFILTER: {
731 		struct fuse_entry_bpf_out *febo = fa->out_args[1].value;
732 
733 		febo->bpf_action = FUSE_ACTION_REMOVE;
734 		return 0;
735 	}
736 
737 	case FUSE_OPENDIR | FUSE_PREFILTER: {
738 		return -EIO;
739 	}
740 
741 	default:
742 		return FUSE_BPF_BACKING;
743 	}
744 }
745