libfuse
cuse.c
Go to the documentation of this file.
1/*
2 CUSE example: Character device in Userspace
3 Copyright (C) 2008-2009 SUSE Linux Products GmbH
4 Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file COPYING.
8
9*/
10
34#define FUSE_USE_VERSION 31
35
36#include <cuse_lowlevel.h>
37#include <fuse_opt.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43#include <errno.h>
44
45#include "ioctl.h"
46
47static void *cusexmp_buf;
48static size_t cusexmp_size;
49
50static const char *usage =
51"usage: cusexmp [options]\n"
52"\n"
53"options:\n"
54" --help|-h print this help message\n"
55" --maj=MAJ|-M MAJ device major number\n"
56" --min=MIN|-m MIN device minor number\n"
57" --name=NAME|-n NAME device name (mandatory)\n"
58" -d -o debug enable debug output (implies -f)\n"
59" -f foreground operation\n"
60" -s disable multi-threaded operation\n"
61"\n";
62
63static int cusexmp_resize(size_t new_size)
64{
65 void *new_buf;
66
67 if (new_size == cusexmp_size)
68 return 0;
69
70 new_buf = realloc(cusexmp_buf, new_size);
71 if (!new_buf && new_size)
72 return -ENOMEM;
73
74 if (new_size > cusexmp_size)
75 memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
76
77 cusexmp_buf = new_buf;
78 cusexmp_size = new_size;
79
80 return 0;
81}
82
83static int cusexmp_expand(size_t new_size)
84{
85 if (new_size > cusexmp_size)
86 return cusexmp_resize(new_size);
87 return 0;
88}
89
90static void cusexmp_init(void *userdata, struct fuse_conn_info *conn)
91{
92 (void)userdata;
93
94 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
95 conn->no_interrupt = 1;
96}
97
98static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
99{
100 fuse_reply_open(req, fi);
101}
102
103static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
104 struct fuse_file_info *fi)
105{
106 (void)fi;
107
108 if (off >= cusexmp_size)
109 off = cusexmp_size;
110 if (size > cusexmp_size - off)
111 size = cusexmp_size - off;
112
113 fuse_reply_buf(req, cusexmp_buf + off, size);
114}
115
116static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
117 off_t off, struct fuse_file_info *fi)
118{
119 (void)fi;
120
121 if (cusexmp_expand(off + size)) {
122 fuse_reply_err(req, ENOMEM);
123 return;
124 }
125
126 memcpy(cusexmp_buf + off, buf, size);
127 fuse_reply_write(req, size);
128}
129
130static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
131 size_t in_bufsz, size_t out_bufsz, int is_read)
132{
133 const struct fioc_rw_arg *arg;
134 struct iovec in_iov[2], out_iov[3], iov[3];
135 size_t cur_size;
136
137 /* read in arg */
138 in_iov[0].iov_base = addr;
139 in_iov[0].iov_len = sizeof(*arg);
140 if (!in_bufsz) {
141 fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
142 return;
143 }
144 arg = in_buf;
145 in_buf += sizeof(*arg);
146 in_bufsz -= sizeof(*arg);
147
148 /* prepare size outputs */
149 out_iov[0].iov_base =
150 addr + offsetof(struct fioc_rw_arg, prev_size);
151 out_iov[0].iov_len = sizeof(arg->prev_size);
152
153 out_iov[1].iov_base =
154 addr + offsetof(struct fioc_rw_arg, new_size);
155 out_iov[1].iov_len = sizeof(arg->new_size);
156
157 /* prepare client buf */
158 if (is_read) {
159 out_iov[2].iov_base = arg->buf;
160 out_iov[2].iov_len = arg->size;
161 if (!out_bufsz) {
162 fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
163 return;
164 }
165 } else {
166 in_iov[1].iov_base = arg->buf;
167 in_iov[1].iov_len = arg->size;
168 if (arg->size && !in_bufsz) {
169 fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
170 return;
171 }
172 }
173
174 /* we're all set */
175 cur_size = cusexmp_size;
176 iov[0].iov_base = &cur_size;
177 iov[0].iov_len = sizeof(cur_size);
178
179 iov[1].iov_base = &cusexmp_size;
180 iov[1].iov_len = sizeof(cusexmp_size);
181
182 if (is_read) {
183 size_t off = arg->offset;
184 size_t size = arg->size;
185
186 if (off >= cusexmp_size)
187 off = cusexmp_size;
188 if (size > cusexmp_size - off)
189 size = cusexmp_size - off;
190
191 iov[2].iov_base = cusexmp_buf + off;
192 iov[2].iov_len = size;
193 fuse_reply_ioctl_iov(req, size, iov, 3);
194 } else {
195 if (cusexmp_expand(arg->offset + in_bufsz)) {
196 fuse_reply_err(req, ENOMEM);
197 return;
198 }
199
200 memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
201 fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
202 }
203}
204
205static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
206 struct fuse_file_info *fi, unsigned flags,
207 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
208{
209 int is_read = 0;
210
211 (void)fi;
212
213 if (flags & FUSE_IOCTL_COMPAT) {
214 fuse_reply_err(req, ENOSYS);
215 return;
216 }
217
218 switch (cmd) {
219 case FIOC_GET_SIZE:
220 if (!out_bufsz) {
221 struct iovec iov = { arg, sizeof(size_t) };
222
223 fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
224 } else
225 fuse_reply_ioctl(req, 0, &cusexmp_size,
226 sizeof(cusexmp_size));
227 break;
228
229 case FIOC_SET_SIZE:
230 if (!in_bufsz) {
231 struct iovec iov = { arg, sizeof(size_t) };
232
233 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
234 } else {
235 cusexmp_resize(*(size_t *)in_buf);
236 fuse_reply_ioctl(req, 0, NULL, 0);
237 }
238 break;
239
240 case FIOC_READ:
241 is_read = 1;
242 /* fall through */
243 case FIOC_WRITE:
244 fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
245 break;
246
247 default:
248 fuse_reply_err(req, EINVAL);
249 }
250}
251
252struct cusexmp_param {
253 unsigned major;
254 unsigned minor;
255 char *dev_name;
256 int is_help;
257};
258
259#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
260
261static const struct fuse_opt cusexmp_opts[] = {
262 CUSEXMP_OPT("-M %u", major),
263 CUSEXMP_OPT("--maj=%u", major),
264 CUSEXMP_OPT("-m %u", minor),
265 CUSEXMP_OPT("--min=%u", minor),
266 CUSEXMP_OPT("-n %s", dev_name),
267 CUSEXMP_OPT("--name=%s", dev_name),
268 FUSE_OPT_KEY("-h", 0),
269 FUSE_OPT_KEY("--help", 0),
271};
272
273static int cusexmp_process_arg(void *data, const char *arg, int key,
274 struct fuse_args *outargs)
275{
276 struct cusexmp_param *param = data;
277
278 (void)outargs;
279 (void)arg;
280
281 switch (key) {
282 case 0:
283 param->is_help = 1;
284 fprintf(stderr, "%s", usage);
285 return fuse_opt_add_arg(outargs, "-ho");
286 default:
287 return 1;
288 }
289}
290
291static const struct cuse_lowlevel_ops cusexmp_clop = {
292 .init = cusexmp_init,
293 .open = cusexmp_open,
294 .read = cusexmp_read,
295 .write = cusexmp_write,
296 .ioctl = cusexmp_ioctl,
297};
298
299int main(int argc, char **argv)
300{
301 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
302 struct cusexmp_param param = { 0, 0, NULL, 0 };
303 char dev_name[128] = "DEVNAME=";
304 const char *dev_info_argv[] = { dev_name };
305 struct cuse_info ci;
306 int ret = 1;
307
308 if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
309 printf("failed to parse option\n");
310 free(param.dev_name);
311 goto out;
312 }
313
314 if (!param.is_help) {
315 if (!param.dev_name) {
316 fprintf(stderr, "Error: device name missing\n");
317 goto out;
318 }
319 strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME="));
320 free(param.dev_name);
321 }
322
323 memset(&ci, 0, sizeof(ci));
324 ci.dev_major = param.major;
325 ci.dev_minor = param.minor;
326 ci.dev_info_argc = 1;
327 ci.dev_info_argv = dev_info_argv;
328 ci.flags = CUSE_UNRESTRICTED_IOCTL;
329
330 ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL);
331
332out:
333 fuse_opt_free_args(&args);
334 return ret;
335}
#define FUSE_IOCTL_COMPAT
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt