ddf19c
From f6c6830f772e8060255323d2a458cd0e774d9654 Mon Sep 17 00:00:00 2001
ddf19c
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
ddf19c
Date: Mon, 27 Jan 2020 19:00:42 +0100
ddf19c
Subject: [PATCH 011/116] virtiofsd: Add fuse_lowlevel.c
ddf19c
MIME-Version: 1.0
ddf19c
Content-Type: text/plain; charset=UTF-8
ddf19c
Content-Transfer-Encoding: 8bit
ddf19c
ddf19c
RH-Author: Dr. David Alan Gilbert <dgilbert@redhat.com>
ddf19c
Message-id: <20200127190227.40942-8-dgilbert@redhat.com>
ddf19c
Patchwork-id: 93456
ddf19c
O-Subject: [RHEL-AV-8.2 qemu-kvm PATCH 007/112] virtiofsd: Add fuse_lowlevel.c
ddf19c
Bugzilla: 1694164
ddf19c
RH-Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com>
ddf19c
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
ddf19c
RH-Acked-by: Sergio Lopez Pascual <slp@redhat.com>
ddf19c
ddf19c
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
ddf19c
ddf19c
fuse_lowlevel is one of the largest files from the library
ddf19c
and does most of the work.  Add it separately to keep the diff
ddf19c
sizes small.
ddf19c
Again this is from upstream fuse-3.8.0
ddf19c
ddf19c
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
ddf19c
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
ddf19c
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
ddf19c
(cherry picked from commit 2de121f01e37e2fe98a4362f4abf7c0848697f76)
ddf19c
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
ddf19c
---
ddf19c
 tools/virtiofsd/fuse_lowlevel.c | 3129 +++++++++++++++++++++++++++++++++++++++
ddf19c
 1 file changed, 3129 insertions(+)
ddf19c
 create mode 100644 tools/virtiofsd/fuse_lowlevel.c
