1 /*
2   CUSE: Character device in Userspace
3   Copyright (C) 2008       SUSE Linux Products GmbH
4   Copyright (C) 2008       Tejun Heo <teheo@suse.de>
5 
6   This program can be distributed under the terms of the GNU LGPLv2.
7   See the file COPYING.LIB.
8 */
9 
10 #include "cuse_lowlevel.h"
11 #include "fuse_kernel.h"
12 #include "fuse_i.h"
13 #include "fuse_opt.h"
14 #include "fuse_misc.h"
15 
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <stddef.h>
20 #include <errno.h>
21 #include <unistd.h>
22 
23 struct cuse_data {
24 	struct cuse_lowlevel_ops	clop;
25 	unsigned			max_read;
26 	unsigned			dev_major;
27 	unsigned			dev_minor;
28 	unsigned			flags;
29 	unsigned			dev_info_len;
30 	char				dev_info[];
31 };
32 
req_clop(fuse_req_t req)33 static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
34 {
35 	return &req->f->cuse_data->clop;
36 }
37 
cuse_fll_open(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)38 static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
39 			  struct fuse_file_info *fi)
40 {
41 	(void)ino;
42 	req_clop(req)->open(req, fi);
43 }
44 
cuse_fll_read(fuse_req_t req,fuse_ino_t ino,size_t size,off_t off,struct fuse_file_info * fi)45 static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
46 			  off_t off, struct fuse_file_info *fi)
47 {
48 	(void)ino;
49 	req_clop(req)->read(req, size, off, fi);
50 }
51 
cuse_fll_write(fuse_req_t req,fuse_ino_t ino,const char * buf,size_t size,off_t off,struct fuse_file_info * fi)52 static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
53 			   size_t size, off_t off, struct fuse_file_info *fi)
54 {
55 	(void)ino;
56 	req_clop(req)->write(req, buf, size, off, fi);
57 }
58 
cuse_fll_flush(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)59 static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
60 			   struct fuse_file_info *fi)
61 {
62 	(void)ino;
63 	req_clop(req)->flush(req, fi);
64 }
65 
cuse_fll_release(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)66 static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
67 			     struct fuse_file_info *fi)
68 {
69 	(void)ino;
70 	req_clop(req)->release(req, fi);
71 }
72 
cuse_fll_fsync(fuse_req_t req,fuse_ino_t ino,int datasync,struct fuse_file_info * fi)73 static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
74 			   struct fuse_file_info *fi)
75 {
76 	(void)ino;
77 	req_clop(req)->fsync(req, datasync, fi);
78 }
79 
cuse_fll_ioctl(fuse_req_t req,fuse_ino_t ino,int cmd,void * arg,struct fuse_file_info * fi,unsigned int flags,const void * in_buf,size_t in_bufsz,size_t out_bufsz)80 static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
81 		       struct fuse_file_info *fi, unsigned int flags,
82 		       const void *in_buf, size_t in_bufsz, size_t out_bufsz)
83 {
84 	(void)ino;
85 	req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
86 			     out_bufsz);
87 }
88 
cuse_fll_poll(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi,struct fuse_pollhandle * ph)89 static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
90 			  struct fuse_file_info *fi, struct fuse_pollhandle *ph)
91 {
92 	(void)ino;
93 	req_clop(req)->poll(req, fi, ph);
94 }
95 
cuse_pack_info(int argc,const char ** argv,char * buf)96 static size_t cuse_pack_info(int argc, const char **argv, char *buf)
97 {
98 	size_t size = 0;
99 	int i;
100 
101 	for (i = 0; i < argc; i++) {
102 		size_t len;
103 
104 		len = strlen(argv[i]) + 1;
105 		size += len;
106 		if (buf) {
107 			memcpy(buf, argv[i], len);
108 			buf += len;
109 		}
110 	}
111 
112 	return size;
113 }
114 
cuse_prep_data(const struct cuse_info * ci,const struct cuse_lowlevel_ops * clop)115 static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
116 					const struct cuse_lowlevel_ops *clop)
117 {
118 	struct cuse_data *cd;
119 	size_t dev_info_len;
120 
121 	dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
122 				      NULL);
123 
124 	if (dev_info_len > CUSE_INIT_INFO_MAX) {
125 		fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n",
126 			dev_info_len, CUSE_INIT_INFO_MAX);
127 		return NULL;
128 	}
129 
130 	cd = calloc(1, sizeof(*cd) + dev_info_len);
131 	if (!cd) {
132 		fprintf(stderr, "cuse: failed to allocate cuse_data\n");
133 		return NULL;
134 	}
135 
136 	memcpy(&cd->clop, clop, sizeof(cd->clop));
137 	cd->max_read = 131072;
138 	cd->dev_major = ci->dev_major;
139 	cd->dev_minor = ci->dev_minor;
140 	cd->dev_info_len = dev_info_len;
141 	cd->flags = ci->flags;
142 	cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
143 
144 	return cd;
145 }
146 
cuse_lowlevel_new(struct fuse_args * args,const struct cuse_info * ci,const struct cuse_lowlevel_ops * clop,void * userdata)147 struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
148 				       const struct cuse_info *ci,
149 				       const struct cuse_lowlevel_ops *clop,
150 				       void *userdata)
151 {
152 	struct fuse_lowlevel_ops lop;
153 	struct cuse_data *cd;
154 	struct fuse_session *se;
155 	struct fuse_ll *ll;
156 
157 	cd = cuse_prep_data(ci, clop);
158 	if (!cd)
159 		return NULL;
160 
161 	memset(&lop, 0, sizeof(lop));
162 	lop.init	= clop->init;
163 	lop.destroy	= clop->destroy;
164 	lop.open	= clop->open		? cuse_fll_open		: NULL;
165 	lop.read	= clop->read		? cuse_fll_read		: NULL;
166 	lop.write	= clop->write		? cuse_fll_write	: NULL;
167 	lop.flush	= clop->flush		? cuse_fll_flush	: NULL;
168 	lop.release	= clop->release		? cuse_fll_release	: NULL;
169 	lop.fsync	= clop->fsync		? cuse_fll_fsync	: NULL;
170 	lop.ioctl	= clop->ioctl		? cuse_fll_ioctl	: NULL;
171 	lop.poll	= clop->poll		? cuse_fll_poll		: NULL;
172 
173 	se = fuse_lowlevel_new_common(args, &lop, sizeof(lop), userdata);
174 	if (!se) {
175 		free(cd);
176 		return NULL;
177 	}
178 	ll = se->data;
179 	ll->cuse_data = cd;
180 
181 	return se;
182 }
183 
cuse_reply_init(fuse_req_t req,struct cuse_init_out * arg,char * dev_info,unsigned dev_info_len)184 static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
185 			   char *dev_info, unsigned dev_info_len)
186 {
187 	struct iovec iov[3];
188 
189 	iov[1].iov_base = arg;
190 	iov[1].iov_len = sizeof(struct cuse_init_out);
191 	iov[2].iov_base = dev_info;
192 	iov[2].iov_len = dev_info_len;
193 
194 	return fuse_send_reply_iov_nofree(req, 0, iov, 3);
195 }
196 
cuse_lowlevel_init(fuse_req_t req,fuse_ino_t nodeid,const void * inarg)197 void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
198 {
199 	struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
200 	struct cuse_init_out outarg;
201 	struct fuse_ll *f = req->f;
202 	struct cuse_data *cd = f->cuse_data;
203 	size_t bufsize = fuse_chan_bufsize(req->ch);
204 	struct cuse_lowlevel_ops *clop = req_clop(req);
205 
206 	(void) nodeid;
207 	if (f->debug) {
208 		fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
209 		fprintf(stderr, "flags=0x%08x\n", arg->flags);
210 	}
211 	f->conn.proto_major = arg->major;
212 	f->conn.proto_minor = arg->minor;
213 	f->conn.capable = 0;
214 	f->conn.want = 0;
215 
216 	if (arg->major < 7) {
217 		fprintf(stderr, "cuse: unsupported protocol version: %u.%u\n",
218 			arg->major, arg->minor);
219 		fuse_reply_err(req, EPROTO);
220 		return;
221 	}
222 
223 	if (bufsize < FUSE_MIN_READ_BUFFER) {
224 		fprintf(stderr, "cuse: warning: buffer size too small: %zu\n",
225 			bufsize);
226 		bufsize = FUSE_MIN_READ_BUFFER;
227 	}
228 
229 	bufsize -= 4096;
230 	if (bufsize < f->conn.max_write)
231 		f->conn.max_write = bufsize;
232 
233 	f->got_init = 1;
234 	if (f->op.init)
235 		f->op.init(f->userdata, &f->conn);
236 
237 	memset(&outarg, 0, sizeof(outarg));
238 	outarg.major = FUSE_KERNEL_VERSION;
239 	outarg.minor = FUSE_KERNEL_MINOR_VERSION;
240 	outarg.flags = cd->flags;
241 	outarg.max_read = cd->max_read;
242 	outarg.max_write = f->conn.max_write;
243 	outarg.dev_major = cd->dev_major;
244 	outarg.dev_minor = cd->dev_minor;
245 
246 	if (f->debug) {
247 		fprintf(stderr, "   CUSE_INIT: %u.%u\n",
248 			outarg.major, outarg.minor);
249 		fprintf(stderr, "   flags=0x%08x\n", outarg.flags);
250 		fprintf(stderr, "   max_read=0x%08x\n", outarg.max_read);
251 		fprintf(stderr, "   max_write=0x%08x\n", outarg.max_write);
252 		fprintf(stderr, "   dev_major=%u\n", outarg.dev_major);
253 		fprintf(stderr, "   dev_minor=%u\n", outarg.dev_minor);
254 		fprintf(stderr, "   dev_info: %.*s\n", cd->dev_info_len,
255 			cd->dev_info);
256 	}
257 
258 	cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
259 
260 	if (clop->init_done)
261 		clop->init_done(f->userdata);
262 
263 	fuse_free_req(req);
264 }
265 
cuse_lowlevel_setup(int argc,char * argv[],const struct cuse_info * ci,const struct cuse_lowlevel_ops * clop,int * multithreaded,void * userdata)266 struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
267 					 const struct cuse_info *ci,
268 					 const struct cuse_lowlevel_ops *clop,
269 					 int *multithreaded, void *userdata)
270 {
271 	const char *devname = "/dev/cuse";
272 	static const struct fuse_opt kill_subtype_opts[] = {
273 		FUSE_OPT_KEY("subtype=",  FUSE_OPT_KEY_DISCARD),
274 		FUSE_OPT_END
275 	};
276 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
277 	struct fuse_session *se;
278 	struct fuse_chan *ch;
279 	int fd;
280 	int foreground;
281 	int res;
282 
283 	res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground);
284 	if (res == -1)
285 		goto err_args;
286 
287 	res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
288 	if (res == -1)
289 		goto err_args;
290 
291 	/*
292 	 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
293 	 * would ensue.
294 	 */
295 	do {
296 		fd = open("/dev/null", O_RDWR);
297 		if (fd > 2)
298 			close(fd);
299 	} while (fd >= 0 && fd <= 2);
300 
301 	se = cuse_lowlevel_new(&args, ci, clop, userdata);
302 	fuse_opt_free_args(&args);
303 	if (se == NULL)
304 		goto err_args;
305 
306 	fd = open(devname, O_RDWR);
307 	if (fd == -1) {
308 		if (errno == ENODEV || errno == ENOENT)
309 			fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n");
310 		else
311 			fprintf(stderr, "cuse: failed to open %s: %s\n",
312 				devname, strerror(errno));
313 		goto err_se;
314 	}
315 
316 	ch = fuse_kern_chan_new(fd);
317 	if (!ch) {
318 		close(fd);
319 		goto err_se;
320 	}
321 
322 	fuse_session_add_chan(se, ch);
323 
324 	res = fuse_set_signal_handlers(se);
325 	if (res == -1)
326 		goto err_se;
327 
328 	res = fuse_daemonize(foreground);
329 	if (res == -1)
330 		goto err_sig;
331 
332 	return se;
333 
334 err_sig:
335 	fuse_remove_signal_handlers(se);
336 err_se:
337 	fuse_session_destroy(se);
338 err_args:
339 	fuse_opt_free_args(&args);
340 	return NULL;
341 }
342 
cuse_lowlevel_teardown(struct fuse_session * se)343 void cuse_lowlevel_teardown(struct fuse_session *se)
344 {
345 	fuse_remove_signal_handlers(se);
346 	fuse_session_destroy(se);
347 }
348 
cuse_lowlevel_main(int argc,char * argv[],const struct cuse_info * ci,const struct cuse_lowlevel_ops * clop,void * userdata)349 int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
350 		       const struct cuse_lowlevel_ops *clop, void *userdata)
351 {
352 	struct fuse_session *se;
353 	int multithreaded;
354 	int res;
355 
356 	se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
357 				 userdata);
358 	if (se == NULL)
359 		return 1;
360 
361 	if (multithreaded)
362 		res = fuse_session_loop_mt(se);
363 	else
364 		res = fuse_session_loop(se);
365 
366 	cuse_lowlevel_teardown(se);
367 	if (res == -1)
368 		return 1;
369 
370 	return 0;
371 }
372