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