Edit

IABSD.fr/src/lib/libfuse/fuse_ops.c

Branch :

  • Show log

    Commit

  • Author : helg
    Date : 2018-07-16 13:10:53
    Hash : 0d644658
    Message : Implement FBT_FSYNC, which is called on fsync(2) and fdatasync(2). Currently ignores the a_waitfor argument and always invokes the file system's fsync implementation synchronously. ok mpi@

  • lib/libfuse/fuse_ops.c
  • /* $OpenBSD: fuse_ops.c,v 1.35 2018/07/16 13:10:53 helg Exp $ */
    /*
     * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    
    #include "fuse_private.h"
    #include "debug.h"
    
    #define CHECK_OPT(opname)	DPRINTF("Opcode: %s\t", #opname);	\
    				DPRINTF("Inode: %llu\t",		\
    				    (unsigned long long)fbuf->fb_ino);	\
    				if (!f->op.opname) {			\
    					fbuf->fb_err = -ENOSYS;		\
    					return (0);			\
    				}
    
    static int
    update_attr(struct fuse *f, struct stat *attr, const char *realname,
        struct fuse_vnode *vn)
    {
    	int ret;
    
    	memset(attr, 0, sizeof(struct stat));
    	ret = f->op.getattr(realname, attr);
    
    	if (attr->st_blksize == 0)
    		attr->st_blksize = 512;
    	if (attr->st_blocks == 0)
    		attr->st_blocks = 4;
    
    	if (!f->conf.use_ino)
    		attr->st_ino = vn->ino;
    
    	if (f->conf.set_mode)
    		attr->st_mode = (attr->st_mode & S_IFMT) | (0777 & ~f->conf.umask);
    
    	if (f->conf.set_uid)
    		attr->st_uid = f->conf.uid;
    
    	if (f->conf.set_gid)
    		attr->st_gid = f->conf.gid;
    
    	return (ret);
    }
    
    static int
    ifuse_ops_init(struct fuse *f)
    {
    	struct fuse_conn_info fci;
    
    	DPRINTF("Opcode: init\t");
    
    	if (f->op.init) {
    		memset(&fci, 0, sizeof(fci));
    		fci.proto_minor = FUSE_MINOR_VERSION;
    		fci.proto_major = FUSE_MAJOR_VERSION;
    
    		f->op.init(&fci);
    	}
    	return (0);
    }
    
    static int
    ifuse_ops_getattr(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    
    	DPRINTF("Opcode: getattr\t");
    	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
    
    	memset(&fbuf->fb_attr, 0, sizeof(struct stat));
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_access(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    
    	CHECK_OPT(access);
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	fbuf->fb_err = f->op.access(realname, fbuf->fb_io_mode);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_open(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_file_info ffi;
    	struct fuse_vnode *vn;
    	char *realname;
    
    	CHECK_OPT(open);
    
    	memset(&ffi, 0, sizeof(ffi));
    	ffi.flags = fbuf->fb_io_flags;
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	fbuf->fb_err = f->op.open(realname, &ffi);
    	free(realname);
    
    	if (!fbuf->fb_err)
    		fbuf->fb_io_fd = ffi.fh;
    
    	return (0);
    }
    
    static int
    ifuse_ops_opendir(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_file_info ffi;
    	struct fuse_vnode *vn;
    	char *realname;
    
    	DPRINTF("Opcode: opendir\t");
    	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
    
    	memset(&ffi, 0, sizeof(ffi));
    	ffi.flags = fbuf->fb_io_flags;
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	if (f->op.opendir) {
    		realname = build_realname(f, vn->ino);
    		if (realname == NULL) {
    			fbuf->fb_err = -errno;
    			return (0);
    		}
    
    		fbuf->fb_err = f->op.opendir(realname, &ffi);
    		free(realname);
    	}
    
    	if (!fbuf->fb_err)
    		fbuf->fb_io_fd = ffi.fh;
    
    	return (0);
    }
    
    #define GENERIC_DIRSIZ(NLEN) \
    ((sizeof (struct dirent) - (MAXNAMLEN+1)) + ((NLEN+1 + 7) &~ 7))
    
    /*
     * This function adds one directory entry to the buffer.
     * FUSE file systems can implement readdir in one of two ways.
     *
     * 1. Read all directory entries in one operation. The off parameter
     *    will always be 0 and this filler function always returns 0.
     * 2. The file system keeps track of the directory entry offsets and
     *    this filler function returns 1 when the buffer is full.
     *
     * OpenBSD currently supports 1. but will still call the file system's
     * readdir function multiple times if either the kernel buffer or the
     * buffer supplied by the calling application is too small to fit all
     * entries. Each call to the file system's readdir function will fill
     * the buffer with the next set of entries.
     */
    static int
    ifuse_fill_readdir(void *dh, const char *name, const struct stat *stbuf,
        off_t off)
    {
    	struct fuse *f;
    	struct fuse_dirhandle *fd = dh;
    	struct fuse_vnode *v;
    	struct fusebuf *fbuf;
    	struct dirent *dir;
    	uint32_t namelen;
    	uint32_t len;
    
    	f = fd->fuse;
    	fbuf = fd->buf;
    	namelen = strnlen(name, MAXNAMLEN);
    	len = GENERIC_DIRSIZ(namelen);
    
    	/* buffer is full so ignore the remaining entries */
    	if (fd->full || (fbuf->fb_len + len > fd->size)) {
    		fd->full = 1;
    		return (0);
    	}
    
    	/* already returned these entries in a previous call so skip */
    	if (fd->start != 0 && fd->idx < fd->start) {
    		fd->idx += len;
    		return (0);
    	}
    
    	dir = (struct dirent *) &fbuf->fb_dat[fbuf->fb_len];
    
    	if (stbuf != NULL && f->conf.use_ino)
    		dir->d_fileno = stbuf->st_ino;
    	else {
    		/*
    		 * This always behaves as if readdir_ino option is set so
    		 * getcwd(3) works.
    		 */
    		v = get_vn_by_name_and_parent(f, (uint8_t *)name, fbuf->fb_ino);
    		if (v == NULL) {
    			if (strcmp(name, ".") == 0)
    				dir->d_fileno = fbuf->fb_ino;
    			else
    				dir->d_fileno = 0xffffffff;
    		} else
    			dir->d_fileno = v->ino;
    	}
    
    	if (stbuf != NULL)
    		dir->d_type = IFTODT(stbuf->st_mode);
    	else
    		dir->d_type = DT_UNKNOWN;
    
    	dir->d_reclen = len;
    	dir->d_off = off + len;		/* XXX */
    	strlcpy(dir->d_name, name, sizeof(dir->d_name));
    	dir->d_namlen = strlen(dir->d_name);
    
    	fbuf->fb_len += len;
    	fd->idx += len;
    
    	return (0);
    }
    
    static int
    ifuse_fill_getdir(fuse_dirh_t fd, const char *name, int type, ino_t ino)
    {
    	struct stat st;
    
    	memset(&st, 0, sizeof(st));
    	st.st_mode = type << 12;
    	if (ino == 0)
    		st.st_ino = 0xffffffff;
    	else
    		st.st_ino = ino;
    
    	return (fd->filler(fd, name, &st, 0));
    }
    
    static int
    ifuse_ops_readdir(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_file_info ffi;
    	struct fuse_dirhandle fd;
    	struct fuse_vnode *vn;
    	char *realname;
    	uint64_t offset;
    	uint32_t size;
    
    	DPRINTF("Opcode: readdir\t");
    	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
    	DPRINTF("Offset: %llu\t", fbuf->fb_io_off);
    	DPRINTF("Size: %lu\t", fbuf->fb_io_len);
    
    	memset(&ffi, 0, sizeof(ffi));
    	ffi.fh = fbuf->fb_io_fd;
    	offset = fbuf->fb_io_off;
    	size = fbuf->fb_io_len;
    
    	fbuf->fb_dat = calloc(1, size);
    
    	if (fbuf->fb_dat == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	memset(&fd, 0, sizeof(fd));
    	fd.filler = ifuse_fill_readdir;
    	fd.buf = fbuf;
    	fd.full = 0;
    	fd.size = size;
    	fd.off = offset;
    	fd.idx = 0;
    	fd.fuse = f;
    	fd.start = offset;
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	if (f->op.readdir)
    		fbuf->fb_err = f->op.readdir(realname, &fd, ifuse_fill_readdir,
    		    offset, &ffi);
    	else if (f->op.getdir)
    		fbuf->fb_err = f->op.getdir(realname, &fd, ifuse_fill_getdir);
    	else
    		fbuf->fb_err = -ENOSYS;
    	free(realname);
    
    	if (fbuf->fb_err)
    		fbuf->fb_len = 0;
    	else if (fd.full && fbuf->fb_len == 0)
    		fbuf->fb_err = -ENOBUFS;
    
    	if (fbuf->fb_len == 0)
    		free(fbuf->fb_dat);
    
    	return (0);
    }
    
    static int
    ifuse_ops_releasedir(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_file_info ffi;
    	struct fuse_vnode *vn;
    	char *realname;
    
    	DPRINTF("Opcode: releasedir\t");
    	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
    
    	memset(&ffi, 0, sizeof(ffi));
    	ffi.fh = fbuf->fb_io_fd;
    	ffi.fh_old = ffi.fh;
    	ffi.flags = fbuf->fb_io_flags;
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	if (f->op.releasedir) {
    		realname = build_realname(f, vn->ino);
    		if (realname == NULL) {
    			fbuf->fb_err = -errno;
    			return (0);
    		}
    
    		fbuf->fb_err = f->op.releasedir(realname, &ffi);
    		free(realname);
    	}
    
    	return (0);
    }
    
    static int
    ifuse_ops_release(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_file_info ffi;
    	struct fuse_vnode *vn;
    	char *realname;
    
    	CHECK_OPT(release);
    
    	memset(&ffi, 0, sizeof(ffi));
    	ffi.fh = fbuf->fb_io_fd;
    	ffi.fh_old = ffi.fh;
    	ffi.flags = fbuf->fb_io_flags;
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    	fbuf->fb_err = f->op.release(realname, &ffi);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_fsync(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_file_info ffi;
    	struct fuse_vnode *vn;
    	char *realname;
    	int datasync;
    
    	CHECK_OPT(fsync);
    
    	memset(&ffi, 0, sizeof(ffi));
    	ffi.fh = fbuf->fb_io_fd;
    
    	/*
    	 * fdatasync(2) is just a wrapper around fsync(2) so datasync
    	 * is always false.
    	 */
    	datasync = 0;
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    	fbuf->fb_err = f->op.fsync(realname, datasync, &ffi);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_flush(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_file_info ffi;
    	struct fuse_vnode *vn;
    	char *realname;
    
    	CHECK_OPT(flush);
    
    	memset(&ffi, 0, sizeof(ffi));
    	ffi.fh = fbuf->fb_io_fd;
    	ffi.fh_old = ffi.fh;
    	ffi.flush = 1;
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    	fbuf->fb_err = f->op.flush(realname, &ffi);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_lookup(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    
    	DPRINTF("Opcode: lookup\t");
    	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
    
    	if (strcmp((const char *)fbuf->fb_dat, "..") == 0) {
    		vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    		if (vn == NULL || vn->parent == NULL) {
    			fbuf->fb_err = -ENOENT;
    			return (0);
    		}
    		vn = vn->parent;
    		if (vn->ino != FUSE_ROOT_INO)
    			ref_vn(vn);
    	} else {
    		vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
    		if (vn == NULL) {
    			vn = alloc_vn(f, (const char *)fbuf->fb_dat, -1,
    			    fbuf->fb_ino);
    			if (vn == NULL) {
    				fbuf->fb_err = -errno;
    				free(fbuf->fb_dat);
    				return (0);
    			}
    			set_vn(f, vn); /*XXX*/
    		} else if (vn->ino != FUSE_ROOT_INO)
    			ref_vn(vn);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
    	fbuf->fb_ino = vn->ino;
    	free(fbuf->fb_dat);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_read(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_file_info ffi;
    	struct fuse_vnode *vn;
    	char *realname;
    	uint64_t offset;
    	uint32_t size;
    	int ret;
    
    	CHECK_OPT(read);
    
    	memset(&ffi, 0, sizeof(ffi));
    	ffi.fh = fbuf->fb_io_fd;
    	size = fbuf->fb_io_len;
    	offset = fbuf->fb_io_off;
    
    	fbuf->fb_dat = malloc(size);
    	if (fbuf->fb_dat == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	ret = f->op.read(realname, (char *)fbuf->fb_dat, size, offset, &ffi);
    	free(realname);
    	if (ret >= 0)
    		fbuf->fb_len = ret;
    	else
    		fbuf->fb_err = ret;
    
    	if (fbuf->fb_len == 0)
    		free(fbuf->fb_dat);
    
    	return (0);
    }
    
    static int
    ifuse_ops_write(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_file_info ffi;
    	struct fuse_vnode *vn;
    	char *realname;
    	uint64_t offset;
    	uint32_t size;
    	int ret;
    
    	CHECK_OPT(write);
    
    	memset(&ffi, 0, sizeof(ffi));
    	ffi.fh = fbuf->fb_io_fd;
    	ffi.fh_old = ffi.fh;
    	ffi.writepage = fbuf->fb_io_flags & 1;
    	size = fbuf->fb_io_len;
    	offset = fbuf->fb_io_off;
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	ret = f->op.write(realname, (char *)fbuf->fb_dat, size, offset, &ffi);
    	free(realname);
    	free(fbuf->fb_dat);
    
    	if (ret >= 0)
    		fbuf->fb_io_len = ret;
    	else
    		fbuf->fb_err = ret;
    
    	return (0);
    }
    
    static int
    ifuse_ops_mkdir(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    	uint32_t mode;
    
    	CHECK_OPT(mkdir);
    
    	mode = fbuf->fb_io_mode;
    	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	free(fbuf->fb_dat);
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	fbuf->fb_err = f->op.mkdir(realname, mode);
    
    	if (!fbuf->fb_err) {
    		fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
    		fbuf->fb_io_mode = fbuf->fb_attr.st_mode;
    		fbuf->fb_ino = vn->ino;
    	}
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_rmdir(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    
    	CHECK_OPT(rmdir);
    	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	free(fbuf->fb_dat);
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	fbuf->fb_err = f->op.rmdir(realname);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_readlink(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    	char name[PATH_MAX + 1];
    	int len, ret;
    
    	DPRINTF("Opcode: readlink\t");
    	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	if (f->op.readlink)
    		ret = f->op.readlink(realname, name, sizeof(name));
    	else
    		ret = -ENOSYS;
    	free(realname);
    
    	fbuf->fb_err = ret;
    	if (!ret) {
    		len = strnlen(name, PATH_MAX);
    		fbuf->fb_len = len;
    		fbuf->fb_dat = malloc(fbuf->fb_len);
    		if (fbuf->fb_dat == NULL) {
    			fbuf->fb_err = -errno;
    			return (0);
    		}
    		memcpy(fbuf->fb_dat, name, len);
    	} else
    		fbuf->fb_len = 0;
    
    	return (0);
    }
    
    static int
    ifuse_ops_unlink(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    
    	CHECK_OPT(unlink);
    
    	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
    	if (vn == NULL) {
    		free(fbuf->fb_dat);
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	free(fbuf->fb_dat);
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	fbuf->fb_err = f->op.unlink(realname);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_statfs(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    
    	memset(&fbuf->fb_stat, 0, sizeof(fbuf->fb_stat));
    
    	CHECK_OPT(statfs);
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	fbuf->fb_err = f->op.statfs(realname, &fbuf->fb_stat);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_link(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    	char *realname_ln;
    	ino_t oldnodeid;
    
    	CHECK_OPT(link);
    	oldnodeid = fbuf->fb_io_ino;
    	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	free(fbuf->fb_dat);
    	realname = build_realname(f, oldnodeid);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realname_ln = build_realname(f, vn->ino);
    	if (realname_ln == NULL) {
    		fbuf->fb_err = -errno;
    		free(realname);
    		return (0);
    	}
    
    	fbuf->fb_err = f->op.link(realname, realname_ln);
    	free(realname);
    	free(realname_ln);
    
    	return (0);
    }
    
    static int
    ifuse_ops_setattr(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	struct timespec ts[2];
    	struct utimbuf tbuf;
    	struct fb_io *io;
    	char *realname;
    	uid_t uid;
    	gid_t gid;
    
    	DPRINTF("Opcode: setattr\t");
    	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    	io = fbtod(fbuf, struct fb_io *);
    
    	if (io->fi_flags & FUSE_FATTR_MODE) {
    		if (f->op.chmod)
    			fbuf->fb_err = f->op.chmod(realname,
    			    fbuf->fb_attr.st_mode);
    		else
    			fbuf->fb_err = -ENOSYS;
    	}
    
    	if (!fbuf->fb_err && (io->fi_flags & FUSE_FATTR_UID ||
    	    io->fi_flags & FUSE_FATTR_GID) ) {
    		uid = (io->fi_flags & FUSE_FATTR_UID) ?
    		    fbuf->fb_attr.st_uid : (uid_t)-1;
    		gid = (io->fi_flags & FUSE_FATTR_GID) ?
    		    fbuf->fb_attr.st_gid : (gid_t)-1;
    		if (f->op.chown)
    			fbuf->fb_err = f->op.chown(realname, uid, gid);
    		else
    			fbuf->fb_err = -ENOSYS;
    	}
    
    	if (!fbuf->fb_err && ( io->fi_flags & FUSE_FATTR_MTIME ||
    		io->fi_flags & FUSE_FATTR_ATIME)) {
    
    		if (f->op.utimens) {
    			ts[0] = fbuf->fb_attr.st_atim;
    			ts[1] = fbuf->fb_attr.st_mtim;
    			fbuf->fb_err = f->op.utimens(realname, ts);
    		} else if (f->op.utime) {
    			tbuf.actime = fbuf->fb_attr.st_atim.tv_sec;
    			tbuf.modtime = fbuf->fb_attr.st_mtim.tv_sec;
    			fbuf->fb_err = f->op.utime(realname, &tbuf);
    		} else
    			fbuf->fb_err = -ENOSYS;
    	}
    
    	if (!fbuf->fb_err && (io->fi_flags & FUSE_FATTR_SIZE)) {
    		if (f->op.truncate)
    			fbuf->fb_err = f->op.truncate(realname,
    			    fbuf->fb_attr.st_size);
    		else
    			fbuf->fb_err = -ENOSYS;
    	}
    
    	memset(&fbuf->fb_attr, 0, sizeof(struct stat));
    
    	if (!fbuf->fb_err)
    		fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
    	free(realname);
    	free(fbuf->fb_dat);
    
    	return (0);
    }
    
    static int
    ifuse_ops_symlink(unused struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    	int len;
    
    	CHECK_OPT(symlink);
    
    	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	len = strlen((char *)fbuf->fb_dat);
    
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	/* fuse invert the symlink params */
    	fbuf->fb_err = f->op.symlink((const char *)&fbuf->fb_dat[len + 1],
    	    realname);
    	fbuf->fb_ino = vn->ino;
    	free(fbuf->fb_dat);
    	free(realname);
    
    	return (0);
    }
    
    static int
    ifuse_ops_rename(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vnt;
    	struct fuse_vnode *vnf;
    	char *realnamef;
    	char *realnamet;
    	int len;
    
    	CHECK_OPT(rename);
    
    	len = strlen((char *)fbuf->fb_dat);
    	vnf = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
    	if (vnf == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	vnt = get_vn_by_name_and_parent(f, &fbuf->fb_dat[len + 1],
    	    fbuf->fb_io_ino);
    	if (vnt == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	free(fbuf->fb_dat);
    
    	realnamef = build_realname(f, vnf->ino);
    	if (realnamef == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	realnamet = build_realname(f, vnt->ino);
    	if (realnamet == NULL) {
    		fbuf->fb_err = -errno;
    		free(realnamef);
    		return (0);
    	}
    
    	fbuf->fb_err = f->op.rename(realnamef, realnamet);
    	free(realnamef);
    	free(realnamet);
    
    	return (0);
    }
    
    static int
    ifuse_ops_destroy(struct fuse *f)
    {
    	struct fuse_context *ctx;
    
    	DPRINTF("Opcode: destroy\n");
    
    	if (f->op.destroy) {
    		ctx = fuse_get_context();
    
    		f->op.destroy((ctx)?ctx->private_data:NULL);
    	}
    
    	f->fc->dead = 1;
    
    	return (0);
    }
    
    static int
    ifuse_ops_reclaim(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    
    	DPRINTF("Opcode: reclaim\t");
    	DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
    
    	vn = tree_get(&f->vnode_tree, fbuf->fb_ino);
    	if (vn != NULL)
    		unref_vn(f, vn);
    
    	return (0);
    }
    
    static int
    ifuse_ops_mknod(struct fuse *f, struct fusebuf *fbuf)
    {
    	struct fuse_vnode *vn;
    	char *realname;
    	uint32_t mode;
    	dev_t dev;
    
    	CHECK_OPT(mknod);
    
    	mode = fbuf->fb_io_mode;
    	dev = fbuf->fb_io_rdev;
    	vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino);
    	if (vn == NULL) {
    		fbuf->fb_err = -errno;
    		free(fbuf->fb_dat);
    		return (0);
    	}
    
    	free(fbuf->fb_dat);
    	realname = build_realname(f, vn->ino);
    	if (realname == NULL) {
    		fbuf->fb_err = -errno;
    		return (0);
    	}
    
    	fbuf->fb_err = f->op.mknod(realname, mode, dev);
    
    	if (!fbuf->fb_err) {
    		fbuf->fb_err = update_attr(f, &fbuf->fb_attr, realname, vn);
    		fbuf->fb_io_mode = fbuf->fb_attr.st_mode;
    		fbuf->fb_ino = vn->ino;
    	}
    	free(realname);
    
    	return (0);
    }
    
    int
    ifuse_exec_opcode(struct fuse *f, struct fusebuf *fbuf)
    {
    	int ret = 0;
    
    	fbuf->fb_len = 0;
    	fbuf->fb_err = 0;
    
    	switch (fbuf->fb_type) {
    	case FBT_LOOKUP:
    		ret = ifuse_ops_lookup(f, fbuf);
    		break;
    	case FBT_GETATTR:
    		ret = ifuse_ops_getattr(f, fbuf);
    		break;
    	case FBT_SETATTR:
    		ret = ifuse_ops_setattr(f, fbuf);
    		break;
    	case FBT_READLINK:
    		ret = ifuse_ops_readlink(f, fbuf);
    		break;
    	case FBT_MKDIR:
    		ret = ifuse_ops_mkdir(f, fbuf);
    		break;
    	case FBT_UNLINK:
    		ret = ifuse_ops_unlink(f, fbuf);
    		break;
    	case FBT_RMDIR:
    		ret = ifuse_ops_rmdir(f, fbuf);
    		break;
    	case FBT_LINK:
    		ret = ifuse_ops_link(f, fbuf);
    		break;
    	case FBT_OPEN:
    		ret = ifuse_ops_open(f, fbuf);
    		break;
    	case FBT_READ:
    		ret = ifuse_ops_read(f, fbuf);
    		break;
    	case FBT_WRITE:
    		ret = ifuse_ops_write(f, fbuf);
    		break;
    	case FBT_STATFS:
    		ret = ifuse_ops_statfs(f, fbuf);
    		break;
    	case FBT_RELEASE:
    		ret = ifuse_ops_release(f, fbuf);
    		break;
    	case FBT_FSYNC:
    		ret = ifuse_ops_fsync(f, fbuf);
    		break;
    	case FBT_FLUSH:
    		ret = ifuse_ops_flush(f, fbuf);
    		break;
    	case FBT_INIT:
    		ret = ifuse_ops_init(f);
    		break;
    	case FBT_OPENDIR:
    		ret = ifuse_ops_opendir(f, fbuf);
    		break;
    	case FBT_READDIR:
    		ret = ifuse_ops_readdir(f, fbuf);
    		break;
    	case FBT_RELEASEDIR:
    		ret = ifuse_ops_releasedir(f, fbuf);
    		break;
    	case FBT_ACCESS:
    		ret = ifuse_ops_access(f, fbuf);
    		break;
    	case FBT_SYMLINK:
    		ret = ifuse_ops_symlink(f, fbuf);
    		break;
    	case FBT_RENAME:
    		ret = ifuse_ops_rename(f, fbuf);
    		break;
    	case FBT_DESTROY:
    		ret = ifuse_ops_destroy(f);
    		break;
    	case FBT_RECLAIM:
    		ret = ifuse_ops_reclaim(f, fbuf);
    		break;
    	case FBT_MKNOD:
    		ret = ifuse_ops_mknod(f, fbuf);
    		break;
    	default:
    		DPRINTF("Opcode: %i not supported\t", fbuf->fb_type);
    		DPRINTF("Inode: %llu\t", (unsigned long long)fbuf->fb_ino);
    
    		fbuf->fb_err = -ENOSYS;
    		fbuf->fb_len = 0;
    	}
    	DPRINTF("\n");
    
    	/* fuse api use negative errno */
    	fbuf->fb_err = -fbuf->fb_err;
    	return (ret);
    }