xref: /linux/tools/testing/selftests/memfd/fuse_mnt.c (revision 95298d63c67673c654c08952672d016212b26054)
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