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 GPL.
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
47 static void *cusexmp_buf;
48 static size_t cusexmp_size;
49
50 static 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
63 static 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
83 static 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
90 static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
91 {
92 fuse_reply_open(req, fi);
93 }
94
95 static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
96 struct fuse_file_info *fi)
97 {
98 (void)fi;
99
100 if (off >= cusexmp_size)
101 off = cusexmp_size;
102 if (size > cusexmp_size - off)
103 size = cusexmp_size - off;
104
105 fuse_reply_buf(req, cusexmp_buf + off, size);
106 }
107
108 static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
109 off_t off, struct fuse_file_info *fi)
110 {
111 (void)fi;
112
113 if (cusexmp_expand(off + size)) {
114 fuse_reply_err(req, ENOMEM);
115 return;
116 }
117
118 memcpy(cusexmp_buf + off, buf, size);
119 fuse_reply_write(req, size);
120 }
121
122 static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
123 size_t in_bufsz, size_t out_bufsz, int is_read)
124 {
125 const struct fioc_rw_arg *arg;
126 struct iovec in_iov[2], out_iov[3], iov[3];
127 size_t cur_size;
128
129 /* read in arg */
130 in_iov[0].iov_base = addr;
131 in_iov[0].iov_len = sizeof(*arg);
132 if (!in_bufsz) {
133 fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
134 return;
135 }
136 arg = in_buf;
137 in_buf += sizeof(*arg);
138 in_bufsz -= sizeof(*arg);
139
140 /* prepare size outputs */
141 out_iov[0].iov_base =
142 addr + offsetof(struct fioc_rw_arg, prev_size);
143 out_iov[0].iov_len = sizeof(arg->prev_size);
144
145 out_iov[1].iov_base =
146 addr + offsetof(struct fioc_rw_arg, new_size);
147 out_iov[1].iov_len = sizeof(arg->new_size);
148
149 /* prepare client buf */
150 if (is_read) {
151 out_iov[2].iov_base = arg->buf;
152 out_iov[2].iov_len = arg->size;
153 if (!out_bufsz) {
154 fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
155 return;
156 }
157 } else {
158 in_iov[1].iov_base = arg->buf;
159 in_iov[1].iov_len = arg->size;
160 if (arg->size && !in_bufsz) {
161 fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
162 return;
163 }
164 }
165
166 /* we're all set */
167 cur_size = cusexmp_size;
168 iov[0].iov_base = &cur_size;
169 iov[0].iov_len = sizeof(cur_size);
170
171 iov[1].iov_base = &cusexmp_size;
172 iov[1].iov_len = sizeof(cusexmp_size);
173
174 if (is_read) {
175 size_t off = arg->offset;
176 size_t size = arg->size;
177
178 if (off >= cusexmp_size)
179 off = cusexmp_size;
180 if (size > cusexmp_size - off)
181 size = cusexmp_size - off;
182
183 iov[2].iov_base = cusexmp_buf + off;
184 iov[2].iov_len = size;
185 fuse_reply_ioctl_iov(req, size, iov, 3);
186 } else {
187 if (cusexmp_expand(arg->offset + in_bufsz)) {
188 fuse_reply_err(req, ENOMEM);
189 return;
190 }
191
192 memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
193 fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
194 }
195 }
196
197 static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
198 struct fuse_file_info *fi, unsigned flags,
199 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
200 {
201 int is_read = 0;
202
203 (void)fi;
204
205 if (flags & FUSE_IOCTL_COMPAT) {
206 fuse_reply_err(req, ENOSYS);
207 return;
208 }
209
210 switch (cmd) {
211 case FIOC_GET_SIZE:
212 if (!out_bufsz) {
213 struct iovec iov = { arg, sizeof(size_t) };
214
215 fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
216 } else
217 fuse_reply_ioctl(req, 0, &cusexmp_size,
218 sizeof(cusexmp_size));
219 break;
220
221 case FIOC_SET_SIZE:
222 if (!in_bufsz) {
223 struct iovec iov = { arg, sizeof(size_t) };
224
225 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
226 } else {
227 cusexmp_resize(*(size_t *)in_buf);
228 fuse_reply_ioctl(req, 0, NULL, 0);
229 }
230 break;
231
232 case FIOC_READ:
233 is_read = 1;
234 /* fall through */
235 case FIOC_WRITE:
236 fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
237 break;
238
239 default:
240 fuse_reply_err(req, EINVAL);
241 }
242 }
243
244 struct cusexmp_param {
245 unsigned major;
246 unsigned minor;
247 char *dev_name;
248 int is_help;
249 };
250
251 #define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
252
253 static const struct fuse_opt cusexmp_opts[] = {
254 CUSEXMP_OPT("-M %u", major),
255 CUSEXMP_OPT("--maj=%u", major),
256 CUSEXMP_OPT("-m %u", minor),
257 CUSEXMP_OPT("--min=%u", minor),
258 CUSEXMP_OPT("-n %s", dev_name),
259 CUSEXMP_OPT("--name=%s", dev_name),
260 FUSE_OPT_KEY("-h", 0),
261 FUSE_OPT_KEY("--help", 0),
262 FUSE_OPT_END
263 };
264
265 static int cusexmp_process_arg(void *data, const char *arg, int key,
266 struct fuse_args *outargs)
267 {
268 struct cusexmp_param *param = data;
269
270 (void)outargs;
271 (void)arg;
272
273 switch (key) {
274 case 0:
275 param->is_help = 1;
276 fprintf(stderr, "%s", usage);
277 return fuse_opt_add_arg(outargs, "-ho");
278 default:
279 return 1;
280 }
281 }
282
283 static const struct cuse_lowlevel_ops cusexmp_clop = {
284 .open = cusexmp_open,
285 .read = cusexmp_read,
286 .write = cusexmp_write,
287 .ioctl = cusexmp_ioctl,
288 };
289
290 int main(int argc, char **argv)
291 {
292 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
293 struct cusexmp_param param = { 0, 0, NULL, 0 };
294 char dev_name[128] = "DEVNAME=";
295 const char *dev_info_argv[] = { dev_name };
296 struct cuse_info ci;
297
298 if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
299 printf("failed to parse option\n");
300 return 1;
301 }
302
303 if (!param.is_help) {
304 if (!param.dev_name) {
305 fprintf(stderr, "Error: device name missing\n");
306 return 1;
307 }
308 strncat(dev_name, param.dev_name, sizeof(dev_name) - 9);
309 }
310
311 memset(&ci, 0, sizeof(ci));
312 ci.dev_major = param.major;
313 ci.dev_minor = param.minor;
314 ci.dev_info_argc = 1;
315 ci.dev_info_argv = dev_info_argv;
316 ci.flags = CUSE_UNRESTRICTED_IOCTL;
317
318 return cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop,
319 NULL);
320 }
fuse_opt_parse
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
fuse_req_t
struct fuse_req * fuse_req_t
Definition: fuse_lowlevel.h:49
FUSE_IOCTL_COMPAT
#define FUSE_IOCTL_COMPAT
Definition: fuse_common.h:371
fuse_reply_ioctl_iov
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
Definition: fuse_lowlevel.c:1034
fuse_args::argc
int argc
Definition: fuse_opt.h:111
FUSE_ARGS_INIT
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
fuse_reply_buf
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
Definition: fuse_lowlevel.c:472
ioctl.h
fuse_reply_write
int fuse_reply_write(fuse_req_t req, size_t count)
Definition: fuse_lowlevel.c:462
fuse_file_info
Definition: fuse_common.h:44
fuse_reply_ioctl_retry
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)
Definition: fuse_lowlevel.c:943
fuse_args::argv
char ** argv
Definition: fuse_opt.h:114
FUSE_OPT_END
#define FUSE_OPT_END
Definition: fuse_opt.h:104
fuse_reply_err
int fuse_reply_err(fuse_req_t req, int err)
Definition: fuse_lowlevel.c:312
FUSE_OPT_KEY
#define FUSE_OPT_KEY(templ, key)
Definition: fuse_opt.h:98
fuse_opt
Definition: fuse_opt.h:77
fuse_reply_ioctl
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
Definition: fuse_lowlevel.c:1013
fuse_args
Definition: fuse_opt.h:109
fuse_opt_add_arg
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition: fuse_opt.c:55
fuse_reply_open
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
Definition: fuse_lowlevel.c:453