ddf19c
ddf19c
diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c
ddf19c
new file mode 100644
ddf19c
index 0000000..f2d7038
ddf19c
--- /dev/null
ddf19c
+++ b/tools/virtiofsd/fuse_lowlevel.c
ddf19c
@@ -0,0 +1,3129 @@
ddf19c
+/*
ddf19c
+  FUSE: Filesystem in Userspace
ddf19c
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
ddf19c
+
ddf19c
+  Implementation of (most of) the low-level FUSE API. The session loop
ddf19c
+  functions are implemented in separate files.
ddf19c
+
ddf19c
+  This program can be distributed under the terms of the GNU LGPLv2.
ddf19c
+  See the file COPYING.LIB
ddf19c
+*/
ddf19c
+
ddf19c
+#define _GNU_SOURCE
ddf19c
+
ddf19c
+#include "config.h"
ddf19c
+#include "fuse_i.h"
ddf19c
+#include "fuse_kernel.h"
ddf19c
+#include "fuse_opt.h"
ddf19c
+#include "fuse_misc.h"
ddf19c
+#include "mount_util.h"
ddf19c
+
ddf19c
+#include <stdio.h>
ddf19c
+#include <stdlib.h>
ddf19c
+#include <stddef.h>
ddf19c
+#include <string.h>
ddf19c
+#include <unistd.h>
ddf19c
+#include <limits.h>
ddf19c
+#include <errno.h>
ddf19c
+#include <assert.h>
ddf19c
+#include <sys file.h="">
ddf19c
+
ddf19c
+#ifndef F_LINUX_SPECIFIC_BASE
ddf19c
+#define F_LINUX_SPECIFIC_BASE       1024
ddf19c
+#endif
ddf19c
+#ifndef F_SETPIPE_SZ
ddf19c
+#define F_SETPIPE_SZ	(F_LINUX_SPECIFIC_BASE + 7)
ddf19c
+#endif
ddf19c
+
ddf19c
+
ddf19c
+#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
ddf19c
+#define OFFSET_MAX 0x7fffffffffffffffLL
ddf19c
+
ddf19c
+#define container_of(ptr, type, member) ({				\
ddf19c
+			const typeof( ((type *)0)->member ) *__mptr = (ptr); \
ddf19c
+			(type *)( (char *)__mptr - offsetof(type,member) );})
ddf19c
+
ddf19c
+struct fuse_pollhandle {
ddf19c
+	uint64_t kh;
ddf19c
+	struct fuse_session *se;
ddf19c
+};
ddf19c
+
ddf19c
+static size_t pagesize;
ddf19c
+
ddf19c
+static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
ddf19c
+{
ddf19c
+	pagesize = getpagesize();
ddf19c
+}
ddf19c
+
ddf19c
+static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
ddf19c
+{
ddf19c
+	attr->ino	= stbuf->st_ino;
ddf19c
+	attr->mode	= stbuf->st_mode;
ddf19c
+	attr->nlink	= stbuf->st_nlink;
ddf19c
+	attr->uid	= stbuf->st_uid;
ddf19c
+	attr->gid	= stbuf->st_gid;
ddf19c
+	attr->rdev	= stbuf->st_rdev;
ddf19c
+	attr->size	= stbuf->st_size;
ddf19c
+	attr->blksize	= stbuf->st_blksize;
ddf19c
+	attr->blocks	= stbuf->st_blocks;
ddf19c
+	attr->atime	= stbuf->st_atime;
ddf19c
+	attr->mtime	= stbuf->st_mtime;
ddf19c
+	attr->ctime	= stbuf->st_ctime;
ddf19c
+	attr->atimensec = ST_ATIM_NSEC(stbuf);
ddf19c
+	attr->mtimensec = ST_MTIM_NSEC(stbuf);
ddf19c
+	attr->ctimensec = ST_CTIM_NSEC(stbuf);
ddf19c
+}
ddf19c
+
ddf19c
+static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
ddf19c
+{
ddf19c
+	stbuf->st_mode	       = attr->mode;
ddf19c
+	stbuf->st_uid	       = attr->uid;
ddf19c
+	stbuf->st_gid	       = attr->gid;
ddf19c
+	stbuf->st_size	       = attr->size;
ddf19c
+	stbuf->st_atime	       = attr->atime;
ddf19c
+	stbuf->st_mtime	       = attr->mtime;
ddf19c
+	stbuf->st_ctime        = attr->ctime;
ddf19c
+	ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
ddf19c
+	ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
ddf19c
+	ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
ddf19c
+}
ddf19c
+
ddf19c
+static	size_t iov_length(const struct iovec *iov, size_t count)
ddf19c
+{
ddf19c
+	size_t seg;
ddf19c
+	size_t ret = 0;
ddf19c
+
ddf19c
+	for (seg = 0; seg < count; seg++)
ddf19c
+		ret += iov[seg].iov_len;
ddf19c
+	return ret;
ddf19c
+}
ddf19c
+
ddf19c
+static void list_init_req(struct fuse_req *req)
ddf19c
+{
ddf19c
+	req->next = req;
ddf19c
+	req->prev = req;
ddf19c
+}
ddf19c
+
ddf19c
+static void list_del_req(struct fuse_req *req)
ddf19c
+{
ddf19c
+	struct fuse_req *prev = req->prev;
ddf19c
+	struct fuse_req *next = req->next;
ddf19c
+	prev->next = next;
ddf19c
+	next->prev = prev;
ddf19c
+}
ddf19c
+
ddf19c
+static void list_add_req(struct fuse_req *req, struct fuse_req *next)
ddf19c
+{
ddf19c
+	struct fuse_req *prev = next->prev;
ddf19c
+	req->next = next;
ddf19c
+	req->prev = prev;
ddf19c
+	prev->next = req;
ddf19c
+	next->prev = req;
ddf19c
+}
ddf19c
+
ddf19c
+static void destroy_req(fuse_req_t req)
ddf19c
+{
ddf19c
+	pthread_mutex_destroy(&req->lock);
ddf19c
+	free(req);
ddf19c
+}
ddf19c
+
ddf19c
+void fuse_free_req(fuse_req_t req)
ddf19c
+{
ddf19c
+	int ctr;
ddf19c
+	struct fuse_session *se = req->se;
ddf19c
+
ddf19c
+	pthread_mutex_lock(&se->lock);
ddf19c
+	req->u.ni.func = NULL;
ddf19c
+	req->u.ni.data = NULL;
ddf19c
+	list_del_req(req);
ddf19c
+	ctr = --req->ctr;
ddf19c
+	fuse_chan_put(req->ch);
ddf19c
+	req->ch = NULL;
ddf19c
+	pthread_mutex_unlock(&se->lock);
ddf19c
+	if (!ctr)
ddf19c
+		destroy_req(req);
ddf19c
+}
ddf19c
+
ddf19c
+static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
ddf19c
+{
ddf19c
+	struct fuse_req *req;
ddf19c
+
ddf19c
+	req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
ddf19c
+	if (req == NULL) {
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
ddf19c
+	} else {
ddf19c
+		req->se = se;
ddf19c
+		req->ctr = 1;
ddf19c
+		list_init_req(req);
ddf19c
+		fuse_mutex_init(&req->lock);
ddf19c
+	}
ddf19c
+
ddf19c
+	return req;
ddf19c
+}
ddf19c
+
ddf19c
+/* Send data. If *ch* is NULL, send via session master fd */
ddf19c
+static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
ddf19c
+			 struct iovec *iov, int count)
ddf19c
+{
ddf19c
+	struct fuse_out_header *out = iov[0].iov_base;
ddf19c
+
ddf19c
+	out->len = iov_length(iov, count);
ddf19c
+	if (se->debug) {
ddf19c
+		if (out->unique == 0) {
ddf19c
+			fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
ddf19c
+				out->error, out->len);
ddf19c
+		} else if (out->error) {
ddf19c
+			fuse_log(FUSE_LOG_DEBUG,
ddf19c
+				"   unique: %llu, error: %i (%s), outsize: %i\n",
ddf19c
+				(unsigned long long) out->unique, out->error,
ddf19c
+				strerror(-out->error), out->len);
ddf19c
+		} else {
ddf19c
+			fuse_log(FUSE_LOG_DEBUG,
ddf19c
+				"   unique: %llu, success, outsize: %i\n",
ddf19c
+				(unsigned long long) out->unique, out->len);
ddf19c
+		}
ddf19c
+	}
ddf19c
+
ddf19c
+	ssize_t res = writev(ch ? ch->fd : se->fd,
ddf19c
+			     iov, count);
ddf19c
+	int err = errno;
ddf19c
+
ddf19c
+	if (res == -1) {
ddf19c
+		assert(se != NULL);
ddf19c
+
ddf19c
+		/* ENOENT means the operation was interrupted */
ddf19c
+		if (!fuse_session_exited(se) && err != ENOENT)
ddf19c
+			perror("fuse: writing device");
ddf19c
+		return -err;
ddf19c
+	}
ddf19c
+
ddf19c
+	return 0;
ddf19c
+}
ddf19c
+
ddf19c
+
ddf19c
+int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
ddf19c
+			       int count)
ddf19c
+{
ddf19c
+	struct fuse_out_header out;
ddf19c
+
ddf19c
+	if (error <= -1000 || error > 0) {
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n",	error);
ddf19c
+		error = -ERANGE;
ddf19c
+	}
ddf19c
+
ddf19c
+	out.unique = req->unique;
ddf19c
+	out.error = error;
ddf19c
+
ddf19c
+	iov[0].iov_base = &out;
ddf19c
+	iov[0].iov_len = sizeof(struct fuse_out_header);
ddf19c
+
ddf19c
+	return fuse_send_msg(req->se, req->ch, iov, count);
ddf19c
+}
ddf19c
+
ddf19c
+static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
ddf19c
+			  int count)
ddf19c
+{
ddf19c
+	int res;
ddf19c
+
ddf19c
+	res = fuse_send_reply_iov_nofree(req, error, iov, count);
ddf19c
+	fuse_free_req(req);
ddf19c
+	return res;
ddf19c
+}
ddf19c
+
ddf19c
+static int send_reply(fuse_req_t req, int error, const void *arg,
ddf19c
+		      size_t argsize)
ddf19c
+{
ddf19c
+	struct iovec iov[2];
ddf19c
+	int count = 1;
ddf19c
+	if (argsize) {
ddf19c
+		iov[1].iov_base = (void *) arg;
ddf19c
+		iov[1].iov_len = argsize;
ddf19c
+		count++;
ddf19c
+	}
ddf19c
+	return send_reply_iov(req, error, iov, count);
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
ddf19c
+{
ddf19c
+	int res;
ddf19c
+	struct iovec *padded_iov;
ddf19c
+
ddf19c
+	padded_iov = malloc((count + 1) * sizeof(struct iovec));
ddf19c
+	if (padded_iov == NULL)
ddf19c
+		return fuse_reply_err(req, ENOMEM);
ddf19c
+
ddf19c
+	memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
ddf19c
+	count++;
ddf19c
+
ddf19c
+	res = send_reply_iov(req, 0, padded_iov, count);
ddf19c
+	free(padded_iov);
ddf19c
+
ddf19c
+	return res;
ddf19c
+}
ddf19c
+
ddf19c
+
ddf19c
+/* `buf` is allowed to be empty so that the proper size may be
ddf19c
+   allocated by the caller */
ddf19c
+size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
ddf19c
+			 const char *name, const struct stat *stbuf, off_t off)
ddf19c
+{
ddf19c
+	(void)req;
ddf19c
+	size_t namelen;
ddf19c
+	size_t entlen;
ddf19c
+	size_t entlen_padded;
ddf19c
+	struct fuse_dirent *dirent;
ddf19c
+
ddf19c
+	namelen = strlen(name);
ddf19c
+	entlen = FUSE_NAME_OFFSET + namelen;
ddf19c
+	entlen_padded = FUSE_DIRENT_ALIGN(entlen);
ddf19c
+
ddf19c
+	if ((buf == NULL) || (entlen_padded > bufsize))
ddf19c
+	  return entlen_padded;
ddf19c
+
ddf19c
+	dirent = (struct fuse_dirent*) buf;
ddf19c
+	dirent->ino = stbuf->st_ino;
ddf19c
+	dirent->off = off;
ddf19c
+	dirent->namelen = namelen;
ddf19c
+	dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
ddf19c
+	memcpy(dirent->name, name, namelen);
ddf19c
+	memset(dirent->name + namelen, 0, entlen_padded - entlen);
ddf19c
+
ddf19c
+	return entlen_padded;
ddf19c
+}
ddf19c
+
ddf19c
+static void convert_statfs(const struct statvfs *stbuf,
ddf19c
+			   struct fuse_kstatfs *kstatfs)
ddf19c
+{
ddf19c
+	kstatfs->bsize	 = stbuf->f_bsize;
ddf19c
+	kstatfs->frsize	 = stbuf->f_frsize;
ddf19c
+	kstatfs->blocks	 = stbuf->f_blocks;
ddf19c
+	kstatfs->bfree	 = stbuf->f_bfree;
ddf19c
+	kstatfs->bavail	 = stbuf->f_bavail;
ddf19c
+	kstatfs->files	 = stbuf->f_files;
ddf19c
+	kstatfs->ffree	 = stbuf->f_ffree;
ddf19c
+	kstatfs->namelen = stbuf->f_namemax;
ddf19c
+}
ddf19c
+
ddf19c
+static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
ddf19c
+{
ddf19c
+	return send_reply(req, 0, arg, argsize);
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_err(fuse_req_t req, int err)
ddf19c
+{
ddf19c
+	return send_reply(req, -err, NULL, 0);
ddf19c
+}
ddf19c
+
ddf19c
+void fuse_reply_none(fuse_req_t req)
ddf19c
+{
ddf19c
+	fuse_free_req(req);
ddf19c
+}
ddf19c
+
ddf19c
+static unsigned long calc_timeout_sec(double t)
ddf19c
+{
ddf19c
+	if (t > (double) ULONG_MAX)
ddf19c
+		return ULONG_MAX;
ddf19c
+	else if (t < 0.0)
ddf19c
+		return 0;
ddf19c
+	else
ddf19c
+		return (unsigned long) t;
ddf19c
+}
ddf19c
+
ddf19c
+static unsigned int calc_timeout_nsec(double t)
ddf19c
+{
ddf19c
+	double f = t - (double) calc_timeout_sec(t);
ddf19c
+	if (f < 0.0)
ddf19c
+		return 0;
ddf19c
+	else if (f >= 0.999999999)
ddf19c
+		return 999999999;
ddf19c
+	else
ddf19c
+		return (unsigned int) (f * 1.0e9);
ddf19c
+}
ddf19c
+
ddf19c
+static void fill_entry(struct fuse_entry_out *arg,
ddf19c
+		       const struct fuse_entry_param *e)
ddf19c
+{
ddf19c
+	arg->nodeid = e->ino;
ddf19c
+	arg->generation = e->generation;
ddf19c
+	arg->entry_valid = calc_timeout_sec(e->entry_timeout);
ddf19c
+	arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
ddf19c
+	arg->attr_valid = calc_timeout_sec(e->attr_timeout);
ddf19c
+	arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
ddf19c
+	convert_stat(&e->attr, &arg->attr);
ddf19c
+}
ddf19c
+
ddf19c
+/* `buf` is allowed to be empty so that the proper size may be
ddf19c
+   allocated by the caller */
ddf19c
+size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
ddf19c
+			      const char *name,
ddf19c
+			      const struct fuse_entry_param *e, off_t off)
ddf19c
+{
ddf19c
+	(void)req;
ddf19c
+	size_t namelen;
ddf19c
+	size_t entlen;
ddf19c
+	size_t entlen_padded;
ddf19c
+
ddf19c
+	namelen = strlen(name);
ddf19c
+	entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
ddf19c
+	entlen_padded = FUSE_DIRENT_ALIGN(entlen);
ddf19c
+	if ((buf == NULL) || (entlen_padded > bufsize))
ddf19c
+	  return entlen_padded;
ddf19c
+
ddf19c
+	struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
ddf19c
+	memset(&dp->entry_out, 0, sizeof(dp->entry_out));
ddf19c
+	fill_entry(&dp->entry_out, e);
ddf19c
+
ddf19c
+	struct fuse_dirent *dirent = &dp->dirent;
ddf19c
+	dirent->ino = e->attr.st_ino;
ddf19c
+	dirent->off = off;
ddf19c
+	dirent->namelen = namelen;
ddf19c
+	dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
ddf19c
+	memcpy(dirent->name, name, namelen);
ddf19c
+	memset(dirent->name + namelen, 0, entlen_padded - entlen);
ddf19c
+
ddf19c
+	return entlen_padded;
ddf19c
+}
ddf19c
+
ddf19c
+static void fill_open(struct fuse_open_out *arg,
ddf19c
+		      const struct fuse_file_info *f)
ddf19c
+{
ddf19c
+	arg->fh = f->fh;
ddf19c
+	if (f->direct_io)
ddf19c
+		arg->open_flags |= FOPEN_DIRECT_IO;
ddf19c
+	if (f->keep_cache)
ddf19c
+		arg->open_flags |= FOPEN_KEEP_CACHE;
ddf19c
+	if (f->cache_readdir)
ddf19c
+		arg->open_flags |= FOPEN_CACHE_DIR;
ddf19c
+	if (f->nonseekable)
ddf19c
+		arg->open_flags |= FOPEN_NONSEEKABLE;
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
ddf19c
+{
ddf19c
+	struct fuse_entry_out arg;
ddf19c
+	size_t size = req->se->conn.proto_minor < 9 ?
ddf19c
+		FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
ddf19c
+
ddf19c
+	/* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
ddf19c
+	   negative entry */
ddf19c
+	if (!e->ino && req->se->conn.proto_minor < 4)
ddf19c
+		return fuse_reply_err(req, ENOENT);
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	fill_entry(&arg, e);
ddf19c
+	return send_reply_ok(req, &arg, size);
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
ddf19c
+		      const struct fuse_file_info *f)
ddf19c
+{
ddf19c
+	char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
ddf19c
+	size_t entrysize = req->se->conn.proto_minor < 9 ?
ddf19c
+		FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
ddf19c
+	struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
ddf19c
+	struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
ddf19c
+
ddf19c
+	memset(buf, 0, sizeof(buf));
ddf19c
+	fill_entry(earg, e);
ddf19c
+	fill_open(oarg, f);
ddf19c
+	return send_reply_ok(req, buf,
ddf19c
+			     entrysize + sizeof(struct fuse_open_out));
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
ddf19c
+		    double attr_timeout)
ddf19c
+{
ddf19c
+	struct fuse_attr_out arg;
ddf19c
+	size_t size = req->se->conn.proto_minor < 9 ?
ddf19c
+		FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.attr_valid = calc_timeout_sec(attr_timeout);
ddf19c
+	arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
ddf19c
+	convert_stat(attr, &arg.attr);
ddf19c
+
ddf19c
+	return send_reply_ok(req, &arg, size);
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_readlink(fuse_req_t req, const char *linkname)
ddf19c
+{
ddf19c
+	return send_reply_ok(req, linkname, strlen(linkname));
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
ddf19c
+{
ddf19c
+	struct fuse_open_out arg;
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	fill_open(&arg, f);
ddf19c
+	return send_reply_ok(req, &arg, sizeof(arg));
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_write(fuse_req_t req, size_t count)
ddf19c
+{
ddf19c
+	struct fuse_write_out arg;
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.size = count;
ddf19c
+
ddf19c
+	return send_reply_ok(req, &arg, sizeof(arg));
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
ddf19c
+{
ddf19c
+	return send_reply_ok(req, buf, size);
ddf19c
+}
ddf19c
+
ddf19c
+static int fuse_send_data_iov_fallback(struct fuse_session *se,
ddf19c
+				       struct fuse_chan *ch,
ddf19c
+				       struct iovec *iov, int iov_count,
ddf19c
+				       struct fuse_bufvec *buf,
ddf19c
+				       size_t len)
ddf19c
+{
ddf19c
+	struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
ddf19c
+	void *mbuf;
ddf19c
+	int res;
ddf19c
+
ddf19c
+	/* Optimize common case */
ddf19c
+	if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
ddf19c
+	    !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
ddf19c
+		/* FIXME: also avoid memory copy if there are multiple buffers
ddf19c
+		   but none of them contain an fd */
ddf19c
+
ddf19c
+		iov[iov_count].iov_base = buf->buf[0].mem;
ddf19c
+		iov[iov_count].iov_len = len;
ddf19c
+		iov_count++;
ddf19c
+		return fuse_send_msg(se, ch, iov, iov_count);
ddf19c
+	}
ddf19c
+
ddf19c
+	res = posix_memalign(&mbuf, pagesize, len);
ddf19c
+	if (res != 0)
ddf19c
+		return res;
ddf19c
+
ddf19c
+	mem_buf.buf[0].mem = mbuf;
ddf19c
+	res = fuse_buf_copy(&mem_buf, buf, 0);
ddf19c
+	if (res < 0) {
ddf19c
+		free(mbuf);
ddf19c
+		return -res;
ddf19c
+	}
ddf19c
+	len = res;
ddf19c
+
ddf19c
+	iov[iov_count].iov_base = mbuf;
ddf19c
+	iov[iov_count].iov_len = len;
ddf19c
+	iov_count++;
ddf19c
+	res = fuse_send_msg(se, ch, iov, iov_count);
ddf19c
+	free(mbuf);
ddf19c
+
ddf19c
+	return res;
ddf19c
+}
ddf19c
+
ddf19c
+struct fuse_ll_pipe {
ddf19c
+	size_t size;
ddf19c
+	int can_grow;
ddf19c
+	int pipe[2];
ddf19c
+};
ddf19c
+
ddf19c
+static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
ddf19c
+{
ddf19c
+	close(llp->pipe[0]);
ddf19c
+	close(llp->pipe[1]);
ddf19c
+	free(llp);
ddf19c
+}
ddf19c
+
ddf19c
+#ifdef HAVE_SPLICE
ddf19c
+#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
ddf19c
+static int fuse_pipe(int fds[2])
ddf19c
+{
ddf19c
+	int rv = pipe(fds);
ddf19c
+
ddf19c
+	if (rv == -1)
ddf19c
+		return rv;
ddf19c
+
ddf19c
+	if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
ddf19c
+	    fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
ddf19c
+	    fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
ddf19c
+	    fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
ddf19c
+		close(fds[0]);
ddf19c
+		close(fds[1]);
ddf19c
+		rv = -1;
ddf19c
+	}
ddf19c
+	return rv;
ddf19c
+}
ddf19c
+#else
ddf19c
+static int fuse_pipe(int fds[2])
ddf19c
+{
ddf19c
+	return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
ddf19c
+}
ddf19c
+#endif
ddf19c
+
ddf19c
+static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
ddf19c
+{
ddf19c
+	struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
ddf19c
+	if (llp == NULL) {
ddf19c
+		int res;
ddf19c
+
ddf19c
+		llp = malloc(sizeof(struct fuse_ll_pipe));
ddf19c
+		if (llp == NULL)
ddf19c
+			return NULL;
ddf19c
+
ddf19c
+		res = fuse_pipe(llp->pipe);
ddf19c
+		if (res == -1) {
ddf19c
+			free(llp);
ddf19c
+			return NULL;
ddf19c
+		}
ddf19c
+
ddf19c
+		/*
ddf19c
+		 *the default size is 16 pages on linux
ddf19c
+		 */
ddf19c
+		llp->size = pagesize * 16;
ddf19c
+		llp->can_grow = 1;
ddf19c
+
ddf19c
+		pthread_setspecific(se->pipe_key, llp);
ddf19c
+	}
ddf19c
+
ddf19c
+	return llp;
ddf19c
+}
ddf19c
+#endif
ddf19c
+
ddf19c
+static void fuse_ll_clear_pipe(struct fuse_session *se)
ddf19c
+{
ddf19c
+	struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
ddf19c
+	if (llp) {
ddf19c
+		pthread_setspecific(se->pipe_key, NULL);
ddf19c
+		fuse_ll_pipe_free(llp);
ddf19c
+	}
ddf19c
+}
ddf19c
+
ddf19c
+#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
ddf19c
+static int read_back(int fd, char *buf, size_t len)
ddf19c
+{
ddf19c
+	int res;
ddf19c
+
ddf19c
+	res = read(fd, buf, len);
ddf19c
+	if (res == -1) {
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
ddf19c
+		return -EIO;
ddf19c
+	}
ddf19c
+	if (res != len) {
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
ddf19c
+		return -EIO;
ddf19c
+	}
ddf19c
+	return 0;
ddf19c
+}
ddf19c
+
ddf19c
+static int grow_pipe_to_max(int pipefd)
ddf19c
+{
ddf19c
+	int max;
ddf19c
+	int res;
ddf19c
+	int maxfd;
ddf19c
+	char buf[32];
ddf19c
+
ddf19c
+	maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
ddf19c
+	if (maxfd < 0)
ddf19c
+		return -errno;
ddf19c
+
ddf19c
+	res = read(maxfd, buf, sizeof(buf) - 1);
ddf19c
+	if (res < 0) {
ddf19c
+		int saved_errno;
ddf19c
+
ddf19c
+		saved_errno = errno;
ddf19c
+		close(maxfd);
ddf19c
+		return -saved_errno;
ddf19c
+	}
ddf19c
+	close(maxfd);
ddf19c
+	buf[res] = '\0';
ddf19c
+
ddf19c
+	max = atoi(buf);
ddf19c
+	res = fcntl(pipefd, F_SETPIPE_SZ, max);
ddf19c
+	if (res < 0)
ddf19c
+		return -errno;
ddf19c
+	return max;
ddf19c
+}
ddf19c
+
ddf19c
+static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
ddf19c
+			       struct iovec *iov, int iov_count,
ddf19c
+			       struct fuse_bufvec *buf, unsigned int flags)
ddf19c
+{
ddf19c
+	int res;
ddf19c
+	size_t len = fuse_buf_size(buf);
ddf19c
+	struct fuse_out_header *out = iov[0].iov_base;
ddf19c
+	struct fuse_ll_pipe *llp;
ddf19c
+	int splice_flags;
ddf19c
+	size_t pipesize;
ddf19c
+	size_t total_fd_size;
ddf19c
+	size_t idx;
ddf19c
+	size_t headerlen;
ddf19c
+	struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
ddf19c
+
ddf19c
+	if (se->broken_splice_nonblock)
ddf19c
+		goto fallback;
ddf19c
+
ddf19c
+	if (flags & FUSE_BUF_NO_SPLICE)
ddf19c
+		goto fallback;
ddf19c
+
ddf19c
+	total_fd_size = 0;
ddf19c
+	for (idx = buf->idx; idx < buf->count; idx++) {
ddf19c
+		if (buf->buf[idx].flags & FUSE_BUF_IS_FD) {
ddf19c
+			total_fd_size = buf->buf[idx].size;
ddf19c
+			if (idx == buf->idx)
ddf19c
+				total_fd_size -= buf->off;
ddf19c
+		}
ddf19c
+	}
ddf19c
+	if (total_fd_size < 2 * pagesize)
ddf19c
+		goto fallback;
ddf19c
+
ddf19c
+	if (se->conn.proto_minor < 14 ||
ddf19c
+	    !(se->conn.want & FUSE_CAP_SPLICE_WRITE))
ddf19c
+		goto fallback;
ddf19c
+
ddf19c
+	llp = fuse_ll_get_pipe(se);
ddf19c
+	if (llp == NULL)
ddf19c
+		goto fallback;
ddf19c
+
ddf19c
+
ddf19c
+	headerlen = iov_length(iov, iov_count);
ddf19c
+
ddf19c
+	out->len = headerlen + len;
ddf19c
+
ddf19c
+	/*
ddf19c
+	 * Heuristic for the required pipe size, does not work if the
ddf19c
+	 * source contains less than page size fragments
ddf19c
+	 */
ddf19c
+	pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
ddf19c
+
ddf19c
+	if (llp->size < pipesize) {
ddf19c
+		if (llp->can_grow) {
ddf19c
+			res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
ddf19c
+			if (res == -1) {
ddf19c
+				res = grow_pipe_to_max(llp->pipe[0]);
ddf19c
+				if (res > 0)
ddf19c
+					llp->size = res;
ddf19c
+				llp->can_grow = 0;
ddf19c
+				goto fallback;
ddf19c
+			}
ddf19c
+			llp->size = res;
ddf19c
+		}
ddf19c
+		if (llp->size < pipesize)
ddf19c
+			goto fallback;
ddf19c
+	}
ddf19c
+
ddf19c
+
ddf19c
+	res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
ddf19c
+	if (res == -1)
ddf19c
+		goto fallback;
ddf19c
+
ddf19c
+	if (res != headerlen) {
ddf19c
+		res = -EIO;
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
ddf19c
+			headerlen);
ddf19c
+		goto clear_pipe;
ddf19c
+	}
ddf19c
+
ddf19c
+	pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
ddf19c
+	pipe_buf.buf[0].fd = llp->pipe[1];
ddf19c
+
ddf19c
+	res = fuse_buf_copy(&pipe_buf, buf,
ddf19c
+			    FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK);
ddf19c
+	if (res < 0) {
ddf19c
+		if (res == -EAGAIN || res == -EINVAL) {
ddf19c
+			/*
ddf19c
+			 * Should only get EAGAIN on kernels with
ddf19c
+			 * broken SPLICE_F_NONBLOCK support (<=
ddf19c
+			 * 2.6.35) where this error or a short read is
ddf19c
+			 * returned even if the pipe itself is not
ddf19c
+			 * full
ddf19c
+			 *
ddf19c
+			 * EINVAL might mean that splice can't handle
ddf19c
+			 * this combination of input and output.
ddf19c
+			 */
ddf19c
+			if (res == -EAGAIN)
ddf19c
+				se->broken_splice_nonblock = 1;
ddf19c
+
ddf19c
+			pthread_setspecific(se->pipe_key, NULL);
ddf19c
+			fuse_ll_pipe_free(llp);
ddf19c
+			goto fallback;
ddf19c
+		}
ddf19c
+		res = -res;
ddf19c
+		goto clear_pipe;
ddf19c
+	}
ddf19c
+
ddf19c
+	if (res != 0 && res < len) {
ddf19c
+		struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
ddf19c
+		void *mbuf;
ddf19c
+		size_t now_len = res;
ddf19c
+		/*
ddf19c
+		 * For regular files a short count is either
ddf19c
+		 *  1) due to EOF, or
ddf19c
+		 *  2) because of broken SPLICE_F_NONBLOCK (see above)
ddf19c
+		 *
ddf19c
+		 * For other inputs it's possible that we overflowed
ddf19c
+		 * the pipe because of small buffer fragments.
ddf19c
+		 */
ddf19c
+
ddf19c
+		res = posix_memalign(&mbuf, pagesize, len);
ddf19c
+		if (res != 0)
ddf19c
+			goto clear_pipe;
ddf19c
+
ddf19c
+		mem_buf.buf[0].mem = mbuf;
ddf19c
+		mem_buf.off = now_len;
ddf19c
+		res = fuse_buf_copy(&mem_buf, buf, 0);
ddf19c
+		if (res > 0) {
ddf19c
+			char *tmpbuf;
ddf19c
+			size_t extra_len = res;
ddf19c
+			/*
ddf19c
+			 * Trickiest case: got more data.  Need to get
ddf19c
+			 * back the data from the pipe and then fall
ddf19c
+			 * back to regular write.
ddf19c
+			 */
ddf19c
+			tmpbuf = malloc(headerlen);
ddf19c
+			if (tmpbuf == NULL) {
ddf19c
+				free(mbuf);
ddf19c
+				res = ENOMEM;
ddf19c
+				goto clear_pipe;
ddf19c
+			}
ddf19c
+			res = read_back(llp->pipe[0], tmpbuf, headerlen);
ddf19c
+			free(tmpbuf);
ddf19c
+			if (res != 0) {
ddf19c
+				free(mbuf);
ddf19c
+				goto clear_pipe;
ddf19c
+			}
ddf19c
+			res = read_back(llp->pipe[0], mbuf, now_len);
ddf19c
+			if (res != 0) {
ddf19c
+				free(mbuf);
ddf19c
+				goto clear_pipe;
ddf19c
+			}
ddf19c
+			len = now_len + extra_len;
ddf19c
+			iov[iov_count].iov_base = mbuf;
ddf19c
+			iov[iov_count].iov_len = len;
ddf19c
+			iov_count++;
ddf19c
+			res = fuse_send_msg(se, ch, iov, iov_count);
ddf19c
+			free(mbuf);
ddf19c
+			return res;
ddf19c
+		}
ddf19c
+		free(mbuf);
ddf19c
+		res = now_len;
ddf19c
+	}
ddf19c
+	len = res;
ddf19c
+	out->len = headerlen + len;
ddf19c
+
ddf19c
+	if (se->debug) {
ddf19c
+		fuse_log(FUSE_LOG_DEBUG,
ddf19c
+			"   unique: %llu, success, outsize: %i (splice)\n",
ddf19c
+			(unsigned long long) out->unique, out->len);
ddf19c
+	}
ddf19c
+
ddf19c
+	splice_flags = 0;
ddf19c
+	if ((flags & FUSE_BUF_SPLICE_MOVE) &&
ddf19c
+	    (se->conn.want & FUSE_CAP_SPLICE_MOVE))
ddf19c
+		splice_flags |= SPLICE_F_MOVE;
ddf19c
+
ddf19c
+	res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd,
ddf19c
+		     NULL, out->len, splice_flags);
ddf19c
+	if (res == -1) {
ddf19c
+		res = -errno;
ddf19c
+		perror("fuse: splice from pipe");
ddf19c
+		goto clear_pipe;
ddf19c
+	}
ddf19c
+	if (res != out->len) {
ddf19c
+		res = -EIO;
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
ddf19c
+			res, out->len);
ddf19c
+		goto clear_pipe;
ddf19c
+	}
ddf19c
+	return 0;
ddf19c
+
ddf19c
+clear_pipe:
ddf19c
+	fuse_ll_clear_pipe(se);
ddf19c
+	return res;
ddf19c
+
ddf19c
+fallback:
ddf19c
+	return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
ddf19c
+}
ddf19c
+#else
ddf19c
+static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
ddf19c
+			       struct iovec *iov, int iov_count,
ddf19c
+			       struct fuse_bufvec *buf, unsigned int flags)
ddf19c
+{
ddf19c
+	size_t len = fuse_buf_size(buf);
ddf19c
+	(void) flags;
ddf19c
+
ddf19c
+	return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
ddf19c
+}
ddf19c
+#endif
ddf19c
+
ddf19c
+int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
ddf19c
+		    enum fuse_buf_copy_flags flags)
ddf19c
+{
ddf19c
+	struct iovec iov[2];
ddf19c
+	struct fuse_out_header out;
ddf19c
+	int res;
ddf19c
+
ddf19c
+	iov[0].iov_base = &out;
ddf19c
+	iov[0].iov_len = sizeof(struct fuse_out_header);
ddf19c
+
ddf19c
+	out.unique = req->unique;
ddf19c
+	out.error = 0;
ddf19c
+
ddf19c
+	res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags);
ddf19c
+	if (res <= 0) {
ddf19c
+		fuse_free_req(req);
ddf19c
+		return res;
ddf19c
+	} else {
ddf19c
+		return fuse_reply_err(req, res);
ddf19c
+	}
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
ddf19c
+{
ddf19c
+	struct fuse_statfs_out arg;
ddf19c
+	size_t size = req->se->conn.proto_minor < 4 ?
ddf19c
+		FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	convert_statfs(stbuf, &arg.st);
ddf19c
+
ddf19c
+	return send_reply_ok(req, &arg, size);
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_xattr(fuse_req_t req, size_t count)
ddf19c
+{
ddf19c
+	struct fuse_getxattr_out arg;
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.size = count;
ddf19c
+
ddf19c
+	return send_reply_ok(req, &arg, sizeof(arg));
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
ddf19c
+{
ddf19c
+	struct fuse_lk_out arg;
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.lk.type = lock->l_type;
ddf19c
+	if (lock->l_type != F_UNLCK) {
ddf19c
+		arg.lk.start = lock->l_start;
ddf19c
+		if (lock->l_len == 0)
ddf19c
+			arg.lk.end = OFFSET_MAX;
ddf19c
+		else
ddf19c
+			arg.lk.end = lock->l_start + lock->l_len - 1;
ddf19c
+	}
ddf19c
+	arg.lk.pid = lock->l_pid;
ddf19c
+	return send_reply_ok(req, &arg, sizeof(arg));
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
ddf19c
+{
ddf19c
+	struct fuse_bmap_out arg;
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.block = idx;
ddf19c
+
ddf19c
+	return send_reply_ok(req, &arg, sizeof(arg));
ddf19c
+}
ddf19c
+
ddf19c
+static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
ddf19c
+						      size_t count)
ddf19c
+{
ddf19c
+	struct fuse_ioctl_iovec *fiov;
ddf19c
+	size_t i;
ddf19c
+
ddf19c
+	fiov = malloc(sizeof(fiov[0]) * count);
ddf19c
+	if (!fiov)
ddf19c
+		return NULL;
ddf19c
+
ddf19c
+	for (i = 0; i < count; i++) {
ddf19c
+		fiov[i].base = (uintptr_t) iov[i].iov_base;
ddf19c
+		fiov[i].len = iov[i].iov_len;
ddf19c
+	}
ddf19c
+
ddf19c
+	return fiov;
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_ioctl_retry(fuse_req_t req,
ddf19c
+			   const struct iovec *in_iov, size_t in_count,
ddf19c
+			   const struct iovec *out_iov, size_t out_count)
ddf19c
+{
ddf19c
+	struct fuse_ioctl_out arg;
ddf19c
+	struct fuse_ioctl_iovec *in_fiov = NULL;
ddf19c
+	struct fuse_ioctl_iovec *out_fiov = NULL;
ddf19c
+	struct iovec iov[4];
ddf19c
+	size_t count = 1;
ddf19c
+	int res;
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.flags |= FUSE_IOCTL_RETRY;
ddf19c
+	arg.in_iovs = in_count;
ddf19c
+	arg.out_iovs = out_count;
ddf19c
+	iov[count].iov_base = &arg;
ddf19c
+	iov[count].iov_len = sizeof(arg);
ddf19c
+	count++;
ddf19c
+
ddf19c
+	if (req->se->conn.proto_minor < 16) {
ddf19c
+		if (in_count) {
ddf19c
+			iov[count].iov_base = (void *)in_iov;
ddf19c
+			iov[count].iov_len = sizeof(in_iov[0]) * in_count;
ddf19c
+			count++;
ddf19c
+		}
ddf19c
+
ddf19c
+		if (out_count) {
ddf19c
+			iov[count].iov_base = (void *)out_iov;
ddf19c
+			iov[count].iov_len = sizeof(out_iov[0]) * out_count;
ddf19c
+			count++;
ddf19c
+		}
ddf19c
+	} else {
ddf19c
+		/* Can't handle non-compat 64bit ioctls on 32bit */
ddf19c
+		if (sizeof(void *) == 4 && req->ioctl_64bit) {
ddf19c
+			res = fuse_reply_err(req, EINVAL);
ddf19c
+			goto out;
ddf19c
+		}
ddf19c
+
ddf19c
+		if (in_count) {
ddf19c
+			in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
ddf19c
+			if (!in_fiov)
ddf19c
+				goto enomem;
ddf19c
+
ddf19c
+			iov[count].iov_base = (void *)in_fiov;
ddf19c
+			iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
ddf19c
+			count++;
ddf19c
+		}
ddf19c
+		if (out_count) {
ddf19c
+			out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
ddf19c
+			if (!out_fiov)
ddf19c
+				goto enomem;
ddf19c
+
ddf19c
+			iov[count].iov_base = (void *)out_fiov;
ddf19c
+			iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
ddf19c
+			count++;
ddf19c
+		}
ddf19c
+	}
ddf19c
+
ddf19c
+	res = send_reply_iov(req, 0, iov, count);
ddf19c
+out:
ddf19c
+	free(in_fiov);
ddf19c
+	free(out_fiov);
ddf19c
+
ddf19c
+	return res;
ddf19c
+
ddf19c
+enomem:
ddf19c
+	res = fuse_reply_err(req, ENOMEM);
ddf19c
+	goto out;
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
ddf19c
+{
ddf19c
+	struct fuse_ioctl_out arg;
ddf19c
+	struct iovec iov[3];
ddf19c
+	size_t count = 1;
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.result = result;
ddf19c
+	iov[count].iov_base = &arg;
ddf19c
+	iov[count].iov_len = sizeof(arg);
ddf19c
+	count++;
ddf19c
+
ddf19c
+	if (size) {
ddf19c
+		iov[count].iov_base = (char *) buf;
ddf19c
+		iov[count].iov_len = size;
ddf19c
+		count++;
ddf19c
+	}
ddf19c
+
ddf19c
+	return send_reply_iov(req, 0, iov, count);
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
ddf19c
+			 int count)
ddf19c
+{
ddf19c
+	struct iovec *padded_iov;
ddf19c
+	struct fuse_ioctl_out arg;
ddf19c
+	int res;
ddf19c
+
ddf19c
+	padded_iov = malloc((count + 2) * sizeof(struct iovec));
ddf19c
+	if (padded_iov == NULL)
ddf19c
+		return fuse_reply_err(req, ENOMEM);
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.result = result;
ddf19c
+	padded_iov[1].iov_base = &arg;
ddf19c
+	padded_iov[1].iov_len = sizeof(arg);
ddf19c
+
ddf19c
+	memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
ddf19c
+
ddf19c
+	res = send_reply_iov(req, 0, padded_iov, count + 2);
ddf19c
+	free(padded_iov);
ddf19c
+
ddf19c
+	return res;
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_poll(fuse_req_t req, unsigned revents)
ddf19c
+{
ddf19c
+	struct fuse_poll_out arg;
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.revents = revents;
ddf19c
+
ddf19c
+	return send_reply_ok(req, &arg, sizeof(arg));
ddf19c
+}
ddf19c
+
ddf19c
+int fuse_reply_lseek(fuse_req_t req, off_t off)
ddf19c
+{
ddf19c
+	struct fuse_lseek_out arg;
ddf19c
+
ddf19c
+	memset(&arg, 0, sizeof(arg));
ddf19c
+	arg.offset = off;
ddf19c
+
ddf19c
+	return send_reply_ok(req, &arg, sizeof(arg));
ddf19c
+}
ddf19c
+
ddf19c
+static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	char *name = (char *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.lookup)
ddf19c
+		req->se->op.lookup(req, nodeid, name);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.forget)
ddf19c
+		req->se->op.forget(req, nodeid, arg->nlookup);
ddf19c
+	else
ddf19c
+		fuse_reply_none(req);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
ddf19c
+			    const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_batch_forget_in *arg = (void *) inarg;
ddf19c
+	struct fuse_forget_one *param = (void *) PARAM(arg);
ddf19c
+	unsigned int i;
ddf19c
+
ddf19c
+	(void) nodeid;
ddf19c
+
ddf19c
+	if (req->se->op.forget_multi) {
ddf19c
+		req->se->op.forget_multi(req, arg->count,
ddf19c
+				     (struct fuse_forget_data *) param);
ddf19c
+	} else if (req->se->op.forget) {
ddf19c
+		for (i = 0; i < arg->count; i++) {
ddf19c
+			struct fuse_forget_one *forget = ¶m[i];
ddf19c
+			struct fuse_req *dummy_req;
ddf19c
+
ddf19c
+			dummy_req = fuse_ll_alloc_req(req->se);
ddf19c
+			if (dummy_req == NULL)
ddf19c
+				break;
ddf19c
+
ddf19c
+			dummy_req->unique = req->unique;
ddf19c
+			dummy_req->ctx = req->ctx;
ddf19c
+			dummy_req->ch = NULL;
ddf19c
+
ddf19c
+			req->se->op.forget(dummy_req, forget->nodeid,
ddf19c
+					  forget->nlookup);
ddf19c
+		}
ddf19c
+		fuse_reply_none(req);
ddf19c
+	} else {
ddf19c
+		fuse_reply_none(req);
ddf19c
+	}
ddf19c
+}
ddf19c
+
ddf19c
+static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_file_info *fip = NULL;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	if (req->se->conn.proto_minor >= 9) {
ddf19c
+		struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
ddf19c
+
ddf19c
+		if (arg->getattr_flags & FUSE_GETATTR_FH) {
ddf19c
+			memset(&fi, 0, sizeof(fi));
ddf19c
+			fi.fh = arg->fh;
ddf19c
+			fip = &fi;
ddf19c
+		}
ddf19c
+	}
ddf19c
+
ddf19c
+	if (req->se->op.getattr)
ddf19c
+		req->se->op.getattr(req, nodeid, fip);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.setattr) {
ddf19c
+		struct fuse_file_info *fi = NULL;
ddf19c
+		struct fuse_file_info fi_store;
ddf19c
+		struct stat stbuf;
ddf19c
+		memset(&stbuf, 0, sizeof(stbuf));
ddf19c
+		convert_attr(arg, &stbuf);
ddf19c
+		if (arg->valid & FATTR_FH) {
ddf19c
+			arg->valid &= ~FATTR_FH;
ddf19c
+			memset(&fi_store, 0, sizeof(fi_store));
ddf19c
+			fi = &fi_store;
ddf19c
+			fi->fh = arg->fh;
ddf19c
+		}
ddf19c
+		arg->valid &=
ddf19c
+			FUSE_SET_ATTR_MODE	|
ddf19c
+			FUSE_SET_ATTR_UID	|
ddf19c
+			FUSE_SET_ATTR_GID	|
ddf19c
+			FUSE_SET_ATTR_SIZE	|
ddf19c
+			FUSE_SET_ATTR_ATIME	|
ddf19c
+			FUSE_SET_ATTR_MTIME	|
ddf19c
+			FUSE_SET_ATTR_ATIME_NOW	|
ddf19c
+			FUSE_SET_ATTR_MTIME_NOW |
ddf19c
+			FUSE_SET_ATTR_CTIME;
ddf19c
+
ddf19c
+		req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
ddf19c
+	} else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.access)
ddf19c
+		req->se->op.access(req, nodeid, arg->mask);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	(void) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.readlink)
ddf19c
+		req->se->op.readlink(req, nodeid);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
ddf19c
+	char *name = PARAM(arg);
ddf19c
+
ddf19c
+	if (req->se->conn.proto_minor >= 12)
ddf19c
+		req->ctx.umask = arg->umask;
ddf19c
+	else
ddf19c
+		name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
ddf19c
+
ddf19c
+	if (req->se->op.mknod)
ddf19c
+		req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->conn.proto_minor >= 12)
ddf19c
+		req->ctx.umask = arg->umask;
ddf19c
+
ddf19c
+	if (req->se->op.mkdir)
ddf19c
+		req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	char *name = (char *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.unlink)
ddf19c
+		req->se->op.unlink(req, nodeid, name);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	char *name = (char *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.rmdir)
ddf19c
+		req->se->op.rmdir(req, nodeid, name);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	char *name = (char *) inarg;
ddf19c
+	char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
ddf19c
+
ddf19c
+	if (req->se->op.symlink)
ddf19c
+		req->se->op.symlink(req, linkname, nodeid, name);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
ddf19c
+	char *oldname = PARAM(arg);
ddf19c
+	char *newname = oldname + strlen(oldname) + 1;
ddf19c
+
ddf19c
+	if (req->se->op.rename)
ddf19c
+		req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
ddf19c
+				  0);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
ddf19c
+	char *oldname = PARAM(arg);
ddf19c
+	char *newname = oldname + strlen(oldname) + 1;
ddf19c
+
ddf19c
+	if (req->se->op.rename)
ddf19c
+		req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
ddf19c
+				  arg->flags);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.link)
ddf19c
+		req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.create) {
ddf19c
+		struct fuse_file_info fi;
ddf19c
+		char *name = PARAM(arg);
ddf19c
+
ddf19c
+		memset(&fi, 0, sizeof(fi));
ddf19c
+		fi.flags = arg->flags;
ddf19c
+
ddf19c
+		if (req->se->conn.proto_minor >= 12)
ddf19c
+			req->ctx.umask = arg->umask;
ddf19c
+		else
ddf19c
+			name = (char *) inarg + sizeof(struct fuse_open_in);
ddf19c
+
ddf19c
+		req->se->op.create(req, nodeid, name, arg->mode, &fi);
ddf19c
+	} else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.flags = arg->flags;
ddf19c
+
ddf19c
+	if (req->se->op.open)
ddf19c
+		req->se->op.open(req, nodeid, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_open(req, &fi);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.read) {
ddf19c
+		struct fuse_file_info fi;
ddf19c
+
ddf19c
+		memset(&fi, 0, sizeof(fi));
ddf19c
+		fi.fh = arg->fh;
ddf19c
+		if (req->se->conn.proto_minor >= 9) {
ddf19c
+			fi.lock_owner = arg->lock_owner;
ddf19c
+			fi.flags = arg->flags;
ddf19c
+		}
ddf19c
+		req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
ddf19c
+	} else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+	char *param;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+	fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
ddf19c
+
ddf19c
+	if (req->se->conn.proto_minor < 9) {
ddf19c
+		param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
ddf19c
+	} else {
ddf19c
+		fi.lock_owner = arg->lock_owner;
ddf19c
+		fi.flags = arg->flags;
ddf19c
+		param = PARAM(arg);
ddf19c
+	}
ddf19c
+
ddf19c
+	if (req->se->op.write)
ddf19c
+		req->se->op.write(req, nodeid, param, arg->size,
ddf19c
+				 arg->offset, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
ddf19c
+			 const struct fuse_buf *ibuf)
ddf19c
+{
ddf19c
+	struct fuse_session *se = req->se;
ddf19c
+	struct fuse_bufvec bufv = {
ddf19c
+		.buf[0] = *ibuf,
ddf19c
+		.count = 1,
ddf19c
+	};
ddf19c
+	struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+	fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
ddf19c
+
ddf19c
+	if (se->conn.proto_minor < 9) {
ddf19c
+		bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
ddf19c
+		bufv.buf[0].size -= sizeof(struct fuse_in_header) +
ddf19c
+			FUSE_COMPAT_WRITE_IN_SIZE;
ddf19c
+		assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
ddf19c
+	} else {
ddf19c
+		fi.lock_owner = arg->lock_owner;
ddf19c
+		fi.flags = arg->flags;
ddf19c
+		if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
ddf19c
+			bufv.buf[0].mem = PARAM(arg);
ddf19c
+
ddf19c
+		bufv.buf[0].size -= sizeof(struct fuse_in_header) +
ddf19c
+			sizeof(struct fuse_write_in);
ddf19c
+	}
ddf19c
+	if (bufv.buf[0].size < arg->size) {
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
ddf19c
+		fuse_reply_err(req, EIO);
ddf19c
+		goto out;
ddf19c
+	}
ddf19c
+	bufv.buf[0].size = arg->size;
ddf19c
+
ddf19c
+	se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
ddf19c
+
ddf19c
+out:
ddf19c
+	/* Need to reset the pipe if ->write_buf() didn't consume all data */
ddf19c
+	if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
ddf19c
+		fuse_ll_clear_pipe(se);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+	fi.flush = 1;
ddf19c
+	if (req->se->conn.proto_minor >= 7)
ddf19c
+		fi.lock_owner = arg->lock_owner;
ddf19c
+
ddf19c
+	if (req->se->op.flush)
ddf19c
+		req->se->op.flush(req, nodeid, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.flags = arg->flags;
ddf19c
+	fi.fh = arg->fh;
ddf19c
+	if (req->se->conn.proto_minor >= 8) {
ddf19c
+		fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
ddf19c
+		fi.lock_owner = arg->lock_owner;
ddf19c
+	}
ddf19c
+	if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
ddf19c
+		fi.flock_release = 1;
ddf19c
+		fi.lock_owner = arg->lock_owner;
ddf19c
+	}
ddf19c
+
ddf19c
+	if (req->se->op.release)
ddf19c
+		req->se->op.release(req, nodeid, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, 0);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+	int datasync = arg->fsync_flags & 1;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+
ddf19c
+	if (req->se->op.fsync)
ddf19c
+		req->se->op.fsync(req, nodeid, datasync, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.flags = arg->flags;
ddf19c
+
ddf19c
+	if (req->se->op.opendir)
ddf19c
+		req->se->op.opendir(req, nodeid, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_open(req, &fi);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+
ddf19c
+	if (req->se->op.readdir)
ddf19c
+		req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+
ddf19c
+	if (req->se->op.readdirplus)
ddf19c
+		req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.flags = arg->flags;
ddf19c
+	fi.fh = arg->fh;
ddf19c
+
ddf19c
+	if (req->se->op.releasedir)
ddf19c
+		req->se->op.releasedir(req, nodeid, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, 0);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+	int datasync = arg->fsync_flags & 1;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+
ddf19c
+	if (req->se->op.fsyncdir)
ddf19c
+		req->se->op.fsyncdir(req, nodeid, datasync, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	(void) nodeid;
ddf19c
+	(void) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.statfs)
ddf19c
+		req->se->op.statfs(req, nodeid);
ddf19c
+	else {
ddf19c
+		struct statvfs buf = {
ddf19c
+			.f_namemax = 255,
ddf19c
+			.f_bsize = 512,
ddf19c
+		};
ddf19c
+		fuse_reply_statfs(req, &buf);
ddf19c
+	}
ddf19c
+}
ddf19c
+
ddf19c
+static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
ddf19c
+	char *name = PARAM(arg);
ddf19c
+	char *value = name + strlen(name) + 1;
ddf19c
+
ddf19c
+	if (req->se->op.setxattr)
ddf19c
+		req->se->op.setxattr(req, nodeid, name, value, arg->size,
ddf19c
+				    arg->flags);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.getxattr)
ddf19c
+		req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.listxattr)
ddf19c
+		req->se->op.listxattr(req, nodeid, arg->size);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	char *name = (char *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.removexattr)
ddf19c
+		req->se->op.removexattr(req, nodeid, name);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void convert_fuse_file_lock(struct fuse_file_lock *fl,
ddf19c
+				   struct flock *flock)
ddf19c
+{
ddf19c
+	memset(flock, 0, sizeof(struct flock));
ddf19c
+	flock->l_type = fl->type;
ddf19c
+	flock->l_whence = SEEK_SET;
ddf19c
+	flock->l_start = fl->start;
ddf19c
+	if (fl->end == OFFSET_MAX)
ddf19c
+		flock->l_len = 0;
ddf19c
+	else
ddf19c
+		flock->l_len = fl->end - fl->start + 1;
ddf19c
+	flock->l_pid = fl->pid;
ddf19c
+}
ddf19c
+
ddf19c
+static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+	struct flock flock;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+	fi.lock_owner = arg->owner;
ddf19c
+
ddf19c
+	convert_fuse_file_lock(&arg->lk, &flock);
ddf19c
+	if (req->se->op.getlk)
ddf19c
+		req->se->op.getlk(req, nodeid, &fi, &flock);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
ddf19c
+			    const void *inarg, int sleep)
ddf19c
+{
ddf19c
+	struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+	struct flock flock;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+	fi.lock_owner = arg->owner;
ddf19c
+
ddf19c
+	if (arg->lk_flags & FUSE_LK_FLOCK) {
ddf19c
+		int op = 0;
ddf19c
+
ddf19c
+		switch (arg->lk.type) {
ddf19c
+		case F_RDLCK:
ddf19c
+			op = LOCK_SH;
ddf19c
+			break;
ddf19c
+		case F_WRLCK:
ddf19c
+			op = LOCK_EX;
ddf19c
+			break;
ddf19c
+		case F_UNLCK:
ddf19c
+			op = LOCK_UN;
ddf19c
+			break;
ddf19c
+		}
ddf19c
+		if (!sleep)
ddf19c
+			op |= LOCK_NB;
ddf19c
+
ddf19c
+		if (req->se->op.flock)
ddf19c
+			req->se->op.flock(req, nodeid, &fi, op);
ddf19c
+		else
ddf19c
+			fuse_reply_err(req, ENOSYS);
ddf19c
+	} else {
ddf19c
+		convert_fuse_file_lock(&arg->lk, &flock);
ddf19c
+		if (req->se->op.setlk)
ddf19c
+			req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
ddf19c
+		else
ddf19c
+			fuse_reply_err(req, ENOSYS);
ddf19c
+	}
ddf19c
+}
ddf19c
+
ddf19c
+static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	do_setlk_common(req, nodeid, inarg, 0);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	do_setlk_common(req, nodeid, inarg, 1);
ddf19c
+}
ddf19c
+
ddf19c
+static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
ddf19c
+{
ddf19c
+	struct fuse_req *curr;
ddf19c
+
ddf19c
+	for (curr = se->list.next; curr != &se->list; curr = curr->next) {
ddf19c
+		if (curr->unique == req->u.i.unique) {
ddf19c
+			fuse_interrupt_func_t func;
ddf19c
+			void *data;
ddf19c
+
ddf19c
+			curr->ctr++;
ddf19c
+			pthread_mutex_unlock(&se->lock);
ddf19c
+
ddf19c
+			/* Ugh, ugly locking */
ddf19c
+			pthread_mutex_lock(&curr->lock);
ddf19c
+			pthread_mutex_lock(&se->lock);
ddf19c
+			curr->interrupted = 1;
ddf19c
+			func = curr->u.ni.func;
ddf19c
+			data = curr->u.ni.data;
ddf19c
+			pthread_mutex_unlock(&se->lock);
ddf19c
+			if (func)
ddf19c
+				func(curr, data);
ddf19c
+			pthread_mutex_unlock(&curr->lock);
ddf19c
+
ddf19c
+			pthread_mutex_lock(&se->lock);
ddf19c
+			curr->ctr--;
ddf19c
+			if (!curr->ctr)
ddf19c
+				destroy_req(curr);
ddf19c
+
ddf19c
+			return 1;
ddf19c
+		}
ddf19c
+	}
ddf19c
+	for (curr = se->interrupts.next; curr != &se->interrupts;
ddf19c
+	     curr = curr->next) {
ddf19c
+		if (curr->u.i.unique == req->u.i.unique)
ddf19c
+			return 1;
ddf19c
+	}
ddf19c
+	return 0;
ddf19c
+}
ddf19c
+
ddf19c
+static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
ddf19c
+	struct fuse_session *se = req->se;
ddf19c
+
ddf19c
+	(void) nodeid;
ddf19c
+	if (se->debug)
ddf19c
+		fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
ddf19c
+			(unsigned long long) arg->unique);
ddf19c
+
ddf19c
+	req->u.i.unique = arg->unique;
ddf19c
+
ddf19c
+	pthread_mutex_lock(&se->lock);
ddf19c
+	if (find_interrupted(se, req))
ddf19c
+		destroy_req(req);
ddf19c
+	else
ddf19c
+		list_add_req(req, &se->interrupts);
ddf19c
+	pthread_mutex_unlock(&se->lock);
ddf19c
+}
ddf19c
+
ddf19c
+static struct fuse_req *check_interrupt(struct fuse_session *se,
ddf19c
+					struct fuse_req *req)
ddf19c
+{
ddf19c
+	struct fuse_req *curr;
ddf19c
+
ddf19c
+	for (curr = se->interrupts.next; curr != &se->interrupts;
ddf19c
+	     curr = curr->next) {
ddf19c
+		if (curr->u.i.unique == req->unique) {
ddf19c
+			req->interrupted = 1;
ddf19c
+			list_del_req(curr);
ddf19c
+			free(curr);
ddf19c
+			return NULL;
ddf19c
+		}
ddf19c
+	}
ddf19c
+	curr = se->interrupts.next;
ddf19c
+	if (curr != &se->interrupts) {
ddf19c
+		list_del_req(curr);
ddf19c
+		list_init_req(curr);
ddf19c
+		return curr;
ddf19c
+	} else
ddf19c
+		return NULL;
ddf19c
+}
ddf19c
+
ddf19c
+static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
ddf19c
+
ddf19c
+	if (req->se->op.bmap)
ddf19c
+		req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
ddf19c
+	unsigned int flags = arg->flags;
ddf19c
+	void *in_buf = arg->in_size ? PARAM(arg) : NULL;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	if (flags & FUSE_IOCTL_DIR &&
ddf19c
+	    !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) {
ddf19c
+		fuse_reply_err(req, ENOTTY);
ddf19c
+		return;
ddf19c
+	}
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+
ddf19c
+	if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
ddf19c
+	    !(flags & FUSE_IOCTL_32BIT)) {
ddf19c
+		req->ioctl_64bit = 1;
ddf19c
+	}
ddf19c
+
ddf19c
+	if (req->se->op.ioctl)
ddf19c
+		req->se->op.ioctl(req, nodeid, arg->cmd,
ddf19c
+				 (void *)(uintptr_t)arg->arg, &fi, flags,
ddf19c
+				 in_buf, arg->in_size, arg->out_size);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
ddf19c
+{
ddf19c
+	free(ph);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+	fi.poll_events = arg->events;
ddf19c
+
ddf19c
+	if (req->se->op.poll) {
ddf19c
+		struct fuse_pollhandle *ph = NULL;
ddf19c
+
ddf19c
+		if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
ddf19c
+			ph = malloc(sizeof(struct fuse_pollhandle));
ddf19c
+			if (ph == NULL) {
ddf19c
+				fuse_reply_err(req, ENOMEM);
ddf19c
+				return;
ddf19c
+			}
ddf19c
+			ph->kh = arg->kh;
ddf19c
+			ph->se = req->se;
ddf19c
+		}
ddf19c
+
ddf19c
+		req->se->op.poll(req, nodeid, &fi, ph);
ddf19c
+	} else {
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+	}
ddf19c
+}
ddf19c
+
ddf19c
+static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+
ddf19c
+	if (req->se->op.fallocate)
ddf19c
+		req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
ddf19c
+	struct fuse_file_info fi_in, fi_out;
ddf19c
+
ddf19c
+	memset(&fi_in, 0, sizeof(fi_in));
ddf19c
+	fi_in.fh = arg->fh_in;
ddf19c
+
ddf19c
+	memset(&fi_out, 0, sizeof(fi_out));
ddf19c
+	fi_out.fh = arg->fh_out;
ddf19c
+
ddf19c
+
ddf19c
+	if (req->se->op.copy_file_range)
ddf19c
+		req->se->op.copy_file_range(req, nodeid_in, arg->off_in,
ddf19c
+					    &fi_in, arg->nodeid_out,
ddf19c
+					    arg->off_out, &fi_out, arg->len,
ddf19c
+					    arg->flags);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg;
ddf19c
+	struct fuse_file_info fi;
ddf19c
+
ddf19c
+	memset(&fi, 0, sizeof(fi));
ddf19c
+	fi.fh = arg->fh;
ddf19c
+
ddf19c
+	if (req->se->op.lseek)
ddf19c
+		req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
ddf19c
+	else
ddf19c
+		fuse_reply_err(req, ENOSYS);
ddf19c
+}
ddf19c
+
ddf19c
+static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
ddf19c
+{
ddf19c
+	struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
ddf19c
+	struct fuse_init_out outarg;
ddf19c
+	struct fuse_session *se = req->se;
ddf19c
+	size_t bufsize = se->bufsize;
ddf19c
+	size_t outargsize = sizeof(outarg);
ddf19c
+
ddf19c
+	(void) nodeid;
ddf19c
+	if (se->debug) {
ddf19c
+		fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
ddf19c
+		if (arg->major == 7 && arg->minor >= 6) {
ddf19c
+			fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
ddf19c
+			fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
ddf19c
+				arg->max_readahead);
ddf19c
+		}
ddf19c
+	}
ddf19c
+	se->conn.proto_major = arg->major;
ddf19c
+	se->conn.proto_minor = arg->minor;
ddf19c
+	se->conn.capable = 0;
ddf19c
+	se->conn.want = 0;
ddf19c
+
ddf19c
+	memset(&outarg, 0, sizeof(outarg));
ddf19c
+	outarg.major = FUSE_KERNEL_VERSION;
ddf19c
+	outarg.minor = FUSE_KERNEL_MINOR_VERSION;
ddf19c
+
ddf19c
+	if (arg->major < 7) {
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
ddf19c
+			arg->major, arg->minor);
ddf19c
+		fuse_reply_err(req, EPROTO);
ddf19c
+		return;
ddf19c
+	}
ddf19c
+
ddf19c
+	if (arg->major > 7) {
ddf19c
+		/* Wait for a second INIT request with a 7.X version */
ddf19c
+		send_reply_ok(req, &outarg, sizeof(outarg));
ddf19c
+		return;
ddf19c
+	}
ddf19c
+
ddf19c
+	if (arg->minor >= 6) {
ddf19c
+		if (arg->max_readahead < se->conn.max_readahead)
ddf19c
+			se->conn.max_readahead = arg->max_readahead;
ddf19c
+		if (arg->flags & FUSE_ASYNC_READ)
ddf19c
+			se->conn.capable |= FUSE_CAP_ASYNC_READ;
ddf19c
+		if (arg->flags & FUSE_POSIX_LOCKS)
ddf19c
+			se->conn.capable |= FUSE_CAP_POSIX_LOCKS;
ddf19c
+		if (arg->flags & FUSE_ATOMIC_O_TRUNC)
ddf19c
+			se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
ddf19c
+		if (arg->flags & FUSE_EXPORT_SUPPORT)
ddf19c
+			se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
ddf19c
+		if (arg->flags & FUSE_DONT_MASK)
ddf19c
+			se->conn.capable |= FUSE_CAP_DONT_MASK;
ddf19c
+		if (arg->flags & FUSE_FLOCK_LOCKS)
ddf19c
+			se->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
ddf19c
+		if (arg->flags & FUSE_AUTO_INVAL_DATA)
ddf19c
+			se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA;
ddf19c
+		if (arg->flags & FUSE_DO_READDIRPLUS)
ddf19c
+			se->conn.capable |= FUSE_CAP_READDIRPLUS;
ddf19c
+		if (arg->flags & FUSE_READDIRPLUS_AUTO)
ddf19c
+			se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO;
ddf19c
+		if (arg->flags & FUSE_ASYNC_DIO)
ddf19c
+			se->conn.capable |= FUSE_CAP_ASYNC_DIO;
ddf19c
+		if (arg->flags & FUSE_WRITEBACK_CACHE)
ddf19c
+			se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
ddf19c
+		if (arg->flags & FUSE_NO_OPEN_SUPPORT)
ddf19c
+			se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT;
ddf19c
+		if (arg->flags & FUSE_PARALLEL_DIROPS)
ddf19c
+			se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS;
ddf19c
+		if (arg->flags & FUSE_POSIX_ACL)
ddf19c
+			se->conn.capable |= FUSE_CAP_POSIX_ACL;
ddf19c
+		if (arg->flags & FUSE_HANDLE_KILLPRIV)
ddf19c
+			se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV;
ddf19c
+		if (arg->flags & FUSE_NO_OPENDIR_SUPPORT)
ddf19c
+			se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT;
ddf19c
+		if (!(arg->flags & FUSE_MAX_PAGES)) {
ddf19c
+			size_t max_bufsize =
ddf19c
+				FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
ddf19c
+				+ FUSE_BUFFER_HEADER_SIZE;
ddf19c
+			if (bufsize > max_bufsize) {
ddf19c
+				bufsize = max_bufsize;
ddf19c
+			}
ddf19c
+		}
ddf19c
+	} else {
ddf19c
+		se->conn.max_readahead = 0;
ddf19c
+	}
ddf19c
+
ddf19c
+	if (se->conn.proto_minor >= 14) {
ddf19c
+#ifdef HAVE_SPLICE
ddf19c
+#ifdef HAVE_VMSPLICE
ddf19c
+		se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
ddf19c
+#endif
ddf19c
+		se->conn.capable |= FUSE_CAP_SPLICE_READ;
ddf19c
+#endif
ddf19c
+	}
ddf19c
+	if (se->conn.proto_minor >= 18)
ddf19c
+		se->conn.capable |= FUSE_CAP_IOCTL_DIR;
ddf19c
+
ddf19c
+	/* Default settings for modern filesystems.
ddf19c
+	 *
ddf19c
+	 * Most of these capabilities were disabled by default in
ddf19c
+	 * libfuse2 for backwards compatibility reasons. In libfuse3,
ddf19c
+	 * we can finally enable them by default (as long as they're
ddf19c
+	 * supported by the kernel).
ddf19c
+	 */
ddf19c
+#define LL_SET_DEFAULT(cond, cap) \
ddf19c
+	if ((cond) && (se->conn.capable & (cap))) \
ddf19c
+		se->conn.want |= (cap)
ddf19c
+	LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
ddf19c
+	LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS);
ddf19c
+	LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
ddf19c
+	LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV);
ddf19c
+	LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
ddf19c
+	LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
ddf19c
+	LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
ddf19c
+	LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
ddf19c
+	LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
ddf19c
+		       FUSE_CAP_POSIX_LOCKS);
ddf19c
+	LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
ddf19c
+	LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
ddf19c
+	LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
ddf19c
+		       FUSE_CAP_READDIRPLUS_AUTO);
ddf19c
+	se->conn.time_gran = 1;
ddf19c
+	
ddf19c
+	if (bufsize < FUSE_MIN_READ_BUFFER) {
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n",
ddf19c
+			bufsize);
ddf19c
+		bufsize = FUSE_MIN_READ_BUFFER;
ddf19c
+	}
ddf19c
+	se->bufsize = bufsize;
ddf19c
+
ddf19c
+	if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE)
ddf19c
+		se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE;
ddf19c
+
ddf19c
+	se->got_init = 1;
ddf19c
+	if (se->op.init)
ddf19c
+		se->op.init(se->userdata, &se->conn);
ddf19c
+
ddf19c
+	if (se->conn.want & (~se->conn.capable)) {
ddf19c
+		fuse_log(FUSE_LOG_ERR, "fuse: error: filesystem requested capabilities "
ddf19c
+			"0x%x that are not supported by kernel, aborting.\n",
ddf19c
+			se->conn.want & (~se->conn.capable));
ddf19c
+		fuse_reply_err(req, EPROTO);
ddf19c
+		se->error = -EPROTO;
ddf19c
+		fuse_session_exit(se);
ddf19c
+		return;
ddf19c
+	}
ddf19c
+
ddf19c
+	unsigned max_read_mo = get_max_read(se->mo);
ddf19c
+	if (se->conn.max_read != max_read_mo) {
ddf19c