Merge branch 'clone_fd'
[fuse.git] / example / cusexmp.c
blob8d2207528ca9e393c9c14195ec83aad43f7b9140
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>
6 This program can be distributed under the terms of the GNU GPL.
7 See the file COPYING.
9 */
11 /** @file
12 * @tableofcontents
14 * cusexmp.c - CUSE example: Character device in Userspace
16 * \section section_compile compiling this example
18 * gcc -Wall cusexmp.c `pkg-config fuse3 --cflags --libs` -o cusexmp
20 * \section section_source the complete source
21 * \include cusexmp.c
25 #define FUSE_USE_VERSION 30
27 #include <config.h>
29 #include <cuse_lowlevel.h>
30 #include <fuse_opt.h>
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <errno.h>
38 #include "fioc.h"
40 static void *cusexmp_buf;
41 static size_t cusexmp_size;
43 static const char *usage =
44 "usage: cusexmp [options]\n"
45 "\n"
46 "options:\n"
47 " --help|-h print this help message\n"
48 " --maj=MAJ|-M MAJ device major number\n"
49 " --min=MIN|-m MIN device minor number\n"
50 " --name=NAME|-n NAME device name (mandatory)\n"
51 "\n";
53 static int cusexmp_resize(size_t new_size)
55 void *new_buf;
57 if (new_size == cusexmp_size)
58 return 0;
60 new_buf = realloc(cusexmp_buf, new_size);
61 if (!new_buf && new_size)
62 return -ENOMEM;
64 if (new_size > cusexmp_size)
65 memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
67 cusexmp_buf = new_buf;
68 cusexmp_size = new_size;
70 return 0;
73 static int cusexmp_expand(size_t new_size)
75 if (new_size > cusexmp_size)
76 return cusexmp_resize(new_size);
77 return 0;
80 static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
82 fuse_reply_open(req, fi);
85 static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
86 struct fuse_file_info *fi)
88 (void)fi;
90 if (off >= cusexmp_size)
91 off = cusexmp_size;
92 if (size > cusexmp_size - off)
93 size = cusexmp_size - off;
95 fuse_reply_buf(req, cusexmp_buf + off, size);
98 static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
99 off_t off, struct fuse_file_info *fi)
101 (void)fi;
103 if (cusexmp_expand(off + size)) {
104 fuse_reply_err(req, ENOMEM);
105 return;
108 memcpy(cusexmp_buf + off, buf, size);
109 fuse_reply_write(req, size);
112 static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
113 size_t in_bufsz, size_t out_bufsz, int is_read)
115 const struct fioc_rw_arg *arg;
116 struct iovec in_iov[2], out_iov[3], iov[3];
117 size_t cur_size;
119 /* read in arg */
120 in_iov[0].iov_base = addr;
121 in_iov[0].iov_len = sizeof(*arg);
122 if (!in_bufsz) {
123 fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
124 return;
126 arg = in_buf;
127 in_buf += sizeof(*arg);
128 in_bufsz -= sizeof(*arg);
130 /* prepare size outputs */
131 out_iov[0].iov_base =
132 addr + (unsigned long)&(((struct fioc_rw_arg *)0)->prev_size);
133 out_iov[0].iov_len = sizeof(arg->prev_size);
135 out_iov[1].iov_base =
136 addr + (unsigned long)&(((struct fioc_rw_arg *)0)->new_size);
137 out_iov[1].iov_len = sizeof(arg->new_size);
139 /* prepare client buf */
140 if (is_read) {
141 out_iov[2].iov_base = arg->buf;
142 out_iov[2].iov_len = arg->size;
143 if (!out_bufsz) {
144 fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
145 return;
147 } else {
148 in_iov[1].iov_base = arg->buf;
149 in_iov[1].iov_len = arg->size;
150 if (arg->size && !in_bufsz) {
151 fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
152 return;
156 /* we're all set */
157 cur_size = cusexmp_size;
158 iov[0].iov_base = &cur_size;
159 iov[0].iov_len = sizeof(cur_size);
161 iov[1].iov_base = &cusexmp_size;
162 iov[1].iov_len = sizeof(cusexmp_size);
164 if (is_read) {
165 size_t off = arg->offset;
166 size_t size = arg->size;
168 if (off >= cusexmp_size)
169 off = cusexmp_size;
170 if (size > cusexmp_size - off)
171 size = cusexmp_size - off;
173 iov[2].iov_base = cusexmp_buf + off;
174 iov[2].iov_len = size;
175 fuse_reply_ioctl_iov(req, size, iov, 3);
176 } else {
177 if (cusexmp_expand(arg->offset + in_bufsz)) {
178 fuse_reply_err(req, ENOMEM);
179 return;
182 memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
183 fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
187 static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
188 struct fuse_file_info *fi, unsigned flags,
189 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
191 int is_read = 0;
193 (void)fi;
195 if (flags & FUSE_IOCTL_COMPAT) {
196 fuse_reply_err(req, ENOSYS);
197 return;
200 switch (cmd) {
201 case FIOC_GET_SIZE:
202 if (!out_bufsz) {
203 struct iovec iov = { arg, sizeof(size_t) };
205 fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
206 } else
207 fuse_reply_ioctl(req, 0, &cusexmp_size,
208 sizeof(cusexmp_size));
209 break;
211 case FIOC_SET_SIZE:
212 if (!in_bufsz) {
213 struct iovec iov = { arg, sizeof(size_t) };
215 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
216 } else {
217 cusexmp_resize(*(size_t *)in_buf);
218 fuse_reply_ioctl(req, 0, NULL, 0);
220 break;
222 case FIOC_READ:
223 is_read = 1;
224 case FIOC_WRITE:
225 fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
226 break;
228 default:
229 fuse_reply_err(req, EINVAL);
233 struct cusexmp_param {
234 unsigned major;
235 unsigned minor;
236 char *dev_name;
237 int is_help;
240 #define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
242 static const struct fuse_opt cusexmp_opts[] = {
243 CUSEXMP_OPT("-M %u", major),
244 CUSEXMP_OPT("--maj=%u", major),
245 CUSEXMP_OPT("-m %u", minor),
246 CUSEXMP_OPT("--min=%u", minor),
247 CUSEXMP_OPT("-n %s", dev_name),
248 CUSEXMP_OPT("--name=%s", dev_name),
249 FUSE_OPT_KEY("-h", 0),
250 FUSE_OPT_KEY("--help", 0),
251 FUSE_OPT_END
254 static int cusexmp_process_arg(void *data, const char *arg, int key,
255 struct fuse_args *outargs)
257 struct cusexmp_param *param = data;
259 (void)outargs;
260 (void)arg;
262 switch (key) {
263 case 0:
264 param->is_help = 1;
265 fprintf(stderr, "%s", usage);
266 return fuse_opt_add_arg(outargs, "-ho");
267 default:
268 return 1;
272 static const struct cuse_lowlevel_ops cusexmp_clop = {
273 .open = cusexmp_open,
274 .read = cusexmp_read,
275 .write = cusexmp_write,
276 .ioctl = cusexmp_ioctl,
279 int main(int argc, char **argv)
281 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
282 struct cusexmp_param param = { 0, 0, NULL, 0 };
283 char dev_name[128] = "DEVNAME=";
284 const char *dev_info_argv[] = { dev_name };
285 struct cuse_info ci;
287 if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
288 printf("failed to parse option\n");
289 return 1;
292 if (!param.is_help) {
293 if (!param.dev_name) {
294 fprintf(stderr, "Error: device name missing\n");
295 return 1;
297 strncat(dev_name, param.dev_name, sizeof(dev_name) - 9);
300 memset(&ci, 0, sizeof(ci));
301 ci.dev_major = param.major;
302 ci.dev_minor = param.minor;
303 ci.dev_info_argc = 1;
304 ci.dev_info_argv = dev_info_argv;
305 ci.flags = CUSE_UNRESTRICTED_IOCTL;
307 return cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop,
308 NULL);