1/* $OpenBSD: fuse_ops.c,v 1.35 2018/07/16 13:10:53 helg Exp $ */
2/*
3 * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <errno.h>
19#include <string.h>
20#include <stdlib.h>
21
22#include "fuse_private.h"
23#include "debug.h"
24
25#define CHECK_OPT(opname)	DPRINTF("Opcode: %s\t", #opname);	\
26				DPRINTF("Inode: %llu\t",		\
27				    (unsigned long long)fbuf->fb_ino);	\
28				if (!f->op.opname) {			\
29					fbuf->fb_err = -ENOSYS;		\
30					return (0);			\
31				}
32
33static int
34update_attr(struct fuse *f, struct stat *attr, const char *realname,
35    struct fuse_vnode *vn)
36{
37	int ret;
38
39	memset(attr, 0, sizeof(struct stat));
40	ret = f->op.getattr(realname, attr);
41
42	if (attr->st_blksize == 0)
43		attr->st_blksize = 512;
44	if (attr->st_blocks == 0)
45		attr->st_blocks = 4;
46
47	if (!f->conf.use_ino)
48		attr->st_ino = vn->ino;
49
50	if (f->conf.set_mode)
51		attr->st_mode = (attr->st_mode & S_IFMT) | (0777 & ~f->conf.umask);
52
53	if (f->conf.set_uid)
54		attr->st_uid = f->conf.uid;
55
56	if (f->conf.set_gid)
57		attr->st_gid = f->conf.gid;
58
59	return (ret);
60}
61
62static int
63ifuse_ops_init(struct fuse *f)
64{
65	struct fuse_conn_info fci;
66
67	DPRINTF("Opcode: init\t");
68
69	if (f->op.init) {
70		memset(&fci, 0, sizeof(fci));
71		fci.proto_minor = FUSE_MINOR_VERSION;
72		fci.proto_major = FUSE_MAJOR_VERSION;
73
74		f->op.init(&fci);
75	}
76	return (0);
77}
78
79static int
80ifuse_ops_getattr(struct fuse *f, struct fusebuf *fbuf)
81{
82	struct fuse_vnode *vn;
83	char *realname;
84
85	DPRINTF("Opcode: getattr\t");
86	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
87
88	memset(&fbuf->fb_attr, 0, sizeof(struct stat));
89
90	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
91	if (vn == NULL) {
92		fbuf->fb_err = -errno;
93		return (0);
94	}
95
96	realname = build_realname(f, vn->ino);
97	if (realname == NULL) {
98		fbuf->fb_err = -errno;
99		return (0);
100	}
101
102	fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
103	free(realname);
104
105	return (0);
106}
107
108static int
109ifuse_ops_access(struct fuse *f, struct fusebuf *fbuf)
110{
111	struct fuse_vnode *vn;
112	char *realname;
113
114	CHECK_OPT(access);
115
116	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
117	if (vn == NULL) {
118		fbuf->fb_err = -errno;
119		return (0);
120	}
121
122	realname = build_realname(f, vn->ino);
123	if (realname == NULL) {
124		fbuf->fb_err = -errno;
125		return (0);
126	}
127
128	fbuf->fb_err = f->op.access(realname, fbuf->fb_io_mode);
129	free(realname);
130
131	return (0);
132}
133
134static int
135ifuse_ops_open(struct fuse *f, struct fusebuf *fbuf)
136{
137	struct fuse_file_info ffi;
138	struct fuse_vnode *vn;
139	char *realname;
140
141	CHECK_OPT(open);
142
143	memset(&ffi, 0, sizeof(ffi));
144	ffi.flags = fbuf->fb_io_flags;
145
146	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
147	if (vn == NULL) {
148		fbuf->fb_err = -errno;
149		return (0);
150	}
151
152	realname = build_realname(f, vn->ino);
153	if (realname == NULL) {
154		fbuf->fb_err = -errno;
155		return (0);
156	}
157
158	fbuf->fb_err = f->op.open(realname, &ffi);
159	free(realname);
160
161	if (!fbuf->fb_err)
162		fbuf->fb_io_fd = ffi.fh;
163
164	return (0);
165}
166
167static int
168ifuse_ops_opendir(struct fuse *f, struct fusebuf *fbuf)
169{
170	struct fuse_file_info ffi;
171	struct fuse_vnode *vn;
172	char *realname;
173
174	DPRINTF("Opcode: opendir\t");
175	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
176
177	memset(&ffi, 0, sizeof(ffi));
178	ffi.flags = fbuf->fb_io_flags;
179
180	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
181	if (vn == NULL) {
182		fbuf->fb_err = -errno;
183		return (0);
184	}
185
186	if (f->op.opendir) {
187		realname = build_realname(f, vn->ino);
188		if (realname == NULL) {
189			fbuf->fb_err = -errno;
190			return (0);
191		}
192
193		fbuf->fb_err = f->op.opendir(realname, &ffi);
194		free(realname);
195	}
196
197	if (!fbuf->fb_err)
198		fbuf->fb_io_fd = ffi.fh;
199
200	return (0);
201}
202
203#define GENERIC_DIRSIZ(NLEN) \
204((sizeof (struct dirent) - (MAXNAMLEN+1)) + ((NLEN+1 + 7) &~ 7))
205
206/*
207 * This function adds one directory entry to the buffer.
208 * FUSE file systems can implement readdir in one of two ways.
209 *
210 * 1. Read all directory entries in one operation. The off parameter
211 *    will always be 0 and this filler function always returns 0.
212 * 2. The file system keeps track of the directory entry offsets and
213 *    this filler function returns 1 when the buffer is full.
214 *
215 * OpenBSD currently supports 1. but will still call the file system's
216 * readdir function multiple times if either the kernel buffer or the
217 * buffer supplied by the calling application is too small to fit all
218 * entries. Each call to the file system's readdir function will fill
219 * the buffer with the next set of entries.
220 */
221static int
222ifuse_fill_readdir(void *dh, const char *name, const struct stat *stbuf,
223    off_t off)
224{
225	struct fuse *f;
226	struct fuse_dirhandle *fd = dh;
227	struct fuse_vnode *v;
228	struct fusebuf *fbuf;
229	struct dirent *dir;
230	uint32_t namelen;
231	uint32_t len;
232
233	f = fd->fuse;
234	fbuf = fd->buf;
235	namelen = strnlen(name, MAXNAMLEN);
236	len = GENERIC_DIRSIZ(namelen);
237
238	/* buffer is full so ignore the remaining entries */
239	if (fd->full || (fbuf->fb_len + len > fd->size)) {
240		fd->full = 1;
241		return (0);
242	}
243
244	/* already returned these entries in a previous call so skip */
245	if (fd->start != 0 && fd->idx < fd->start) {
246		fd->idx += len;
247		return (0);
248	}
249
250	dir = (struct dirent *) &fbuf->fb_dat[fbuf->fb_len];
251
252	if (stbuf != NULL && f->conf.use_ino)
253		dir->d_fileno = stbuf->st_ino;
254	else {
255		/*
256		 * This always behaves as if readdir_ino option is set so
257		 * getcwd(3) works.
258		 */
259		v = get_vn_by_name_and_parent(f, (uint8_t *)name, fbuf->fb_ino);
260		if (v == NULL) {
261			if (strcmp(name, ".") == 0)
262				dir->d_fileno = fbuf->fb_ino;
263			else
264				dir->d_fileno = 0xffffffff;
265		} else
266			dir->d_fileno = v->ino;
267	}
268
269	if (stbuf != NULL)
270		dir->d_type = IFTODT(stbuf->st_mode);
271	else
272		dir->d_type = DT_UNKNOWN;
273
274	dir->d_reclen = len;
275	dir->d_off = off + len;		/* XXX */
276	strlcpy(dir->d_name, name, sizeof(dir->d_name));
277	dir->d_namlen = strlen(dir->d_name);
278
279	fbuf->fb_len += len;
280	fd->idx += len;
281
282	return (0);
283}
284
285static int
286ifuse_fill_getdir(fuse_dirh_t fd, const char *name, int type, ino_t ino)
287{
288	struct stat st;
289
290	memset(&st, 0, sizeof(st));
291	st.st_mode = type << 12;
292	if (ino == 0)
293		st.st_ino = 0xffffffff;
294	else
295		st.st_ino = ino;
296
297	return (fd->filler(fd, name, &st, 0));
298}
299
300static int
301ifuse_ops_readdir(struct fuse *f, struct fusebuf *fbuf)
302{
303	struct fuse_file_info ffi;
304	struct fuse_dirhandle fd;
305	struct fuse_vnode *vn;
306	char *realname;
307	uint64_t offset;
308	uint32_t size;
309
310	DPRINTF("Opcode: readdir\t");
311	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
312	DPRINTF("Offset: %llu\t", fbuf->fb_io_off);
313	DPRINTF("Size: %lu\t", fbuf->fb_io_len);
314
315	memset(&ffi, 0, sizeof(ffi));
316	ffi.fh = fbuf->fb_io_fd;
317	offset = fbuf->fb_io_off;
318	size = fbuf->fb_io_len;
319
320	fbuf->fb_dat = calloc(1, size);
321
322	if (fbuf->fb_dat == NULL) {
323		fbuf->fb_err = -errno;
324		return (0);
325	}
326
327	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
328	if (vn == NULL) {
329		fbuf->fb_err = -errno;
330		free(fbuf->fb_dat);
331		return (0);
332	}
333
334	memset(&fd, 0, sizeof(fd));
335	fd.filler = ifuse_fill_readdir;
336	fd.buf = fbuf;
337	fd.full = 0;
338	fd.size = size;
339	fd.off = offset;
340	fd.idx = 0;
341	fd.fuse = f;
342	fd.start = offset;
343
344	realname = build_realname(f, vn->ino);
345	if (realname == NULL) {
346		fbuf->fb_err = -errno;
347		free(fbuf->fb_dat);
348		return (0);
349	}
350
351	if (f->op.readdir)
352		fbuf->fb_err = f->op.readdir(realname, &fd, ifuse_fill_readdir,
353		    offset, &ffi);
354	else if (f->op.getdir)
355		fbuf->fb_err = f->op.getdir(realname, &fd, ifuse_fill_getdir);
356	else
357		fbuf->fb_err = -ENOSYS;
358	free(realname);
359
360	if (fbuf->fb_err)
361		fbuf->fb_len = 0;
362	else if (fd.full && fbuf->fb_len == 0)
363		fbuf->fb_err = -ENOBUFS;
364
365	if (fbuf->fb_len == 0)
366		free(fbuf->fb_dat);
367
368	return (0);
369}
370
371static int
372ifuse_ops_releasedir(struct fuse *f, struct fusebuf *fbuf)
373{
374	struct fuse_file_info ffi;
375	struct fuse_vnode *vn;
376	char *realname;
377
378	DPRINTF("Opcode: releasedir\t");
379	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
380
381	memset(&ffi, 0, sizeof(ffi));
382	ffi.fh = fbuf->fb_io_fd;
383	ffi.fh_old = ffi.fh;
384	ffi.flags = fbuf->fb_io_flags;
385
386	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
387	if (vn == NULL) {
388		fbuf->fb_err = -errno;
389		return (0);
390	}
391
392	if (f->op.releasedir) {
393		realname = build_realname(f, vn->ino);
394		if (realname == NULL) {
395			fbuf->fb_err = -errno;
396			return (0);
397		}
398
399		fbuf->fb_err = f->op.releasedir(realname, &ffi);
400		free(realname);
401	}
402
403	return (0);
404}
405
406static int
407ifuse_ops_release(struct fuse *f, struct fusebuf *fbuf)
408{
409	struct fuse_file_info ffi;
410	struct fuse_vnode *vn;
411	char *realname;
412
413	CHECK_OPT(release);
414
415	memset(&ffi, 0, sizeof(ffi));
416	ffi.fh = fbuf->fb_io_fd;
417	ffi.fh_old = ffi.fh;
418	ffi.flags = fbuf->fb_io_flags;
419
420	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
421	if (vn == NULL) {
422		fbuf->fb_err = -errno;
423		return (0);
424	}
425
426	realname = build_realname(f, vn->ino);
427	if (realname == NULL) {
428		fbuf->fb_err = -errno;
429		return (0);
430	}
431	fbuf->fb_err = f->op.release(realname, &ffi);
432	free(realname);
433
434	return (0);
435}
436
437static int
438ifuse_ops_fsync(struct fuse *f, struct fusebuf *fbuf)
439{
440	struct fuse_file_info ffi;
441	struct fuse_vnode *vn;
442	char *realname;
443	int datasync;
444
445	CHECK_OPT(fsync);
446
447	memset(&ffi, 0, sizeof(ffi));
448	ffi.fh = fbuf->fb_io_fd;
449
450	/*
451	 * fdatasync(2) is just a wrapper around fsync(2) so datasync
452	 * is always false.
453	 */
454	datasync = 0;
455
456	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
457	if (vn == NULL) {
458		fbuf->fb_err = -errno;
459		return (0);
460	}
461
462	realname = build_realname(f, vn->ino);
463	if (realname == NULL) {
464		fbuf->fb_err = -errno;
465		return (0);
466	}
467	fbuf->fb_err = f->op.fsync(realname, datasync, &ffi);
468	free(realname);
469
470	return (0);
471}
472
473static int
474ifuse_ops_flush(struct fuse *f, struct fusebuf *fbuf)
475{
476	struct fuse_file_info ffi;
477	struct fuse_vnode *vn;
478	char *realname;
479
480	CHECK_OPT(flush);
481
482	memset(&ffi, 0, sizeof(ffi));
483	ffi.fh = fbuf->fb_io_fd;
484	ffi.fh_old = ffi.fh;
485	ffi.flush = 1;
486
487	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
488	if (vn == NULL) {
489		fbuf->fb_err = -errno;
490		return (0);
491	}
492
493	realname = build_realname(f, vn->ino);
494	if (realname == NULL) {
495		fbuf->fb_err = -errno;
496		return (0);
497	}
498	fbuf->fb_err = f->op.flush(realname, &ffi);
499	free(realname);
500
501	return (0);
502}
503
504static int
505ifuse_ops_lookup(struct fuse *f, struct fusebuf *fbuf)
506{
507	struct fuse_vnode *vn;
508	char *realname;
509
510	DPRINTF("Opcode: lookup\t");
511	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
512
513	if (strcmp((const char *)fbuf->fb_dat, "..") == 0) {
514		vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
515		if (vn == NULL || vn->parent == NULL) {
516			fbuf->fb_err = -ENOENT;
517			return (0);
518		}
519		vn = vn->parent;
520		if (vn->ino != FUSE_ROOT_INO)
521			ref_vn(vn);
522	} else {
523		vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
524		if (vn == NULL) {
525			vn = alloc_vn(f, (const char *)fbuf->fb_dat, -1,
526			    fbuf->fb_ino);
527			if (vn == NULL) {
528				fbuf->fb_err = -errno;
529				free(fbuf->fb_dat);
530				return (0);
531			}
532			set_vn(f, vn); /*XXX*/
533		} else if (vn->ino != FUSE_ROOT_INO)
534			ref_vn(vn);
535	}
536
537	realname = build_realname(f, vn->ino);
538	if (realname == NULL) {
539		fbuf->fb_err = -errno;
540		free(fbuf->fb_dat);
541		return (0);
542	}
543
544	fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
545	fbuf->fb_ino = vn->ino;
546	free(fbuf->fb_dat);
547	free(realname);
548
549	return (0);
550}
551
552static int
553ifuse_ops_read(struct fuse *f, struct fusebuf *fbuf)
554{
555	struct fuse_file_info ffi;
556	struct fuse_vnode *vn;
557	char *realname;
558	uint64_t offset;
559	uint32_t size;
560	int ret;
561
562	CHECK_OPT(read);
563
564	memset(&ffi, 0, sizeof(ffi));
565	ffi.fh = fbuf->fb_io_fd;
566	size = fbuf->fb_io_len;
567	offset = fbuf->fb_io_off;
568
569	fbuf->fb_dat = malloc(size);
570	if (fbuf->fb_dat == NULL) {
571		fbuf->fb_err = -errno;
572		return (0);
573	}
574
575	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
576	if (vn == NULL) {
577		fbuf->fb_err = -errno;
578		free(fbuf->fb_dat);
579		return (0);
580	}
581
582	realname = build_realname(f, vn->ino);
583	if (realname == NULL) {
584		fbuf->fb_err = -errno;
585		free(fbuf->fb_dat);
586		return (0);
587	}
588
589	ret = f->op.read(realname, (char *)fbuf->fb_dat, size, offset, &ffi);
590	free(realname);
591	if (ret >= 0)
592		fbuf->fb_len = ret;
593	else
594		fbuf->fb_err = ret;
595
596	if (fbuf->fb_len == 0)
597		free(fbuf->fb_dat);
598
599	return (0);
600}
601
602static int
603ifuse_ops_write(struct fuse *f, struct fusebuf *fbuf)
604{
605	struct fuse_file_info ffi;
606	struct fuse_vnode *vn;
607	char *realname;
608	uint64_t offset;
609	uint32_t size;
610	int ret;
611
612	CHECK_OPT(write);
613
614	memset(&ffi, 0, sizeof(ffi));
615	ffi.fh = fbuf->fb_io_fd;
616	ffi.fh_old = ffi.fh;
617	ffi.writepage = fbuf->fb_io_flags & 1;
618	size = fbuf->fb_io_len;
619	offset = fbuf->fb_io_off;
620
621	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
622	if (vn == NULL) {
623		fbuf->fb_err = -errno;
624		free(fbuf->fb_dat);
625		return (0);
626	}
627
628	realname = build_realname(f, vn->ino);
629	if (realname == NULL) {
630		fbuf->fb_err = -errno;
631		free(fbuf->fb_dat);
632		return (0);
633	}
634
635	ret = f->op.write(realname, (char *)fbuf->fb_dat, size, offset, &ffi);
636	free(realname);
637	free(fbuf->fb_dat);
638
639	if (ret >= 0)
640		fbuf->fb_io_len = ret;
641	else
642		fbuf->fb_err = ret;
643
644	return (0);
645}
646
647static int
648ifuse_ops_mkdir(struct fuse *f, struct fusebuf *fbuf)
649{
650	struct fuse_vnode *vn;
651	char *realname;
652	uint32_t mode;
653
654	CHECK_OPT(mkdir);
655
656	mode = fbuf->fb_io_mode;
657	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
658	if (vn == NULL) {
659		fbuf->fb_err = -errno;
660		free(fbuf->fb_dat);
661		return (0);
662	}
663
664	free(fbuf->fb_dat);
665	realname = build_realname(f, vn->ino);
666	if (realname == NULL) {
667		fbuf->fb_err = -errno;
668		return (0);
669	}
670
671	fbuf->fb_err = f->op.mkdir(realname, mode);
672
673	if (!fbuf->fb_err) {
674		fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
675		fbuf->fb_io_mode = fbuf->fb_attr.st_mode;
676		fbuf->fb_ino = vn->ino;
677	}
678	free(realname);
679
680	return (0);
681}
682
683static int
684ifuse_ops_rmdir(struct fuse *f, struct fusebuf *fbuf)
685{
686	struct fuse_vnode *vn;
687	char *realname;
688
689	CHECK_OPT(rmdir);
690	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
691	if (vn == NULL) {
692		fbuf->fb_err = -errno;
693		free(fbuf->fb_dat);
694		return (0);
695	}
696
697	free(fbuf->fb_dat);
698	realname = build_realname(f, vn->ino);
699	if (realname == NULL) {
700		fbuf->fb_err = -errno;
701		return (0);
702	}
703
704	fbuf->fb_err = f->op.rmdir(realname);
705	free(realname);
706
707	return (0);
708}
709
710static int
711ifuse_ops_readlink(struct fuse *f, struct fusebuf *fbuf)
712{
713	struct fuse_vnode *vn;
714	char *realname;
715	char name[PATH_MAX + 1];
716	int len, ret;
717
718	DPRINTF("Opcode: readlink\t");
719	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
720
721	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
722	if (vn == NULL) {
723		fbuf->fb_err = -errno;
724		return (0);
725	}
726
727	realname = build_realname(f, vn->ino);
728	if (realname == NULL) {
729		fbuf->fb_err = -errno;
730		return (0);
731	}
732
733	if (f->op.readlink)
734		ret = f->op.readlink(realname, name, sizeof(name));
735	else
736		ret = -ENOSYS;
737	free(realname);
738
739	fbuf->fb_err = ret;
740	if (!ret) {
741		len = strnlen(name, PATH_MAX);
742		fbuf->fb_len = len;
743		fbuf->fb_dat = malloc(fbuf->fb_len);
744		if (fbuf->fb_dat == NULL) {
745			fbuf->fb_err = -errno;
746			return (0);
747		}
748		memcpy(fbuf->fb_dat, name, len);
749	} else
750		fbuf->fb_len = 0;
751
752	return (0);
753}
754
755static int
756ifuse_ops_unlink(struct fuse *f, struct fusebuf *fbuf)
757{
758	struct fuse_vnode *vn;
759	char *realname;
760
761	CHECK_OPT(unlink);
762
763	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
764	if (vn == NULL) {
765		free(fbuf->fb_dat);
766		fbuf->fb_err = -errno;
767		return (0);
768	}
769
770	free(fbuf->fb_dat);
771	realname = build_realname(f, vn->ino);
772	if (realname == NULL) {
773		fbuf->fb_err = -errno;
774		return (0);
775	}
776
777	fbuf->fb_err = f->op.unlink(realname);
778	free(realname);
779
780	return (0);
781}
782
783static int
784ifuse_ops_statfs(struct fuse *f, struct fusebuf *fbuf)
785{
786	struct fuse_vnode *vn;
787	char *realname;
788
789	memset(&fbuf->fb_stat, 0, sizeof(fbuf->fb_stat));
790
791	CHECK_OPT(statfs);
792
793	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
794	if (vn == NULL) {
795		fbuf->fb_err = -errno;
796		return (0);
797	}
798
799	realname = build_realname(f, vn->ino);
800	if (realname == NULL) {
801		fbuf->fb_err = -errno;
802		return (0);
803	}
804
805	fbuf->fb_err = f->op.statfs(realname, &fbuf->fb_stat);
806	free(realname);
807
808	return (0);
809}
810
811static int
812ifuse_ops_link(struct fuse *f, struct fusebuf *fbuf)
813{
814	struct fuse_vnode *vn;
815	char *realname;
816	char *realname_ln;
817	ino_t oldnodeid;
818
819	CHECK_OPT(link);
820	oldnodeid = fbuf->fb_io_ino;
821	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
822	if (vn == NULL) {
823		fbuf->fb_err = -errno;
824		free(fbuf->fb_dat);
825		return (0);
826	}
827
828	free(fbuf->fb_dat);
829	realname = build_realname(f, oldnodeid);
830	if (realname == NULL) {
831		fbuf->fb_err = -errno;
832		return (0);
833	}
834
835	realname_ln = build_realname(f, vn->ino);
836	if (realname_ln == NULL) {
837		fbuf->fb_err = -errno;
838		free(realname);
839		return (0);
840	}
841
842	fbuf->fb_err = f->op.link(realname, realname_ln);
843	free(realname);
844	free(realname_ln);
845
846	return (0);
847}
848
849static int
850ifuse_ops_setattr(struct fuse *f, struct fusebuf *fbuf)
851{
852	struct fuse_vnode *vn;
853	struct timespec ts[2];
854	struct utimbuf tbuf;
855	struct fb_io *io;
856	char *realname;
857	uid_t uid;
858	gid_t gid;
859
860	DPRINTF("Opcode: setattr\t");
861	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
862
863	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
864	if (vn == NULL) {
865		fbuf->fb_err = -errno;
866		free(fbuf->fb_dat);
867		return (0);
868	}
869
870	realname = build_realname(f, vn->ino);
871	if (realname == NULL) {
872		fbuf->fb_err = -errno;
873		free(fbuf->fb_dat);
874		return (0);
875	}
876	io = fbtod(fbuf, struct fb_io *);
877
878	if (io->fi_flags & FUSE_FATTR_MODE) {
879		if (f->op.chmod)
880			fbuf->fb_err = f->op.chmod(realname,
881			    fbuf->fb_attr.st_mode);
882		else
883			fbuf->fb_err = -ENOSYS;
884	}
885
886	if (!fbuf->fb_err && (io->fi_flags & FUSE_FATTR_UID ||
887	    io->fi_flags & FUSE_FATTR_GID) ) {
888		uid = (io->fi_flags & FUSE_FATTR_UID) ?
889		    fbuf->fb_attr.st_uid : (uid_t)-1;
890		gid = (io->fi_flags & FUSE_FATTR_GID) ?
891		    fbuf->fb_attr.st_gid : (gid_t)-1;
892		if (f->op.chown)
893			fbuf->fb_err = f->op.chown(realname, uid, gid);
894		else
895			fbuf->fb_err = -ENOSYS;
896	}
897
898	if (!fbuf->fb_err && ( io->fi_flags & FUSE_FATTR_MTIME ||
899		io->fi_flags & FUSE_FATTR_ATIME)) {
900
901		if (f->op.utimens) {
902			ts[0] = fbuf->fb_attr.st_atim;
903			ts[1] = fbuf->fb_attr.st_mtim;
904			fbuf->fb_err = f->op.utimens(realname, ts);
905		} else if (f->op.utime) {
906			tbuf.actime = fbuf->fb_attr.st_atim.tv_sec;
907			tbuf.modtime = fbuf->fb_attr.st_mtim.tv_sec;
908			fbuf->fb_err = f->op.utime(realname, &tbuf);
909		} else
910			fbuf->fb_err = -ENOSYS;
911	}
912
913	if (!fbuf->fb_err && (io->fi_flags & FUSE_FATTR_SIZE)) {
914		if (f->op.truncate)
915			fbuf->fb_err = f->op.truncate(realname,
916			    fbuf->fb_attr.st_size);
917		else
918			fbuf->fb_err = -ENOSYS;
919	}
920
921	memset(&fbuf->fb_attr, 0, sizeof(struct stat));
922
923	if (!fbuf->fb_err)
924		fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
925	free(realname);
926	free(fbuf->fb_dat);
927
928	return (0);
929}
930
931static int
932ifuse_ops_symlink(unused struct fuse *f, struct fusebuf *fbuf)
933{
934	struct fuse_vnode *vn;
935	char *realname;
936	int len;
937
938	CHECK_OPT(symlink);
939
940	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
941	if (vn == NULL) {
942		fbuf->fb_err = -errno;
943		free(fbuf->fb_dat);
944		return (0);
945	}
946
947	len = strlen((char *)fbuf->fb_dat);
948
949	realname = build_realname(f, vn->ino);
950	if (realname == NULL) {
951		fbuf->fb_err = -errno;
952		free(fbuf->fb_dat);
953		return (0);
954	}
955
956	/* fuse invert the symlink params */
957	fbuf->fb_err = f->op.symlink((const char *)&fbuf->fb_dat[len + 1],
958	    realname);
959	fbuf->fb_ino = vn->ino;
960	free(fbuf->fb_dat);
961	free(realname);
962
963	return (0);
964}
965
966static int
967ifuse_ops_rename(struct fuse *f, struct fusebuf *fbuf)
968{
969	struct fuse_vnode *vnt;
970	struct fuse_vnode *vnf;
971	char *realnamef;
972	char *realnamet;
973	int len;
974
975	CHECK_OPT(rename);
976
977	len = strlen((char *)fbuf->fb_dat);
978	vnf = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
979	if (vnf == NULL) {
980		fbuf->fb_err = -errno;
981		free(fbuf->fb_dat);
982		return (0);
983	}
984
985	vnt = get_vn_by_name_and_parent(f, &fbuf->fb_dat[len + 1],
986	    fbuf->fb_io_ino);
987	if (vnt == NULL) {
988		fbuf->fb_err = -errno;
989		free(fbuf->fb_dat);
990		return (0);
991	}
992
993	free(fbuf->fb_dat);
994
995	realnamef = build_realname(f, vnf->ino);
996	if (realnamef == NULL) {
997		fbuf->fb_err = -errno;
998		return (0);
999	}
1000
1001	realnamet = build_realname(f, vnt->ino);
1002	if (realnamet == NULL) {
1003		fbuf->fb_err = -errno;
1004		free(realnamef);
1005		return (0);
1006	}
1007
1008	fbuf->fb_err = f->op.rename(realnamef, realnamet);
1009	free(realnamef);
1010	free(realnamet);
1011
1012	return (0);
1013}
1014
1015static int
1016ifuse_ops_destroy(struct fuse *f)
1017{
1018	struct fuse_context *ctx;
1019
1020	DPRINTF("Opcode: destroy\n");
1021
1022	if (f->op.destroy) {
1023		ctx = fuse_get_context();
1024
1025		f->op.destroy((ctx)?ctx->private_data:NULL);
1026	}
1027
1028	f->fc->dead = 1;
1029
1030	return (0);
1031}
1032
1033static int
1034ifuse_ops_reclaim(struct fuse *f, struct fusebuf *fbuf)
1035{
1036	struct fuse_vnode *vn;
1037
1038	DPRINTF("Opcode: reclaim\t");
1039	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
1040
1041	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
1042	if (vn != NULL)
1043		unref_vn(f, vn);
1044
1045	return (0);
1046}
1047
1048static int
1049ifuse_ops_mknod(struct fuse *f, struct fusebuf *fbuf)
1050{
1051	struct fuse_vnode *vn;
1052	char *realname;
1053	uint32_t mode;
1054	dev_t dev;
1055
1056	CHECK_OPT(mknod);
1057
1058	mode = fbuf->fb_io_mode;
1059	dev = fbuf->fb_io_rdev;
1060	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
1061	if (vn == NULL) {
1062		fbuf->fb_err = -errno;
1063		free(fbuf->fb_dat);
1064		return (0);
1065	}
1066
1067	free(fbuf->fb_dat);
1068	realname = build_realname(f, vn->ino);
1069	if (realname == NULL) {
1070		fbuf->fb_err = -errno;
1071		return (0);
1072	}
1073
1074	fbuf->fb_err = f->op.mknod(realname, mode, dev);
1075
1076	if (!fbuf->fb_err) {
1077		fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
1078		fbuf->fb_io_mode = fbuf->fb_attr.st_mode;
1079		fbuf->fb_ino = vn->ino;
1080	}
1081	free(realname);
1082
1083	return (0);
1084}
1085
1086int
1087ifuse_exec_opcode(struct fuse *f, struct fusebuf *fbuf)
1088{
1089	int ret = 0;
1090
1091	fbuf->fb_len = 0;
1092	fbuf->fb_err = 0;
1093
1094	switch (fbuf->fb_type) {
1095	case FBT_LOOKUP:
1096		ret = ifuse_ops_lookup(f, fbuf);
1097		break;
1098	case FBT_GETATTR:
1099		ret = ifuse_ops_getattr(f, fbuf);
1100		break;
1101	case FBT_SETATTR:
1102		ret = ifuse_ops_setattr(f, fbuf);
1103		break;
1104	case FBT_READLINK:
1105		ret = ifuse_ops_readlink(f, fbuf);
1106		break;
1107	case FBT_MKDIR:
1108		ret = ifuse_ops_mkdir(f, fbuf);
1109		break;
1110	case FBT_UNLINK:
1111		ret = ifuse_ops_unlink(f, fbuf);
1112		break;
1113	case FBT_RMDIR:
1114		ret = ifuse_ops_rmdir(f, fbuf);
1115		break;
1116	case FBT_LINK:
1117		ret = ifuse_ops_link(f, fbuf);
1118		break;
1119	case FBT_OPEN:
1120		ret = ifuse_ops_open(f, fbuf);
1121		break;
1122	case FBT_READ:
1123		ret = ifuse_ops_read(f, fbuf);
1124		break;
1125	case FBT_WRITE:
1126		ret = ifuse_ops_write(f, fbuf);
1127		break;
1128	case FBT_STATFS:
1129		ret = ifuse_ops_statfs(f, fbuf);
1130		break;
1131	case FBT_RELEASE:
1132		ret = ifuse_ops_release(f, fbuf);
1133		break;
1134	case FBT_FSYNC:
1135		ret = ifuse_ops_fsync(f, fbuf);
1136		break;
1137	case FBT_FLUSH:
1138		ret = ifuse_ops_flush(f, fbuf);
1139		break;
1140	case FBT_INIT:
1141		ret = ifuse_ops_init(f);
1142		break;
1143	case FBT_OPENDIR:
1144		ret = ifuse_ops_opendir(f, fbuf);
1145		break;
1146	case FBT_READDIR:
1147		ret = ifuse_ops_readdir(f, fbuf);
1148		break;
1149	case FBT_RELEASEDIR:
1150		ret = ifuse_ops_releasedir(f, fbuf);
1151		break;
1152	case FBT_ACCESS:
1153		ret = ifuse_ops_access(f, fbuf);
1154		break;
1155	case FBT_SYMLINK:
1156		ret = ifuse_ops_symlink(f, fbuf);
1157		break;
1158	case FBT_RENAME:
1159		ret = ifuse_ops_rename(f, fbuf);
1160		break;
1161	case FBT_DESTROY:
1162		ret = ifuse_ops_destroy(f);
1163		break;
1164	case FBT_RECLAIM:
1165		ret = ifuse_ops_reclaim(f, fbuf);
1166		break;
1167	case FBT_MKNOD:
1168		ret = ifuse_ops_mknod(f, fbuf);
1169		break;
1170	default:
1171		DPRINTF("Opcode: %i not supported\t", fbuf->fb_type);
1172		DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
1173
1174		fbuf->fb_err = -ENOSYS;
1175		fbuf->fb_len = 0;
1176	}
1177	DPRINTF("\n");
1178
1179	/* fuse api use negative errno */
1180	fbuf->fb_err = -fbuf->fb_err;
1181	return (ret);
1182}
1183