xref: /linux/tools/testing/selftests/filesystems/fuse/fuse_mnt.c (revision 6238729bfce13f94b701766996a5d116d2df8bff)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * fusectl test file-system
4  * Creates a simple FUSE filesystem with a single read-write file (/test)
5  */
6 
7 #define FUSE_USE_VERSION 26
8 
9 #include <fuse.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 
17 #define MAX(a, b) ((a) > (b) ? (a) : (b))
18 
19 static char *content;
20 static size_t content_size = 0;
21 static const char test_path[] = "/test";
22 
test_getattr(const char * path,struct stat * st)23 static int test_getattr(const char *path, struct stat *st)
24 {
25 	memset(st, 0, sizeof(*st));
26 
27 	if (!strcmp(path, "/")) {
28 		st->st_mode = S_IFDIR | 0755;
29 		st->st_nlink = 2;
30 		return 0;
31 	}
32 
33 	if (!strcmp(path, test_path)) {
34 		st->st_mode = S_IFREG | 0664;
35 		st->st_nlink = 1;
36 		st->st_size = content_size;
37 		return 0;
38 	}
39 
40 	return -ENOENT;
41 }
42 
test_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)43 static int test_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
44 			off_t offset, struct fuse_file_info *fi)
45 {
46 	if (strcmp(path, "/"))
47 		return -ENOENT;
48 
49 	filler(buf, ".", NULL, 0);
50 	filler(buf, "..", NULL, 0);
51 	filler(buf, test_path + 1, NULL, 0);
52 
53 	return 0;
54 }
55 
test_open(const char * path,struct fuse_file_info * fi)56 static int test_open(const char *path, struct fuse_file_info *fi)
57 {
58 	if (strcmp(path, test_path))
59 		return -ENOENT;
60 
61 	return 0;
62 }
63 
test_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)64 static int test_read(const char *path, char *buf, size_t size, off_t offset,
65 		     struct fuse_file_info *fi)
66 {
67 	if (strcmp(path, test_path) != 0)
68 		return -ENOENT;
69 
70 	if (!content || content_size == 0)
71 		return 0;
72 
73 	if (offset >= content_size)
74 		return 0;
75 
76 	if (offset + size > content_size)
77 		size = content_size - offset;
78 
79 	memcpy(buf, content + offset, size);
80 
81 	return size;
82 }
83 
test_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)84 static int test_write(const char *path, const char *buf, size_t size,
85 		      off_t offset, struct fuse_file_info *fi)
86 {
87 	size_t new_size;
88 
89 	if (strcmp(path, test_path) != 0)
90 		return -ENOENT;
91 
92 	if(offset > content_size)
93 		return -EINVAL;
94 
95 	new_size = MAX(offset + size, content_size);
96 
97 	if (new_size > content_size)
98 		content = realloc(content, new_size);
99 
100 	content_size = new_size;
101 
102 	if (!content)
103 		return -ENOMEM;
104 
105 	memcpy(content + offset, buf, size);
106 
107 	return size;
108 }
109 
test_truncate(const char * path,off_t size)110 static int test_truncate(const char *path, off_t size)
111 {
112 	if (strcmp(path, test_path) != 0)
113 		return -ENOENT;
114 
115 	if (size == 0) {
116 		free(content);
117 		content = NULL;
118 		content_size = 0;
119 		return 0;
120 	}
121 
122 	content = realloc(content, size);
123 
124 	if (!content)
125 		return -ENOMEM;
126 
127 	if (size > content_size)
128 		memset(content + content_size, 0, size - content_size);
129 
130 	content_size = size;
131 	return 0;
132 }
133 
134 static struct fuse_operations memfd_ops = {
135 	.getattr = test_getattr,
136 	.readdir = test_readdir,
137 	.open = test_open,
138 	.read = test_read,
139 	.write = test_write,
140 	.truncate = test_truncate,
141 };
142 
main(int argc,char * argv[])143 int main(int argc, char *argv[])
144 {
145 	return fuse_main(argc, argv, &memfd_ops, NULL);
146 }
147