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
24static const char memfd_content[] = "memfd-example-content";
25static const char memfd_path[] = "/memfd";
26
27static 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
45static 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
61static 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
75static 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
101static struct fuse_operations memfd_ops = {
102	.getattr	= memfd_getattr,
103	.readdir	= memfd_readdir,
104	.open		= memfd_open,
105	.read		= memfd_read,
106};
107
108int main(int argc, char *argv[])
109{
110	return fuse_main(argc, argv, &memfd_ops, NULL);
111}
112