Tizen 2.1 base
[framework/base/fuse.git] / example / cusexmp.c
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   gcc -Wall cusexmp.c `pkg-config fuse --cflags --libs` -o cusexmp
10 */
11
12 #define FUSE_USE_VERSION 29
13
14 #include <cuse_lowlevel.h>
15 #include <fuse_opt.h>
16 #include <stddef.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <errno.h>
22
23 #include "fioc.h"
24
25 static void *cusexmp_buf;
26 static size_t cusexmp_size;
27
28 static const char *usage =
29 "usage: cusexmp [options]\n"
30 "\n"
31 "options:\n"
32 "    --help|-h             print this help message\n"
33 "    --maj=MAJ|-M MAJ      device major number\n"
34 "    --min=MIN|-m MIN      device minor number\n"
35 "    --name=NAME|-n NAME   device name (mandatory)\n"
36 "\n";
37
38 static int cusexmp_resize(size_t new_size)
39 {
40         void *new_buf;
41
42         if (new_size == cusexmp_size)
43                 return 0;
44
45         new_buf = realloc(cusexmp_buf, new_size);
46         if (!new_buf && new_size)
47                 return -ENOMEM;
48
49         if (new_size > cusexmp_size)
50                 memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
51
52         cusexmp_buf = new_buf;
53         cusexmp_size = new_size;
54
55         return 0;
56 }
57
58 static int cusexmp_expand(size_t new_size)
59 {
60         if (new_size > cusexmp_size)
61                 return cusexmp_resize(new_size);
62         return 0;
63 }
64
65 static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
66 {
67         fuse_reply_open(req, fi);
68 }
69
70 static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
71                          struct fuse_file_info *fi)
72 {
73         (void)fi;
74
75         if (off >= cusexmp_size)
76                 off = cusexmp_size;
77         if (size > cusexmp_size - off)
78                 size = cusexmp_size - off;
79
80         fuse_reply_buf(req, cusexmp_buf + off, size);
81 }
82
83 static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
84                           off_t off, struct fuse_file_info *fi)
85 {
86         (void)fi;
87
88         if (cusexmp_expand(off + size)) {
89                 fuse_reply_err(req, ENOMEM);
90                 return;
91         }
92
93         memcpy(cusexmp_buf + off, buf, size);
94         fuse_reply_write(req, size);
95 }
96
97 static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
98                        size_t in_bufsz, size_t out_bufsz, int is_read)
99 {
100         const struct fioc_rw_arg *arg;
101         struct iovec in_iov[2], out_iov[3], iov[3];
102         size_t cur_size;
103
104         /* read in arg */
105         in_iov[0].iov_base = addr;
106         in_iov[0].iov_len = sizeof(*arg);
107         if (!in_bufsz) {
108                 fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
109                 return;
110         }
111         arg = in_buf;
112         in_buf += sizeof(*arg);
113         in_bufsz -= sizeof(*arg);
114
115         /* prepare size outputs */
116         out_iov[0].iov_base =
117                 addr + (unsigned long)&(((struct fioc_rw_arg *)0)->prev_size);
118         out_iov[0].iov_len = sizeof(arg->prev_size);
119
120         out_iov[1].iov_base =
121                 addr + (unsigned long)&(((struct fioc_rw_arg *)0)->new_size);
122         out_iov[1].iov_len = sizeof(arg->new_size);
123
124         /* prepare client buf */
125         if (is_read) {
126                 out_iov[2].iov_base = arg->buf;
127                 out_iov[2].iov_len = arg->size;
128                 if (!out_bufsz) {
129                         fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
130                         return;
131                 }
132         } else {
133                 in_iov[1].iov_base = arg->buf;
134                 in_iov[1].iov_len = arg->size;
135                 if (arg->size && !in_bufsz) {
136                         fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
137                         return;
138                 }
139         }
140
141         /* we're all set */
142         cur_size = cusexmp_size;
143         iov[0].iov_base = &cur_size;
144         iov[0].iov_len = sizeof(cur_size);
145
146         iov[1].iov_base = &cusexmp_size;
147         iov[1].iov_len = sizeof(cusexmp_size);
148
149         if (is_read) {
150                 size_t off = arg->offset;
151                 size_t size = arg->size;
152
153                 if (off >= cusexmp_size)
154                         off = cusexmp_size;
155                 if (size > cusexmp_size - off)
156                         size = cusexmp_size - off;
157
158                 iov[2].iov_base = cusexmp_buf + off;
159                 iov[2].iov_len = size;
160                 fuse_reply_ioctl_iov(req, size, iov, 3);
161         } else {
162                 if (cusexmp_expand(arg->offset + in_bufsz)) {
163                         fuse_reply_err(req, ENOMEM);
164                         return;
165                 }
166
167                 memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
168                 fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
169         }
170 }
171
172 static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
173                           struct fuse_file_info *fi, unsigned flags,
174                           const void *in_buf, size_t in_bufsz, size_t out_bufsz)
175 {
176         int is_read = 0;
177
178         (void)fi;
179
180         if (flags & FUSE_IOCTL_COMPAT) {
181                 fuse_reply_err(req, ENOSYS);
182                 return;
183         }
184
185         switch (cmd) {
186         case FIOC_GET_SIZE:
187                 if (!out_bufsz) {
188                         struct iovec iov = { arg, sizeof(size_t) };
189
190                         fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
191                 } else
192                         fuse_reply_ioctl(req, 0, &cusexmp_size,
193                                          sizeof(cusexmp_size));
194                 break;
195
196         case FIOC_SET_SIZE:
197                 if (!in_bufsz) {
198                         struct iovec iov = { arg, sizeof(size_t) };
199
200                         fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
201                 } else {
202                         cusexmp_resize(*(size_t *)in_buf);
203                         fuse_reply_ioctl(req, 0, NULL, 0);
204                 }
205                 break;
206
207         case FIOC_READ:
208                 is_read = 1;
209         case FIOC_WRITE:
210                 fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
211                 break;
212
213         default:
214                 fuse_reply_err(req, EINVAL);
215         }
216 }
217
218 struct cusexmp_param {
219         unsigned                major;
220         unsigned                minor;
221         char                    *dev_name;
222         int                     is_help;
223 };
224
225 #define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
226
227 static const struct fuse_opt cusexmp_opts[] = {
228         CUSEXMP_OPT("-M %u",            major),
229         CUSEXMP_OPT("--maj=%u",         major),
230         CUSEXMP_OPT("-m %u",            minor),
231         CUSEXMP_OPT("--min=%u",         minor),
232         CUSEXMP_OPT("-n %s",            dev_name),
233         CUSEXMP_OPT("--name=%s",        dev_name),
234         FUSE_OPT_KEY("-h",              0),
235         FUSE_OPT_KEY("--help",          0),
236         FUSE_OPT_END
237 };
238
239 static int cusexmp_process_arg(void *data, const char *arg, int key,
240                                struct fuse_args *outargs)
241 {
242         struct cusexmp_param *param = data;
243
244         (void)outargs;
245         (void)arg;
246
247         switch (key) {
248         case 0:
249                 param->is_help = 1;
250                 fprintf(stderr, "%s", usage);
251                 return fuse_opt_add_arg(outargs, "-ho");
252         default:
253                 return 1;
254         }
255 }
256
257 static const struct cuse_lowlevel_ops cusexmp_clop = {
258         .open           = cusexmp_open,
259         .read           = cusexmp_read,
260         .write          = cusexmp_write,
261         .ioctl          = cusexmp_ioctl,
262 };
263
264 int main(int argc, char **argv)
265 {
266         struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
267         struct cusexmp_param param = { 0, 0, NULL, 0 };
268         char dev_name[128] = "DEVNAME=";
269         const char *dev_info_argv[] = { dev_name };
270         struct cuse_info ci;
271
272         if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
273                 printf("failed to parse option\n");
274                 return 1;
275         }
276
277         if (!param.is_help) {
278                 if (!param.dev_name) {
279                         fprintf(stderr, "Error: device name missing\n");
280                         return 1;
281                 }
282                 strncat(dev_name, param.dev_name, sizeof(dev_name) - 9);
283         }
284
285         memset(&ci, 0, sizeof(ci));
286         ci.dev_major = param.major;
287         ci.dev_minor = param.minor;
288         ci.dev_info_argc = 1;
289         ci.dev_info_argv = dev_info_argv;
290         ci.flags = CUSE_UNRESTRICTED_IOCTL;
291
292         return cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop,
293                                   NULL);
294 }