• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Copyright (c) 2021 Google LLC
4  */
5 
6 #ifndef TEST_FUSE__H
7 #define TEST_FUSE__H
8 
9 #define _GNU_SOURCE
10 
11 #include "test_framework.h"
12 
13 #include <dirent.h>
14 #include <sys/stat.h>
15 #include <sys/statfs.h>
16 #include <sys/types.h>
17 
18 #include <include/uapi/linux/fuse.h>
19 
20 #define PAGE_SIZE 4096
21 #define FUSE_POSTFILTER 0x20000
22 
23 extern struct _test_options test_options;
24 
25 /* Slow but semantically easy string functions */
26 
27 /*
28  * struct s just wraps a char pointer
29  * It is a pointer to a malloc'd string, or null
30  * All consumers handle null input correctly
31  * All consumers free the string
32  */
33 struct s {
34 	char *s;
35 };
36 
37 struct s s(const char *s1);
38 struct s sn(const char *s1, const char *s2);
39 int s_cmp(struct s s1, struct s s2);
40 struct s s_cat(struct s s1, struct s s2);
41 struct s s_splitleft(struct s s1, char c);
42 struct s s_splitright(struct s s1, char c);
43 struct s s_word(struct s s1, char c, size_t n);
44 struct s s_path(struct s s1, struct s s2);
45 struct s s_pathn(size_t n, struct s s1, ...);
46 int s_link(struct s src_pathname, struct s dst_pathname);
47 int s_symlink(struct s src_pathname, struct s dst_pathname);
48 int s_mkdir(struct s pathname, mode_t mode);
49 int s_rmdir(struct s pathname);
50 int s_unlink(struct s pathname);
51 int s_open(struct s pathname, int flags, ...);
52 int s_openat(int dirfd, struct s pathname, int flags, ...);
53 int s_creat(struct s pathname, mode_t mode);
54 int s_mkfifo(struct s pathname, mode_t mode);
55 int s_stat(struct s pathname, struct stat *st);
56 int s_statfs(struct s pathname, struct statfs *st);
57 int s_fuse_attr(struct s pathname, struct fuse_attr *fuse_attr_out);
58 DIR *s_opendir(struct s pathname);
59 int s_getxattr(struct s pathname, const char name[], void *value, size_t size,
60 	       ssize_t *ret_size);
61 int s_listxattr(struct s pathname, void *list, size_t size, ssize_t *ret_size);
62 int s_setxattr(struct s pathname, const char name[], const void *value,
63 	       size_t size, int flags);
64 int s_removexattr(struct s pathname, const char name[]);
65 int s_rename(struct s oldpathname, struct s newpathname);
66 int s_mount(struct s source, struct s target, struct s filesystem,
67 	    unsigned long mountflags, struct s data);
68 int s_umount(struct s target);
69 
70 struct s tracing_folder(void);
71 int tracing_on(void);
72 
73 char *concat_file_name(const char *dir, const char *file);
74 char *setup_mount_dir(const char *name);
75 int delete_dir_tree(const char *dir_path, bool remove_root);
76 
77 #define TESTFUSEINNULL(_opcode)						\
78 	do {								\
79 		struct fuse_in_header *in_header =			\
80 				(struct fuse_in_header *)bytes_in;	\
81 		ssize_t res = read(fuse_dev, &bytes_in,			\
82 			sizeof(bytes_in));				\
83 									\
84 		TESTEQUAL(in_header->opcode, _opcode);			\
85 		TESTEQUAL(res, sizeof(*in_header));			\
86 	} while (false)
87 
88 #define TESTFUSEIN(_opcode, in_struct)					\
89 	do {								\
90 		struct fuse_in_header *in_header =			\
91 				(struct fuse_in_header *)bytes_in;	\
92 		ssize_t res = read(fuse_dev, &bytes_in,			\
93 			sizeof(bytes_in));				\
94 									\
95 		TESTEQUAL(in_header->opcode, _opcode);			\
96 		TESTEQUAL(res, sizeof(*in_header) + sizeof(*in_struct));\
97 	} while (false)
98 
99 #define TESTFUSEIN2(_opcode, in_struct1, in_struct2)			\
100 	do {								\
101 		struct fuse_in_header *in_header =			\
102 				(struct fuse_in_header *)bytes_in;	\
103 		ssize_t res = read(fuse_dev, &bytes_in,			\
104 			sizeof(bytes_in));				\
105 									\
106 		TESTEQUAL(in_header->opcode, _opcode);			\
107 		TESTEQUAL(res, sizeof(*in_header) + sizeof(*in_struct1) \
108 						+ sizeof(*in_struct2)); \
109 		in_struct1 = (void *)(bytes_in + sizeof(*in_header));	\
110 		in_struct2 = (void *)(bytes_in + sizeof(*in_header)	\
111 				      + sizeof(*in_struct1));		\
112 	} while (false)
113 
114 #define TESTFUSEINEXT(_opcode, in_struct, extra)			\
115 	do {								\
116 		struct fuse_in_header *in_header =			\
117 				(struct fuse_in_header *)bytes_in;	\
118 		ssize_t res = read(fuse_dev, &bytes_in,			\
119 			sizeof(bytes_in));				\
120 									\
121 		TESTEQUAL(in_header->opcode, _opcode);			\
122 		TESTEQUAL(res,						\
123 		       sizeof(*in_header) + sizeof(*in_struct) + extra);\
124 	} while (false)
125 
126 #define TESTFUSEINUNKNOWN()						\
127 	do {								\
128 		struct fuse_in_header *in_header =			\
129 				(struct fuse_in_header *)bytes_in;	\
130 		ssize_t res = read(fuse_dev, &bytes_in,			\
131 			sizeof(bytes_in));				\
132 									\
133 		TESTGE(res, sizeof(*in_header));			\
134 		TESTEQUAL(in_header->opcode, -1);			\
135 	} while (false)
136 
137 /* Special case lookup since it is asymmetric */
138 #define TESTFUSELOOKUP(expected, filter)				\
139 	do {								\
140 		struct fuse_in_header *in_header =			\
141 				(struct fuse_in_header *)bytes_in;	\
142 		char *name = (char *) (bytes_in + sizeof(*in_header));	\
143 		ssize_t res;						\
144 									\
145 		TEST(res = read(fuse_dev, &bytes_in, sizeof(bytes_in)),	\
146 			  res != -1);					\
147 		/* TODO once we handle forgets properly, remove */	\
148 		if (in_header->opcode == FUSE_FORGET)			\
149 			continue;					\
150 		if (in_header->opcode == FUSE_BATCH_FORGET)		\
151 			continue;					\
152 		TESTGE(res, sizeof(*in_header));			\
153 		TESTEQUAL(in_header->opcode,				\
154 			FUSE_LOOKUP | filter);				\
155 		TESTEQUAL(res,						\
156 			  sizeof(*in_header) + strlen(expected) + 1 +	\
157 				(filter == FUSE_POSTFILTER ?		\
158 				sizeof(struct fuse_entry_out) +		\
159 				sizeof(struct fuse_entry_bpf_out) : 0));\
160 		TESTCOND(!strcmp(name, expected));			\
161 		break;							\
162 	} while (true)
163 
164 #define TESTFUSEOUTEMPTY()						\
165 	do {								\
166 		struct fuse_in_header *in_header =			\
167 				(struct fuse_in_header *)bytes_in;	\
168 		struct fuse_out_header *out_header =			\
169 			(struct fuse_out_header *)bytes_out;		\
170 									\
171 		*out_header = (struct fuse_out_header) {		\
172 			.len = sizeof(*out_header),			\
173 			.unique = in_header->unique,			\
174 		};							\
175 		TESTEQUAL(write(fuse_dev, bytes_out, out_header->len),	\
176 			  out_header->len);				\
177 	} while (false)
178 
179 #define TESTFUSEOUTERROR(errno)						\
180 	do {								\
181 		struct fuse_in_header *in_header =			\
182 				(struct fuse_in_header *)bytes_in;	\
183 		struct fuse_out_header *out_header =			\
184 			(struct fuse_out_header *)bytes_out;		\
185 									\
186 		*out_header = (struct fuse_out_header) {		\
187 			.len = sizeof(*out_header),			\
188 			.error = errno,					\
189 			.unique = in_header->unique,			\
190 		};							\
191 		TESTEQUAL(write(fuse_dev, bytes_out, out_header->len),	\
192 			  out_header->len);				\
193 	} while (false)
194 
195 #define TESTFUSEOUTREAD(data, length)					\
196 	do {								\
197 		struct fuse_in_header *in_header =			\
198 				(struct fuse_in_header *)bytes_in;	\
199 		struct fuse_out_header *out_header =			\
200 			(struct fuse_out_header *)bytes_out;		\
201 									\
202 		*out_header = (struct fuse_out_header) {		\
203 			.len = sizeof(*out_header) + length,		\
204 			.unique = in_header->unique,			\
205 		};							\
206 		memcpy(bytes_out + sizeof(*out_header), data, length);	\
207 		TESTEQUAL(write(fuse_dev, bytes_out, out_header->len),	\
208 			  out_header->len);				\
209 	} while (false)
210 
211 #define TESTFUSEDIROUTREAD(read_out, data, length)			\
212 	do {								\
213 		struct fuse_in_header *in_header =			\
214 				(struct fuse_in_header *)bytes_in;	\
215 		struct fuse_out_header *out_header =			\
216 			(struct fuse_out_header *)bytes_out;		\
217 									\
218 		*out_header = (struct fuse_out_header) {		\
219 			.len = sizeof(*out_header) +			\
220 			       sizeof(*read_out) + length,		\
221 			.unique = in_header->unique,			\
222 		};							\
223 		memcpy(bytes_out + sizeof(*out_header) +		\
224 				sizeof(*read_out), data, length);	\
225 		memcpy(bytes_out + sizeof(*out_header),			\
226 				read_out, sizeof(*read_out));		\
227 		TESTEQUAL(write(fuse_dev, bytes_out, out_header->len),	\
228 			  out_header->len);				\
229 	} while (false)
230 
231 #define TESTFUSEOUT1(type1, obj1)					\
232 	do {								\
233 		*(struct fuse_out_header *) bytes_out			\
234 			= (struct fuse_out_header) {			\
235 			.len = sizeof(struct fuse_out_header)		\
236 				+ sizeof(struct type1),			\
237 			.unique = ((struct fuse_in_header *)		\
238 				   bytes_in)->unique,			\
239 		};							\
240 		*(struct type1 *) (bytes_out				\
241 			+ sizeof(struct fuse_out_header))		\
242 			= obj1;						\
243 		TESTEQUAL(write(fuse_dev, bytes_out,			\
244 			((struct fuse_out_header *)bytes_out)->len),	\
245 			((struct fuse_out_header *)bytes_out)->len);	\
246 	} while (false)
247 
248 #define TESTFUSEOUT2(type1, obj1, type2, obj2)				\
249 	do {								\
250 		*(struct fuse_out_header *) bytes_out			\
251 			= (struct fuse_out_header) {			\
252 			.len = sizeof(struct fuse_out_header)		\
253 				+ sizeof(struct type1)			\
254 				+ sizeof(struct type2),			\
255 			.unique = ((struct fuse_in_header *)		\
256 				   bytes_in)->unique,			\
257 		};							\
258 		*(struct type1 *) (bytes_out				\
259 			+ sizeof(struct fuse_out_header))		\
260 			= obj1;						\
261 		*(struct type2 *) (bytes_out				\
262 			+ sizeof(struct fuse_out_header)		\
263 			+ sizeof(struct type1))				\
264 			= obj2;						\
265 		TESTEQUAL(write(fuse_dev, bytes_out,			\
266 			((struct fuse_out_header *)bytes_out)->len),	\
267 			((struct fuse_out_header *)bytes_out)->len);	\
268 	} while (false)
269 
270 #define TESTFUSEINITFLAGS(fuse_connection_flags)			\
271 	do {								\
272 		DECL_FUSE_IN(init);					\
273 									\
274 		TESTFUSEIN(FUSE_INIT, init_in);				\
275 		TESTEQUAL(init_in->major, FUSE_KERNEL_VERSION);		\
276 		TESTEQUAL(init_in->minor, FUSE_KERNEL_MINOR_VERSION);	\
277 		TESTFUSEOUT1(fuse_init_out, ((struct fuse_init_out) {	\
278 			.major = FUSE_KERNEL_VERSION,			\
279 			.minor = FUSE_KERNEL_MINOR_VERSION,		\
280 			.max_readahead = 4096,				\
281 			.flags = fuse_connection_flags,			\
282 			.max_background = 0,				\
283 			.congestion_threshold = 0,			\
284 			.max_write = 4096,				\
285 			.time_gran = 1000,				\
286 			.max_pages = 12,				\
287 			.map_alignment = 4096,				\
288 		}));							\
289 	} while (false)
290 
291 #define TESTFUSEINIT()							\
292 	TESTFUSEINITFLAGS(0)
293 
294 #define DECL_FUSE_IN(name)						\
295 	struct fuse_##name##_in *name##_in =				\
296 		(struct fuse_##name##_in *)				\
297 		(bytes_in + sizeof(struct fuse_in_header))
298 
299 #define DECL_FUSE(name)							\
300 	struct fuse_##name##_in *name##_in __attribute__((unused));	\
301 	struct fuse_##name##_out *name##_out __attribute__((unused))
302 
303 #define FUSE_ACTION	TEST(pid = fork(), pid != -1);			\
304 			if (pid) {
305 
306 #define FUSE_DAEMON	} else {					\
307 				uint8_t bytes_in[FUSE_MIN_READ_BUFFER]	\
308 					__attribute__((unused));	\
309 				uint8_t bytes_out[FUSE_MIN_READ_BUFFER]	\
310 					__attribute__((unused));
311 
312 #define FUSE_DONE		exit(TEST_SUCCESS);			\
313 			}						\
314 			TESTEQUAL(waitpid(pid, &status, 0), pid);	\
315 			TESTEQUAL(status, TEST_SUCCESS);
316 
317 struct map_relocation {
318 	char *name;
319 	int fd;
320 	int value;
321 };
322 
323 int mount_fuse(const char *mount_dir, int bpf_fd, int dir_fd,
324 	       int *fuse_dev_ptr);
325 int mount_fuse_no_init(const char *mount_dir, int bpf_fd, int dir_fd,
326 	       int *fuse_dev_ptr);
327 int install_elf_bpf(const char *file, const char *section, int *fd,
328 		    struct map_relocation **map_relocations, size_t *map_count);
329 #endif
330