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