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