src/lib/libfuse/fuse_ops.c

1183 lines
23 KiB
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);
}