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.
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
25 #define FUSE_USE_VERSION 30
29 #include <cuse_lowlevel.h>
40 static void *cusexmp_buf
;
41 static size_t cusexmp_size
;
43 static const char *usage
=
44 "usage: cusexmp [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"
53 static int cusexmp_resize(size_t new_size
)
57 if (new_size
== cusexmp_size
)
60 new_buf
= realloc(cusexmp_buf
, new_size
);
61 if (!new_buf
&& new_size
)
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
;
73 static int cusexmp_expand(size_t new_size
)
75 if (new_size
> cusexmp_size
)
76 return cusexmp_resize(new_size
);
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
)
90 if (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
)
103 if (cusexmp_expand(off
+ size
)) {
104 fuse_reply_err(req
, ENOMEM
);
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];
120 in_iov
[0].iov_base
= addr
;
121 in_iov
[0].iov_len
= sizeof(*arg
);
123 fuse_reply_ioctl_retry(req
, in_iov
, 1, NULL
, 0);
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 */
141 out_iov
[2].iov_base
= arg
->buf
;
142 out_iov
[2].iov_len
= arg
->size
;
144 fuse_reply_ioctl_retry(req
, in_iov
, 1, out_iov
, 3);
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);
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
);
165 size_t off
= arg
->offset
;
166 size_t size
= arg
->size
;
168 if (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);
177 if (cusexmp_expand(arg
->offset
+ in_bufsz
)) {
178 fuse_reply_err(req
, ENOMEM
);
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
)
195 if (flags
& FUSE_IOCTL_COMPAT
) {
196 fuse_reply_err(req
, ENOSYS
);
203 struct iovec iov
= { arg
, sizeof(size_t) };
205 fuse_reply_ioctl_retry(req
, NULL
, 0, &iov
, 1);
207 fuse_reply_ioctl(req
, 0, &cusexmp_size
,
208 sizeof(cusexmp_size
));
213 struct iovec iov
= { arg
, sizeof(size_t) };
215 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
217 cusexmp_resize(*(size_t *)in_buf
);
218 fuse_reply_ioctl(req
, 0, NULL
, 0);
225 fioc_do_rw(req
, arg
, in_buf
, in_bufsz
, out_bufsz
, is_read
);
229 fuse_reply_err(req
, EINVAL
);
233 struct cusexmp_param
{
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),
254 static int cusexmp_process_arg(void *data
, const char *arg
, int key
,
255 struct fuse_args
*outargs
)
257 struct cusexmp_param
*param
= data
;
265 fprintf(stderr
, "%s", usage
);
266 return fuse_opt_add_arg(outargs
, "-ho");
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
};
287 if (fuse_opt_parse(&args
, ¶m
, cusexmp_opts
, cusexmp_process_arg
)) {
288 printf("failed to parse option\n");
292 if (!param
.is_help
) {
293 if (!param
.dev_name
) {
294 fprintf(stderr
, "Error: device name missing\n");
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
,