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