1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * memfd test file-system 4 * This file uses FUSE to create a dummy file-system with only one file /memfd. 5 * This file is read-only and takes 1s per read. 6 * 7 * This file-system is used by the memfd test-cases to force the kernel to pin 8 * pages during reads(). Due to the 1s delay of this file-system, this is a 9 * nice way to test race-conditions against get_user_pages() in the kernel. 10 * 11 * We use direct_io==1 to force the kernel to use direct-IO for this 12 * file-system. 13 */ 14 15 #define FUSE_USE_VERSION 26 16 17 #include <fuse.h> 18 #include <stdio.h> 19 #include <string.h> 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <unistd.h> 23 24 static const char memfd_content[] = "memfd-example-content"; 25 static const char memfd_path[] = "/memfd"; 26 27 static int memfd_getattr(const char *path, struct stat *st) 28 { 29 memset(st, 0, sizeof(*st)); 30 31 if (!strcmp(path, "/")) { 32 st->st_mode = S_IFDIR | 0755; 33 st->st_nlink = 2; 34 } else if (!strcmp(path, memfd_path)) { 35 st->st_mode = S_IFREG | 0444; 36 st->st_nlink = 1; 37 st->st_size = strlen(memfd_content); 38 } else { 39 return -ENOENT; 40 } 41 42 return 0; 43 } 44 45 static int memfd_readdir(const char *path, 46 void *buf, 47 fuse_fill_dir_t filler, 48 off_t offset, 49 struct fuse_file_info *fi) 50 { 51 if (strcmp(path, "/")) 52 return -ENOENT; 53 54 filler(buf, ".", NULL, 0); 55 filler(buf, "..", NULL, 0); 56 filler(buf, memfd_path + 1, NULL, 0); 57 58 return 0; 59 } 60 61 static int memfd_open(const char *path, struct fuse_file_info *fi) 62 { 63 if (strcmp(path, memfd_path)) 64 return -ENOENT; 65 66 if ((fi->flags & 3) != O_RDONLY) 67 return -EACCES; 68 69 /* force direct-IO */ 70 fi->direct_io = 1; 71 72 return 0; 73 } 74 75 static int memfd_read(const char *path, 76 char *buf, 77 size_t size, 78 off_t offset, 79 struct fuse_file_info *fi) 80 { 81 size_t len; 82 83 if (strcmp(path, memfd_path) != 0) 84 return -ENOENT; 85 86 sleep(1); 87 88 len = strlen(memfd_content); 89 if (offset < len) { 90 if (offset + size > len) 91 size = len - offset; 92 93 memcpy(buf, memfd_content + offset, size); 94 } else { 95 size = 0; 96 } 97 98 return size; 99 } 100 101 static struct fuse_operations memfd_ops = { 102 .getattr = memfd_getattr, 103 .readdir = memfd_readdir, 104 .open = memfd_open, 105 .read = memfd_read, 106 }; 107 108 int main(int argc, char *argv[]) 109 { 110 return fuse_main(argc, argv, &memfd_ops, NULL); 111 } 112