1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright 2021 Google LLC
4 */
5
6 #include "test_fuse.h"
7
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include <sys/mount.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17
18 #include <linux/unistd.h>
19
20 #include <include/uapi/linux/fuse.h>
21 #include <include/uapi/linux/bpf.h>
22
23 bool user_messages;
24 bool kernel_messages;
25
display_trace(void)26 static int display_trace(void)
27 {
28 int pid = -1;
29 int tp = -1;
30 char c;
31 ssize_t bytes_read;
32 static char line[256] = {0};
33
34 if (!kernel_messages)
35 return TEST_SUCCESS;
36
37 TEST(pid = fork(), pid != -1);
38 if (pid != 0)
39 return pid;
40
41 TESTEQUAL(tracing_on(), 0);
42 TEST(tp = s_open(s_path(tracing_folder(), s("trace_pipe")),
43 O_RDONLY | O_CLOEXEC), tp != -1);
44 for (;;) {
45 TEST(bytes_read = read(tp, &c, sizeof(c)),
46 bytes_read == 1);
47 if (c == '\n') {
48 printf("%s\n", line);
49 line[0] = 0;
50 } else
51 sprintf(line + strlen(line), "%c", c);
52 }
53 out:
54 if (pid == 0) {
55 close(tp);
56 exit(TEST_FAILURE);
57 }
58 return pid;
59 }
60
fuse_opcode_to_string(int opcode)61 static const char *fuse_opcode_to_string(int opcode)
62 {
63 switch (opcode & FUSE_OPCODE_FILTER) {
64 case FUSE_LOOKUP:
65 return "FUSE_LOOKUP";
66 case FUSE_FORGET:
67 return "FUSE_FORGET";
68 case FUSE_GETATTR:
69 return "FUSE_GETATTR";
70 case FUSE_SETATTR:
71 return "FUSE_SETATTR";
72 case FUSE_READLINK:
73 return "FUSE_READLINK";
74 case FUSE_SYMLINK:
75 return "FUSE_SYMLINK";
76 case FUSE_MKNOD:
77 return "FUSE_MKNOD";
78 case FUSE_MKDIR:
79 return "FUSE_MKDIR";
80 case FUSE_UNLINK:
81 return "FUSE_UNLINK";
82 case FUSE_RMDIR:
83 return "FUSE_RMDIR";
84 case FUSE_RENAME:
85 return "FUSE_RENAME";
86 case FUSE_LINK:
87 return "FUSE_LINK";
88 case FUSE_OPEN:
89 return "FUSE_OPEN";
90 case FUSE_READ:
91 return "FUSE_READ";
92 case FUSE_WRITE:
93 return "FUSE_WRITE";
94 case FUSE_STATFS:
95 return "FUSE_STATFS";
96 case FUSE_RELEASE:
97 return "FUSE_RELEASE";
98 case FUSE_FSYNC:
99 return "FUSE_FSYNC";
100 case FUSE_SETXATTR:
101 return "FUSE_SETXATTR";
102 case FUSE_GETXATTR:
103 return "FUSE_GETXATTR";
104 case FUSE_LISTXATTR:
105 return "FUSE_LISTXATTR";
106 case FUSE_REMOVEXATTR:
107 return "FUSE_REMOVEXATTR";
108 case FUSE_FLUSH:
109 return "FUSE_FLUSH";
110 case FUSE_INIT:
111 return "FUSE_INIT";
112 case FUSE_OPENDIR:
113 return "FUSE_OPENDIR";
114 case FUSE_READDIR:
115 return "FUSE_READDIR";
116 case FUSE_RELEASEDIR:
117 return "FUSE_RELEASEDIR";
118 case FUSE_FSYNCDIR:
119 return "FUSE_FSYNCDIR";
120 case FUSE_GETLK:
121 return "FUSE_GETLK";
122 case FUSE_SETLK:
123 return "FUSE_SETLK";
124 case FUSE_SETLKW:
125 return "FUSE_SETLKW";
126 case FUSE_ACCESS:
127 return "FUSE_ACCESS";
128 case FUSE_CREATE:
129 return "FUSE_CREATE";
130 case FUSE_INTERRUPT:
131 return "FUSE_INTERRUPT";
132 case FUSE_BMAP:
133 return "FUSE_BMAP";
134 case FUSE_DESTROY:
135 return "FUSE_DESTROY";
136 case FUSE_IOCTL:
137 return "FUSE_IOCTL";
138 case FUSE_POLL:
139 return "FUSE_POLL";
140 case FUSE_NOTIFY_REPLY:
141 return "FUSE_NOTIFY_REPLY";
142 case FUSE_BATCH_FORGET:
143 return "FUSE_BATCH_FORGET";
144 case FUSE_FALLOCATE:
145 return "FUSE_FALLOCATE";
146 case FUSE_READDIRPLUS:
147 return "FUSE_READDIRPLUS";
148 case FUSE_RENAME2:
149 return "FUSE_RENAME2";
150 case FUSE_LSEEK:
151 return "FUSE_LSEEK";
152 case FUSE_COPY_FILE_RANGE:
153 return "FUSE_COPY_FILE_RANGE";
154 case FUSE_SETUPMAPPING:
155 return "FUSE_SETUPMAPPING";
156 case FUSE_REMOVEMAPPING:
157 return "FUSE_REMOVEMAPPING";
158 //case FUSE_SYNCFS:
159 // return "FUSE_SYNCFS";
160 case CUSE_INIT:
161 return "CUSE_INIT";
162 case CUSE_INIT_BSWAP_RESERVED:
163 return "CUSE_INIT_BSWAP_RESERVED";
164 case FUSE_INIT_BSWAP_RESERVED:
165 return "FUSE_INIT_BSWAP_RESERVED";
166 }
167 return "?";
168 }
169
parse_options(int argc,char * const * argv)170 static int parse_options(int argc, char *const *argv)
171 {
172 signed char c;
173
174 while ((c = getopt(argc, argv, "kuv")) != -1)
175 switch (c) {
176 case 'v':
177 test_options.verbose = true;
178 break;
179
180 case 'u':
181 user_messages = true;
182 break;
183
184 case 'k':
185 kernel_messages = true;
186 break;
187
188 default:
189 return -EINVAL;
190 }
191
192 return 0;
193 }
194
main(int argc,char * argv[])195 int main(int argc, char *argv[])
196 {
197 int result = TEST_FAILURE;
198 int trace_pid = -1;
199 char *mount_dir = NULL;
200 char *src_dir = NULL;
201 int bpf_fd = -1;
202 int src_fd = -1;
203 int fuse_dev = -1;
204 struct map_relocation *map_relocations = NULL;
205 size_t map_count = 0;
206 int i;
207
208 if (geteuid() != 0)
209 ksft_print_msg("Not a root, might fail to mount.\n");
210 TESTEQUAL(parse_options(argc, argv), 0);
211
212 TEST(trace_pid = display_trace(), trace_pid != -1);
213
214 delete_dir_tree("fd-src", true);
215 TEST(src_dir = setup_mount_dir("fd-src"), src_dir);
216 delete_dir_tree("fd-dst", true);
217 TEST(mount_dir = setup_mount_dir("fd-dst"), mount_dir);
218
219 TESTEQUAL(install_elf_bpf("fd_bpf.bpf", "test_daemon", &bpf_fd,
220 &map_relocations, &map_count), 0);
221
222 TEST(src_fd = open("fd-src", O_DIRECTORY | O_RDONLY | O_CLOEXEC),
223 src_fd != -1);
224 TESTSYSCALL(mkdirat(src_fd, "show", 0777));
225 TESTSYSCALL(mkdirat(src_fd, "hide", 0777));
226
227 for (i = 0; i < map_count; ++i)
228 if (!strcmp(map_relocations[i].name, "test_map")) {
229 uint32_t key = 23;
230 uint32_t value = 1234;
231 union bpf_attr attr = {
232 .map_fd = map_relocations[i].fd,
233 .key = ptr_to_u64(&key),
234 .value = ptr_to_u64(&value),
235 .flags = BPF_ANY,
236 };
237 TESTSYSCALL(syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM,
238 &attr, sizeof(attr)));
239 }
240
241 TESTEQUAL(mount_fuse(mount_dir, bpf_fd, src_fd, &fuse_dev), 0);
242
243 if (fork())
244 return 0;
245
246 for (;;) {
247 uint8_t bytes_in[FUSE_MIN_READ_BUFFER];
248 uint8_t bytes_out[FUSE_MIN_READ_BUFFER] __maybe_unused;
249 struct fuse_in_header *in_header =
250 (struct fuse_in_header *)bytes_in;
251 ssize_t res = read(fuse_dev, bytes_in, sizeof(bytes_in));
252
253 if (res == -1)
254 break;
255
256 switch (in_header->opcode) {
257 case FUSE_LOOKUP | FUSE_PREFILTER: {
258 char *name = (char *)(bytes_in + sizeof(*in_header));
259
260 if (user_messages)
261 printf("Lookup %s\n", name);
262 if (!strcmp(name, "hide"))
263 TESTFUSEOUTERROR(-ENOENT);
264 else
265 TESTFUSEOUTREAD(name, strlen(name) + 1);
266 break;
267 }
268 default:
269 if (user_messages) {
270 printf("opcode is %d (%s)\n", in_header->opcode,
271 fuse_opcode_to_string(
272 in_header->opcode));
273 }
274 break;
275 }
276 }
277
278 result = TEST_SUCCESS;
279
280 out:
281 for (i = 0; i < map_count; ++i) {
282 free(map_relocations[i].name);
283 close(map_relocations[i].fd);
284 }
285 free(map_relocations);
286 umount2(mount_dir, MNT_FORCE);
287 delete_dir_tree(mount_dir, true);
288 free(mount_dir);
289 delete_dir_tree(src_dir, true);
290 free(src_dir);
291 if (trace_pid != -1)
292 kill(trace_pid, SIGKILL);
293 return result;
294 }
295