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