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 | |