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