From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5B48BC2D0DB for ; Thu, 23 Jan 2020 13:06:10 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E7BB320661 for ; Thu, 23 Jan 2020 13:06:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="hOi1y8lU" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E7BB320661 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:56740 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iucBU-0000VS-J6 for qemu-devel@archiver.kernel.org; Thu, 23 Jan 2020 08:06:08 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:40056) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iub8a-0001mL-3f for qemu-devel@nongnu.org; Thu, 23 Jan 2020 06:59:13 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iub8R-0004k5-Pa for qemu-devel@nongnu.org; Thu, 23 Jan 2020 06:59:03 -0500 Received: from us-smtp-1.mimecast.com ([207.211.31.81]:51729 helo=us-smtp-delivery-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iub8R-0004jt-Fg for qemu-devel@nongnu.org; Thu, 23 Jan 2020 06:58:55 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1579780735; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UIGV8lWSCSA87/pBAttP0Zsadhex+IibMiYCPpQjm9M=; b=hOi1y8lUBt6vQbT9wFPyZeTEpvlVZQwnikswZnCHbMU4bhW1YMUBacWOcf7R0s+wArrERG QMlVLm4ePbBw1A9H1uw7ZNcYkZWtCU+athpL0j39dGbItVDkX0pIp7L/HWjJz9RcIF+vvx iZhrTX7HgnkcAaNBcIfggZ0avqbEBtM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-377-he8A-2zVO6S9HD7jNJgPsA-1; Thu, 23 Jan 2020 06:58:52 -0500 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 0E9B31088382 for ; Thu, 23 Jan 2020 11:58:52 +0000 (UTC) Received: from dgilbert-t580.localhost (unknown [10.36.118.0]) by smtp.corp.redhat.com (Postfix) with ESMTP id 579E21CB; Thu, 23 Jan 2020 11:58:51 +0000 (UTC) From: "Dr. David Alan Gilbert (git)" To: qemu-devel@nongnu.org, stefanha@redhat.com Subject: [PULL 004/111] virtiofsd: Add fuse_lowlevel.c Date: Thu, 23 Jan 2020 11:56:54 +0000 Message-Id: <20200123115841.138849-5-dgilbert@redhat.com> In-Reply-To: <20200123115841.138849-1-dgilbert@redhat.com> References: <20200123115841.138849-1-dgilbert@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-MC-Unique: he8A-2zVO6S9HD7jNJgPsA-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 207.211.31.81 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" From: "Dr. David Alan Gilbert" fuse_lowlevel is one of the largest files from the library and does most of the work. Add it separately to keep the diff sizes small. Again this is from upstream fuse-3.8.0 Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Daniel P. Berrang=C3=A9 Signed-off-by: Dr. David Alan Gilbert --- tools/virtiofsd/fuse_lowlevel.c | 3129 +++++++++++++++++++++++++++++++ 1 file changed, 3129 insertions(+) create mode 100644 tools/virtiofsd/fuse_lowlevel.c diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowleve= l.c new file mode 100644 index 0000000000..f2d7038e34 --- /dev/null +++ b/tools/virtiofsd/fuse_lowlevel.c @@ -0,0 +1,3129 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + Implementation of (most of) the low-level FUSE API. The session loop + functions are implemented in separate files. + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#define _GNU_SOURCE + +#include "config.h" +#include "fuse_i.h" +#include "fuse_kernel.h" +#include "fuse_opt.h" +#include "fuse_misc.h" +#include "mount_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif +#ifndef F_SETPIPE_SZ +#define F_SETPIPE_SZ=09(F_LINUX_SPECIFIC_BASE + 7) +#endif + + +#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) +#define OFFSET_MAX 0x7fffffffffffffffLL + +#define container_of(ptr, type, member) ({=09=09=09=09\ +=09=09=09const typeof( ((type *)0)->member ) *__mptr =3D (ptr); \ +=09=09=09(type *)( (char *)__mptr - offsetof(type,member) );}) + +struct fuse_pollhandle { +=09uint64_t kh; +=09struct fuse_session *se; +}; + +static size_t pagesize; + +static __attribute__((constructor)) void fuse_ll_init_pagesize(void) +{ +=09pagesize =3D getpagesize(); +} + +static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) +{ +=09attr->ino=09=3D stbuf->st_ino; +=09attr->mode=09=3D stbuf->st_mode; +=09attr->nlink=09=3D stbuf->st_nlink; +=09attr->uid=09=3D stbuf->st_uid; +=09attr->gid=09=3D stbuf->st_gid; +=09attr->rdev=09=3D stbuf->st_rdev; +=09attr->size=09=3D stbuf->st_size; +=09attr->blksize=09=3D stbuf->st_blksize; +=09attr->blocks=09=3D stbuf->st_blocks; +=09attr->atime=09=3D stbuf->st_atime; +=09attr->mtime=09=3D stbuf->st_mtime; +=09attr->ctime=09=3D stbuf->st_ctime; +=09attr->atimensec =3D ST_ATIM_NSEC(stbuf); +=09attr->mtimensec =3D ST_MTIM_NSEC(stbuf); +=09attr->ctimensec =3D ST_CTIM_NSEC(stbuf); +} + +static void convert_attr(const struct fuse_setattr_in *attr, struct stat *= stbuf) +{ +=09stbuf->st_mode=09 =3D attr->mode; +=09stbuf->st_uid=09 =3D attr->uid; +=09stbuf->st_gid=09 =3D attr->gid; +=09stbuf->st_size=09 =3D attr->size; +=09stbuf->st_atime=09 =3D attr->atime; +=09stbuf->st_mtime=09 =3D attr->mtime; +=09stbuf->st_ctime =3D attr->ctime; +=09ST_ATIM_NSEC_SET(stbuf, attr->atimensec); +=09ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); +=09ST_CTIM_NSEC_SET(stbuf, attr->ctimensec); +} + +static=09size_t iov_length(const struct iovec *iov, size_t count) +{ +=09size_t seg; +=09size_t ret =3D 0; + +=09for (seg =3D 0; seg < count; seg++) +=09=09ret +=3D iov[seg].iov_len; +=09return ret; +} + +static void list_init_req(struct fuse_req *req) +{ +=09req->next =3D req; +=09req->prev =3D req; +} + +static void list_del_req(struct fuse_req *req) +{ +=09struct fuse_req *prev =3D req->prev; +=09struct fuse_req *next =3D req->next; +=09prev->next =3D next; +=09next->prev =3D prev; +} + +static void list_add_req(struct fuse_req *req, struct fuse_req *next) +{ +=09struct fuse_req *prev =3D next->prev; +=09req->next =3D next; +=09req->prev =3D prev; +=09prev->next =3D req; +=09next->prev =3D req; +} + +static void destroy_req(fuse_req_t req) +{ +=09pthread_mutex_destroy(&req->lock); +=09free(req); +} + +void fuse_free_req(fuse_req_t req) +{ +=09int ctr; +=09struct fuse_session *se =3D req->se; + +=09pthread_mutex_lock(&se->lock); +=09req->u.ni.func =3D NULL; +=09req->u.ni.data =3D NULL; +=09list_del_req(req); +=09ctr =3D --req->ctr; +=09fuse_chan_put(req->ch); +=09req->ch =3D NULL; +=09pthread_mutex_unlock(&se->lock); +=09if (!ctr) +=09=09destroy_req(req); +} + +static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se) +{ +=09struct fuse_req *req; + +=09req =3D (struct fuse_req *) calloc(1, sizeof(struct fuse_req)); +=09if (req =3D=3D NULL) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n"); +=09} else { +=09=09req->se =3D se; +=09=09req->ctr =3D 1; +=09=09list_init_req(req); +=09=09fuse_mutex_init(&req->lock); +=09} + +=09return req; +} + +/* Send data. If *ch* is NULL, send via session master fd */ +static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, +=09=09=09 struct iovec *iov, int count) +{ +=09struct fuse_out_header *out =3D iov[0].iov_base; + +=09out->len =3D iov_length(iov, count); +=09if (se->debug) { +=09=09if (out->unique =3D=3D 0) { +=09=09=09fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=3D%d length=3D%u\n", +=09=09=09=09out->error, out->len); +=09=09} else if (out->error) { +=09=09=09fuse_log(FUSE_LOG_DEBUG, +=09=09=09=09" unique: %llu, error: %i (%s), outsize: %i\n", +=09=09=09=09(unsigned long long) out->unique, out->error, +=09=09=09=09strerror(-out->error), out->len); +=09=09} else { +=09=09=09fuse_log(FUSE_LOG_DEBUG, +=09=09=09=09" unique: %llu, success, outsize: %i\n", +=09=09=09=09(unsigned long long) out->unique, out->len); +=09=09} +=09} + +=09ssize_t res =3D writev(ch ? ch->fd : se->fd, +=09=09=09 iov, count); +=09int err =3D errno; + +=09if (res =3D=3D -1) { +=09=09assert(se !=3D NULL); + +=09=09/* ENOENT means the operation was interrupted */ +=09=09if (!fuse_session_exited(se) && err !=3D ENOENT) +=09=09=09perror("fuse: writing device"); +=09=09return -err; +=09} + +=09return 0; +} + + +int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *io= v, +=09=09=09 int count) +{ +=09struct fuse_out_header out; + +=09if (error <=3D -1000 || error > 0) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n",=09error); +=09=09error =3D -ERANGE; +=09} + +=09out.unique =3D req->unique; +=09out.error =3D error; + +=09iov[0].iov_base =3D &out; +=09iov[0].iov_len =3D sizeof(struct fuse_out_header); + +=09return fuse_send_msg(req->se, req->ch, iov, count); +} + +static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, +=09=09=09 int count) +{ +=09int res; + +=09res =3D fuse_send_reply_iov_nofree(req, error, iov, count); +=09fuse_free_req(req); +=09return res; +} + +static int send_reply(fuse_req_t req, int error, const void *arg, +=09=09 size_t argsize) +{ +=09struct iovec iov[2]; +=09int count =3D 1; +=09if (argsize) { +=09=09iov[1].iov_base =3D (void *) arg; +=09=09iov[1].iov_len =3D argsize; +=09=09count++; +=09} +=09return send_reply_iov(req, error, iov, count); +} + +int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) +{ +=09int res; +=09struct iovec *padded_iov; + +=09padded_iov =3D malloc((count + 1) * sizeof(struct iovec)); +=09if (padded_iov =3D=3D NULL) +=09=09return fuse_reply_err(req, ENOMEM); + +=09memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); +=09count++; + +=09res =3D send_reply_iov(req, 0, padded_iov, count); +=09free(padded_iov); + +=09return res; +} + + +/* `buf` is allowed to be empty so that the proper size may be + allocated by the caller */ +size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, +=09=09=09 const char *name, const struct stat *stbuf, off_t off) +{ +=09(void)req; +=09size_t namelen; +=09size_t entlen; +=09size_t entlen_padded; +=09struct fuse_dirent *dirent; + +=09namelen =3D strlen(name); +=09entlen =3D FUSE_NAME_OFFSET + namelen; +=09entlen_padded =3D FUSE_DIRENT_ALIGN(entlen); + +=09if ((buf =3D=3D NULL) || (entlen_padded > bufsize)) +=09 return entlen_padded; + +=09dirent =3D (struct fuse_dirent*) buf; +=09dirent->ino =3D stbuf->st_ino; +=09dirent->off =3D off; +=09dirent->namelen =3D namelen; +=09dirent->type =3D (stbuf->st_mode & S_IFMT) >> 12; +=09memcpy(dirent->name, name, namelen); +=09memset(dirent->name + namelen, 0, entlen_padded - entlen); + +=09return entlen_padded; +} + +static void convert_statfs(const struct statvfs *stbuf, +=09=09=09 struct fuse_kstatfs *kstatfs) +{ +=09kstatfs->bsize=09 =3D stbuf->f_bsize; +=09kstatfs->frsize=09 =3D stbuf->f_frsize; +=09kstatfs->blocks=09 =3D stbuf->f_blocks; +=09kstatfs->bfree=09 =3D stbuf->f_bfree; +=09kstatfs->bavail=09 =3D stbuf->f_bavail; +=09kstatfs->files=09 =3D stbuf->f_files; +=09kstatfs->ffree=09 =3D stbuf->f_ffree; +=09kstatfs->namelen =3D stbuf->f_namemax; +} + +static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) +{ +=09return send_reply(req, 0, arg, argsize); +} + +int fuse_reply_err(fuse_req_t req, int err) +{ +=09return send_reply(req, -err, NULL, 0); +} + +void fuse_reply_none(fuse_req_t req) +{ +=09fuse_free_req(req); +} + +static unsigned long calc_timeout_sec(double t) +{ +=09if (t > (double) ULONG_MAX) +=09=09return ULONG_MAX; +=09else if (t < 0.0) +=09=09return 0; +=09else +=09=09return (unsigned long) t; +} + +static unsigned int calc_timeout_nsec(double t) +{ +=09double f =3D t - (double) calc_timeout_sec(t); +=09if (f < 0.0) +=09=09return 0; +=09else if (f >=3D 0.999999999) +=09=09return 999999999; +=09else +=09=09return (unsigned int) (f * 1.0e9); +} + +static void fill_entry(struct fuse_entry_out *arg, +=09=09 const struct fuse_entry_param *e) +{ +=09arg->nodeid =3D e->ino; +=09arg->generation =3D e->generation; +=09arg->entry_valid =3D calc_timeout_sec(e->entry_timeout); +=09arg->entry_valid_nsec =3D calc_timeout_nsec(e->entry_timeout); +=09arg->attr_valid =3D calc_timeout_sec(e->attr_timeout); +=09arg->attr_valid_nsec =3D calc_timeout_nsec(e->attr_timeout); +=09convert_stat(&e->attr, &arg->attr); +} + +/* `buf` is allowed to be empty so that the proper size may be + allocated by the caller */ +size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, +=09=09=09 const char *name, +=09=09=09 const struct fuse_entry_param *e, off_t off) +{ +=09(void)req; +=09size_t namelen; +=09size_t entlen; +=09size_t entlen_padded; + +=09namelen =3D strlen(name); +=09entlen =3D FUSE_NAME_OFFSET_DIRENTPLUS + namelen; +=09entlen_padded =3D FUSE_DIRENT_ALIGN(entlen); +=09if ((buf =3D=3D NULL) || (entlen_padded > bufsize)) +=09 return entlen_padded; + +=09struct fuse_direntplus *dp =3D (struct fuse_direntplus *) buf; +=09memset(&dp->entry_out, 0, sizeof(dp->entry_out)); +=09fill_entry(&dp->entry_out, e); + +=09struct fuse_dirent *dirent =3D &dp->dirent; +=09dirent->ino =3D e->attr.st_ino; +=09dirent->off =3D off; +=09dirent->namelen =3D namelen; +=09dirent->type =3D (e->attr.st_mode & S_IFMT) >> 12; +=09memcpy(dirent->name, name, namelen); +=09memset(dirent->name + namelen, 0, entlen_padded - entlen); + +=09return entlen_padded; +} + +static void fill_open(struct fuse_open_out *arg, +=09=09 const struct fuse_file_info *f) +{ +=09arg->fh =3D f->fh; +=09if (f->direct_io) +=09=09arg->open_flags |=3D FOPEN_DIRECT_IO; +=09if (f->keep_cache) +=09=09arg->open_flags |=3D FOPEN_KEEP_CACHE; +=09if (f->cache_readdir) +=09=09arg->open_flags |=3D FOPEN_CACHE_DIR; +=09if (f->nonseekable) +=09=09arg->open_flags |=3D FOPEN_NONSEEKABLE; +} + +int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) +{ +=09struct fuse_entry_out arg; +=09size_t size =3D req->se->conn.proto_minor < 9 ? +=09=09FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg); + +=09/* before ABI 7.4 e->ino =3D=3D 0 was invalid, only ENOENT meant +=09 negative entry */ +=09if (!e->ino && req->se->conn.proto_minor < 4) +=09=09return fuse_reply_err(req, ENOENT); + +=09memset(&arg, 0, sizeof(arg)); +=09fill_entry(&arg, e); +=09return send_reply_ok(req, &arg, size); +} + +int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, +=09=09 const struct fuse_file_info *f) +{ +=09char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; +=09size_t entrysize =3D req->se->conn.proto_minor < 9 ? +=09=09FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out); +=09struct fuse_entry_out *earg =3D (struct fuse_entry_out *) buf; +=09struct fuse_open_out *oarg =3D (struct fuse_open_out *) (buf + entrysiz= e); + +=09memset(buf, 0, sizeof(buf)); +=09fill_entry(earg, e); +=09fill_open(oarg, f); +=09return send_reply_ok(req, buf, +=09=09=09 entrysize + sizeof(struct fuse_open_out)); +} + +int fuse_reply_attr(fuse_req_t req, const struct stat *attr, +=09=09 double attr_timeout) +{ +=09struct fuse_attr_out arg; +=09size_t size =3D req->se->conn.proto_minor < 9 ? +=09=09FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); + +=09memset(&arg, 0, sizeof(arg)); +=09arg.attr_valid =3D calc_timeout_sec(attr_timeout); +=09arg.attr_valid_nsec =3D calc_timeout_nsec(attr_timeout); +=09convert_stat(attr, &arg.attr); + +=09return send_reply_ok(req, &arg, size); +} + +int fuse_reply_readlink(fuse_req_t req, const char *linkname) +{ +=09return send_reply_ok(req, linkname, strlen(linkname)); +} + +int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) +{ +=09struct fuse_open_out arg; + +=09memset(&arg, 0, sizeof(arg)); +=09fill_open(&arg, f); +=09return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_write(fuse_req_t req, size_t count) +{ +=09struct fuse_write_out arg; + +=09memset(&arg, 0, sizeof(arg)); +=09arg.size =3D count; + +=09return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) +{ +=09return send_reply_ok(req, buf, size); +} + +static int fuse_send_data_iov_fallback(struct fuse_session *se, +=09=09=09=09 struct fuse_chan *ch, +=09=09=09=09 struct iovec *iov, int iov_count, +=09=09=09=09 struct fuse_bufvec *buf, +=09=09=09=09 size_t len) +{ +=09struct fuse_bufvec mem_buf =3D FUSE_BUFVEC_INIT(len); +=09void *mbuf; +=09int res; + +=09/* Optimize common case */ +=09if (buf->count =3D=3D 1 && buf->idx =3D=3D 0 && buf->off =3D=3D 0 && +=09 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { +=09=09/* FIXME: also avoid memory copy if there are multiple buffers +=09=09 but none of them contain an fd */ + +=09=09iov[iov_count].iov_base =3D buf->buf[0].mem; +=09=09iov[iov_count].iov_len =3D len; +=09=09iov_count++; +=09=09return fuse_send_msg(se, ch, iov, iov_count); +=09} + +=09res =3D posix_memalign(&mbuf, pagesize, len); +=09if (res !=3D 0) +=09=09return res; + +=09mem_buf.buf[0].mem =3D mbuf; +=09res =3D fuse_buf_copy(&mem_buf, buf, 0); +=09if (res < 0) { +=09=09free(mbuf); +=09=09return -res; +=09} +=09len =3D res; + +=09iov[iov_count].iov_base =3D mbuf; +=09iov[iov_count].iov_len =3D len; +=09iov_count++; +=09res =3D fuse_send_msg(se, ch, iov, iov_count); +=09free(mbuf); + +=09return res; +} + +struct fuse_ll_pipe { +=09size_t size; +=09int can_grow; +=09int pipe[2]; +}; + +static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp) +{ +=09close(llp->pipe[0]); +=09close(llp->pipe[1]); +=09free(llp); +} + +#ifdef HAVE_SPLICE +#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC) +static int fuse_pipe(int fds[2]) +{ +=09int rv =3D pipe(fds); + +=09if (rv =3D=3D -1) +=09=09return rv; + +=09if (fcntl(fds[0], F_SETFL, O_NONBLOCK) =3D=3D -1 || +=09 fcntl(fds[1], F_SETFL, O_NONBLOCK) =3D=3D -1 || +=09 fcntl(fds[0], F_SETFD, FD_CLOEXEC) =3D=3D -1 || +=09 fcntl(fds[1], F_SETFD, FD_CLOEXEC) =3D=3D -1) { +=09=09close(fds[0]); +=09=09close(fds[1]); +=09=09rv =3D -1; +=09} +=09return rv; +} +#else +static int fuse_pipe(int fds[2]) +{ +=09return pipe2(fds, O_CLOEXEC | O_NONBLOCK); +} +#endif + +static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se) +{ +=09struct fuse_ll_pipe *llp =3D pthread_getspecific(se->pipe_key); +=09if (llp =3D=3D NULL) { +=09=09int res; + +=09=09llp =3D malloc(sizeof(struct fuse_ll_pipe)); +=09=09if (llp =3D=3D NULL) +=09=09=09return NULL; + +=09=09res =3D fuse_pipe(llp->pipe); +=09=09if (res =3D=3D -1) { +=09=09=09free(llp); +=09=09=09return NULL; +=09=09} + +=09=09/* +=09=09 *the default size is 16 pages on linux +=09=09 */ +=09=09llp->size =3D pagesize * 16; +=09=09llp->can_grow =3D 1; + +=09=09pthread_setspecific(se->pipe_key, llp); +=09} + +=09return llp; +} +#endif + +static void fuse_ll_clear_pipe(struct fuse_session *se) +{ +=09struct fuse_ll_pipe *llp =3D pthread_getspecific(se->pipe_key); +=09if (llp) { +=09=09pthread_setspecific(se->pipe_key, NULL); +=09=09fuse_ll_pipe_free(llp); +=09} +} + +#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE) +static int read_back(int fd, char *buf, size_t len) +{ +=09int res; + +=09res =3D read(fd, buf, len); +=09if (res =3D=3D -1) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back fr= om pipe: %s\n", strerror(errno)); +=09=09return -EIO; +=09} +=09if (res !=3D len) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from p= ipe: %i from %zi\n", res, len); +=09=09return -EIO; +=09} +=09return 0; +} + +static int grow_pipe_to_max(int pipefd) +{ +=09int max; +=09int res; +=09int maxfd; +=09char buf[32]; + +=09maxfd =3D open("/proc/sys/fs/pipe-max-size", O_RDONLY); +=09if (maxfd < 0) +=09=09return -errno; + +=09res =3D read(maxfd, buf, sizeof(buf) - 1); +=09if (res < 0) { +=09=09int saved_errno; + +=09=09saved_errno =3D errno; +=09=09close(maxfd); +=09=09return -saved_errno; +=09} +=09close(maxfd); +=09buf[res] =3D '\0'; + +=09max =3D atoi(buf); +=09res =3D fcntl(pipefd, F_SETPIPE_SZ, max); +=09if (res < 0) +=09=09return -errno; +=09return max; +} + +static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *c= h, +=09=09=09 struct iovec *iov, int iov_count, +=09=09=09 struct fuse_bufvec *buf, unsigned int flags) +{ +=09int res; +=09size_t len =3D fuse_buf_size(buf); +=09struct fuse_out_header *out =3D iov[0].iov_base; +=09struct fuse_ll_pipe *llp; +=09int splice_flags; +=09size_t pipesize; +=09size_t total_fd_size; +=09size_t idx; +=09size_t headerlen; +=09struct fuse_bufvec pipe_buf =3D FUSE_BUFVEC_INIT(len); + +=09if (se->broken_splice_nonblock) +=09=09goto fallback; + +=09if (flags & FUSE_BUF_NO_SPLICE) +=09=09goto fallback; + +=09total_fd_size =3D 0; +=09for (idx =3D buf->idx; idx < buf->count; idx++) { +=09=09if (buf->buf[idx].flags & FUSE_BUF_IS_FD) { +=09=09=09total_fd_size =3D buf->buf[idx].size; +=09=09=09if (idx =3D=3D buf->idx) +=09=09=09=09total_fd_size -=3D buf->off; +=09=09} +=09} +=09if (total_fd_size < 2 * pagesize) +=09=09goto fallback; + +=09if (se->conn.proto_minor < 14 || +=09 !(se->conn.want & FUSE_CAP_SPLICE_WRITE)) +=09=09goto fallback; + +=09llp =3D fuse_ll_get_pipe(se); +=09if (llp =3D=3D NULL) +=09=09goto fallback; + + +=09headerlen =3D iov_length(iov, iov_count); + +=09out->len =3D headerlen + len; + +=09/* +=09 * Heuristic for the required pipe size, does not work if the +=09 * source contains less than page size fragments +=09 */ +=09pipesize =3D pagesize * (iov_count + buf->count + 1) + out->len; + +=09if (llp->size < pipesize) { +=09=09if (llp->can_grow) { +=09=09=09res =3D fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize); +=09=09=09if (res =3D=3D -1) { +=09=09=09=09res =3D grow_pipe_to_max(llp->pipe[0]); +=09=09=09=09if (res > 0) +=09=09=09=09=09llp->size =3D res; +=09=09=09=09llp->can_grow =3D 0; +=09=09=09=09goto fallback; +=09=09=09} +=09=09=09llp->size =3D res; +=09=09} +=09=09if (llp->size < pipesize) +=09=09=09goto fallback; +=09} + + +=09res =3D vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK); +=09if (res =3D=3D -1) +=09=09goto fallback; + +=09if (res !=3D headerlen) { +=09=09res =3D -EIO; +=09=09fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res= , +=09=09=09headerlen); +=09=09goto clear_pipe; +=09} + +=09pipe_buf.buf[0].flags =3D FUSE_BUF_IS_FD; +=09pipe_buf.buf[0].fd =3D llp->pipe[1]; + +=09res =3D fuse_buf_copy(&pipe_buf, buf, +=09=09=09 FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK); +=09if (res < 0) { +=09=09if (res =3D=3D -EAGAIN || res =3D=3D -EINVAL) { +=09=09=09/* +=09=09=09 * Should only get EAGAIN on kernels with +=09=09=09 * broken SPLICE_F_NONBLOCK support (<=3D +=09=09=09 * 2.6.35) where this error or a short read is +=09=09=09 * returned even if the pipe itself is not +=09=09=09 * full +=09=09=09 * +=09=09=09 * EINVAL might mean that splice can't handle +=09=09=09 * this combination of input and output. +=09=09=09 */ +=09=09=09if (res =3D=3D -EAGAIN) +=09=09=09=09se->broken_splice_nonblock =3D 1; + +=09=09=09pthread_setspecific(se->pipe_key, NULL); +=09=09=09fuse_ll_pipe_free(llp); +=09=09=09goto fallback; +=09=09} +=09=09res =3D -res; +=09=09goto clear_pipe; +=09} + +=09if (res !=3D 0 && res < len) { +=09=09struct fuse_bufvec mem_buf =3D FUSE_BUFVEC_INIT(len); +=09=09void *mbuf; +=09=09size_t now_len =3D res; +=09=09/* +=09=09 * For regular files a short count is either +=09=09 * 1) due to EOF, or +=09=09 * 2) because of broken SPLICE_F_NONBLOCK (see above) +=09=09 * +=09=09 * For other inputs it's possible that we overflowed +=09=09 * the pipe because of small buffer fragments. +=09=09 */ + +=09=09res =3D posix_memalign(&mbuf, pagesize, len); +=09=09if (res !=3D 0) +=09=09=09goto clear_pipe; + +=09=09mem_buf.buf[0].mem =3D mbuf; +=09=09mem_buf.off =3D now_len; +=09=09res =3D fuse_buf_copy(&mem_buf, buf, 0); +=09=09if (res > 0) { +=09=09=09char *tmpbuf; +=09=09=09size_t extra_len =3D res; +=09=09=09/* +=09=09=09 * Trickiest case: got more data. Need to get +=09=09=09 * back the data from the pipe and then fall +=09=09=09 * back to regular write. +=09=09=09 */ +=09=09=09tmpbuf =3D malloc(headerlen); +=09=09=09if (tmpbuf =3D=3D NULL) { +=09=09=09=09free(mbuf); +=09=09=09=09res =3D ENOMEM; +=09=09=09=09goto clear_pipe; +=09=09=09} +=09=09=09res =3D read_back(llp->pipe[0], tmpbuf, headerlen); +=09=09=09free(tmpbuf); +=09=09=09if (res !=3D 0) { +=09=09=09=09free(mbuf); +=09=09=09=09goto clear_pipe; +=09=09=09} +=09=09=09res =3D read_back(llp->pipe[0], mbuf, now_len); +=09=09=09if (res !=3D 0) { +=09=09=09=09free(mbuf); +=09=09=09=09goto clear_pipe; +=09=09=09} +=09=09=09len =3D now_len + extra_len; +=09=09=09iov[iov_count].iov_base =3D mbuf; +=09=09=09iov[iov_count].iov_len =3D len; +=09=09=09iov_count++; +=09=09=09res =3D fuse_send_msg(se, ch, iov, iov_count); +=09=09=09free(mbuf); +=09=09=09return res; +=09=09} +=09=09free(mbuf); +=09=09res =3D now_len; +=09} +=09len =3D res; +=09out->len =3D headerlen + len; + +=09if (se->debug) { +=09=09fuse_log(FUSE_LOG_DEBUG, +=09=09=09" unique: %llu, success, outsize: %i (splice)\n", +=09=09=09(unsigned long long) out->unique, out->len); +=09} + +=09splice_flags =3D 0; +=09if ((flags & FUSE_BUF_SPLICE_MOVE) && +=09 (se->conn.want & FUSE_CAP_SPLICE_MOVE)) +=09=09splice_flags |=3D SPLICE_F_MOVE; + +=09res =3D splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, +=09=09 NULL, out->len, splice_flags); +=09if (res =3D=3D -1) { +=09=09res =3D -errno; +=09=09perror("fuse: splice from pipe"); +=09=09goto clear_pipe; +=09} +=09if (res !=3D out->len) { +=09=09res =3D -EIO; +=09=09fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n", +=09=09=09res, out->len); +=09=09goto clear_pipe; +=09} +=09return 0; + +clear_pipe: +=09fuse_ll_clear_pipe(se); +=09return res; + +fallback: +=09return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); +} +#else +static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *c= h, +=09=09=09 struct iovec *iov, int iov_count, +=09=09=09 struct fuse_bufvec *buf, unsigned int flags) +{ +=09size_t len =3D fuse_buf_size(buf); +=09(void) flags; + +=09return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); +} +#endif + +int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, +=09=09 enum fuse_buf_copy_flags flags) +{ +=09struct iovec iov[2]; +=09struct fuse_out_header out; +=09int res; + +=09iov[0].iov_base =3D &out; +=09iov[0].iov_len =3D sizeof(struct fuse_out_header); + +=09out.unique =3D req->unique; +=09out.error =3D 0; + +=09res =3D fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); +=09if (res <=3D 0) { +=09=09fuse_free_req(req); +=09=09return res; +=09} else { +=09=09return fuse_reply_err(req, res); +=09} +} + +int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) +{ +=09struct fuse_statfs_out arg; +=09size_t size =3D req->se->conn.proto_minor < 4 ? +=09=09FUSE_COMPAT_STATFS_SIZE : sizeof(arg); + +=09memset(&arg, 0, sizeof(arg)); +=09convert_statfs(stbuf, &arg.st); + +=09return send_reply_ok(req, &arg, size); +} + +int fuse_reply_xattr(fuse_req_t req, size_t count) +{ +=09struct fuse_getxattr_out arg; + +=09memset(&arg, 0, sizeof(arg)); +=09arg.size =3D count; + +=09return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_lock(fuse_req_t req, const struct flock *lock) +{ +=09struct fuse_lk_out arg; + +=09memset(&arg, 0, sizeof(arg)); +=09arg.lk.type =3D lock->l_type; +=09if (lock->l_type !=3D F_UNLCK) { +=09=09arg.lk.start =3D lock->l_start; +=09=09if (lock->l_len =3D=3D 0) +=09=09=09arg.lk.end =3D OFFSET_MAX; +=09=09else +=09=09=09arg.lk.end =3D lock->l_start + lock->l_len - 1; +=09} +=09arg.lk.pid =3D lock->l_pid; +=09return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_bmap(fuse_req_t req, uint64_t idx) +{ +=09struct fuse_bmap_out arg; + +=09memset(&arg, 0, sizeof(arg)); +=09arg.block =3D idx; + +=09return send_reply_ok(req, &arg, sizeof(arg)); +} + +static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *= iov, +=09=09=09=09=09=09 size_t count) +{ +=09struct fuse_ioctl_iovec *fiov; +=09size_t i; + +=09fiov =3D malloc(sizeof(fiov[0]) * count); +=09if (!fiov) +=09=09return NULL; + +=09for (i =3D 0; i < count; i++) { +=09=09fiov[i].base =3D (uintptr_t) iov[i].iov_base; +=09=09fiov[i].len =3D iov[i].iov_len; +=09} + +=09return fiov; +} + +int fuse_reply_ioctl_retry(fuse_req_t req, +=09=09=09 const struct iovec *in_iov, size_t in_count, +=09=09=09 const struct iovec *out_iov, size_t out_count) +{ +=09struct fuse_ioctl_out arg; +=09struct fuse_ioctl_iovec *in_fiov =3D NULL; +=09struct fuse_ioctl_iovec *out_fiov =3D NULL; +=09struct iovec iov[4]; +=09size_t count =3D 1; +=09int res; + +=09memset(&arg, 0, sizeof(arg)); +=09arg.flags |=3D FUSE_IOCTL_RETRY; +=09arg.in_iovs =3D in_count; +=09arg.out_iovs =3D out_count; +=09iov[count].iov_base =3D &arg; +=09iov[count].iov_len =3D sizeof(arg); +=09count++; + +=09if (req->se->conn.proto_minor < 16) { +=09=09if (in_count) { +=09=09=09iov[count].iov_base =3D (void *)in_iov; +=09=09=09iov[count].iov_len =3D sizeof(in_iov[0]) * in_count; +=09=09=09count++; +=09=09} + +=09=09if (out_count) { +=09=09=09iov[count].iov_base =3D (void *)out_iov; +=09=09=09iov[count].iov_len =3D sizeof(out_iov[0]) * out_count; +=09=09=09count++; +=09=09} +=09} else { +=09=09/* Can't handle non-compat 64bit ioctls on 32bit */ +=09=09if (sizeof(void *) =3D=3D 4 && req->ioctl_64bit) { +=09=09=09res =3D fuse_reply_err(req, EINVAL); +=09=09=09goto out; +=09=09} + +=09=09if (in_count) { +=09=09=09in_fiov =3D fuse_ioctl_iovec_copy(in_iov, in_count); +=09=09=09if (!in_fiov) +=09=09=09=09goto enomem; + +=09=09=09iov[count].iov_base =3D (void *)in_fiov; +=09=09=09iov[count].iov_len =3D sizeof(in_fiov[0]) * in_count; +=09=09=09count++; +=09=09} +=09=09if (out_count) { +=09=09=09out_fiov =3D fuse_ioctl_iovec_copy(out_iov, out_count); +=09=09=09if (!out_fiov) +=09=09=09=09goto enomem; + +=09=09=09iov[count].iov_base =3D (void *)out_fiov; +=09=09=09iov[count].iov_len =3D sizeof(out_fiov[0]) * out_count; +=09=09=09count++; +=09=09} +=09} + +=09res =3D send_reply_iov(req, 0, iov, count); +out: +=09free(in_fiov); +=09free(out_fiov); + +=09return res; + +enomem: +=09res =3D fuse_reply_err(req, ENOMEM); +=09goto out; +} + +int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t s= ize) +{ +=09struct fuse_ioctl_out arg; +=09struct iovec iov[3]; +=09size_t count =3D 1; + +=09memset(&arg, 0, sizeof(arg)); +=09arg.result =3D result; +=09iov[count].iov_base =3D &arg; +=09iov[count].iov_len =3D sizeof(arg); +=09count++; + +=09if (size) { +=09=09iov[count].iov_base =3D (char *) buf; +=09=09iov[count].iov_len =3D size; +=09=09count++; +=09} + +=09return send_reply_iov(req, 0, iov, count); +} + +int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *i= ov, +=09=09=09 int count) +{ +=09struct iovec *padded_iov; +=09struct fuse_ioctl_out arg; +=09int res; + +=09padded_iov =3D malloc((count + 2) * sizeof(struct iovec)); +=09if (padded_iov =3D=3D NULL) +=09=09return fuse_reply_err(req, ENOMEM); + +=09memset(&arg, 0, sizeof(arg)); +=09arg.result =3D result; +=09padded_iov[1].iov_base =3D &arg; +=09padded_iov[1].iov_len =3D sizeof(arg); + +=09memcpy(&padded_iov[2], iov, count * sizeof(struct iovec)); + +=09res =3D send_reply_iov(req, 0, padded_iov, count + 2); +=09free(padded_iov); + +=09return res; +} + +int fuse_reply_poll(fuse_req_t req, unsigned revents) +{ +=09struct fuse_poll_out arg; + +=09memset(&arg, 0, sizeof(arg)); +=09arg.revents =3D revents; + +=09return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_lseek(fuse_req_t req, off_t off) +{ +=09struct fuse_lseek_out arg; + +=09memset(&arg, 0, sizeof(arg)); +=09arg.offset =3D off; + +=09return send_reply_ok(req, &arg, sizeof(arg)); +} + +static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg= ) +{ +=09char *name =3D (char *) inarg; + +=09if (req->se->op.lookup) +=09=09req->se->op.lookup(req, nodeid, name); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg= ) +{ +=09struct fuse_forget_in *arg =3D (struct fuse_forget_in *) inarg; + +=09if (req->se->op.forget) +=09=09req->se->op.forget(req, nodeid, arg->nlookup); +=09else +=09=09fuse_reply_none(req); +} + +static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, +=09=09=09 const void *inarg) +{ +=09struct fuse_batch_forget_in *arg =3D (void *) inarg; +=09struct fuse_forget_one *param =3D (void *) PARAM(arg); +=09unsigned int i; + +=09(void) nodeid; + +=09if (req->se->op.forget_multi) { +=09=09req->se->op.forget_multi(req, arg->count, +=09=09=09=09 (struct fuse_forget_data *) param); +=09} else if (req->se->op.forget) { +=09=09for (i =3D 0; i < arg->count; i++) { +=09=09=09struct fuse_forget_one *forget =3D ¶m[i]; +=09=09=09struct fuse_req *dummy_req; + +=09=09=09dummy_req =3D fuse_ll_alloc_req(req->se); +=09=09=09if (dummy_req =3D=3D NULL) +=09=09=09=09break; + +=09=09=09dummy_req->unique =3D req->unique; +=09=09=09dummy_req->ctx =3D req->ctx; +=09=09=09dummy_req->ch =3D NULL; + +=09=09=09req->se->op.forget(dummy_req, forget->nodeid, +=09=09=09=09=09 forget->nlookup); +=09=09} +=09=09fuse_reply_none(req); +=09} else { +=09=09fuse_reply_none(req); +=09} +} + +static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inar= g) +{ +=09struct fuse_file_info *fip =3D NULL; +=09struct fuse_file_info fi; + +=09if (req->se->conn.proto_minor >=3D 9) { +=09=09struct fuse_getattr_in *arg =3D (struct fuse_getattr_in *) inarg; + +=09=09if (arg->getattr_flags & FUSE_GETATTR_FH) { +=09=09=09memset(&fi, 0, sizeof(fi)); +=09=09=09fi.fh =3D arg->fh; +=09=09=09fip =3D &fi; +=09=09} +=09} + +=09if (req->se->op.getattr) +=09=09req->se->op.getattr(req, nodeid, fip); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inar= g) +{ +=09struct fuse_setattr_in *arg =3D (struct fuse_setattr_in *) inarg; + +=09if (req->se->op.setattr) { +=09=09struct fuse_file_info *fi =3D NULL; +=09=09struct fuse_file_info fi_store; +=09=09struct stat stbuf; +=09=09memset(&stbuf, 0, sizeof(stbuf)); +=09=09convert_attr(arg, &stbuf); +=09=09if (arg->valid & FATTR_FH) { +=09=09=09arg->valid &=3D ~FATTR_FH; +=09=09=09memset(&fi_store, 0, sizeof(fi_store)); +=09=09=09fi =3D &fi_store; +=09=09=09fi->fh =3D arg->fh; +=09=09} +=09=09arg->valid &=3D +=09=09=09FUSE_SET_ATTR_MODE=09| +=09=09=09FUSE_SET_ATTR_UID=09| +=09=09=09FUSE_SET_ATTR_GID=09| +=09=09=09FUSE_SET_ATTR_SIZE=09| +=09=09=09FUSE_SET_ATTR_ATIME=09| +=09=09=09FUSE_SET_ATTR_MTIME=09| +=09=09=09FUSE_SET_ATTR_ATIME_NOW=09| +=09=09=09FUSE_SET_ATTR_MTIME_NOW | +=09=09=09FUSE_SET_ATTR_CTIME; + +=09=09req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi); +=09} else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg= ) +{ +=09struct fuse_access_in *arg =3D (struct fuse_access_in *) inarg; + +=09if (req->se->op.access) +=09=09req->se->op.access(req, nodeid, arg->mask); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *ina= rg) +{ +=09(void) inarg; + +=09if (req->se->op.readlink) +=09=09req->se->op.readlink(req, nodeid); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_mknod_in *arg =3D (struct fuse_mknod_in *) inarg; +=09char *name =3D PARAM(arg); + +=09if (req->se->conn.proto_minor >=3D 12) +=09=09req->ctx.umask =3D arg->umask; +=09else +=09=09name =3D (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE; + +=09if (req->se->op.mknod) +=09=09req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_mkdir_in *arg =3D (struct fuse_mkdir_in *) inarg; + +=09if (req->se->conn.proto_minor >=3D 12) +=09=09req->ctx.umask =3D arg->umask; + +=09if (req->se->op.mkdir) +=09=09req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg= ) +{ +=09char *name =3D (char *) inarg; + +=09if (req->se->op.unlink) +=09=09req->se->op.unlink(req, nodeid, name); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09char *name =3D (char *) inarg; + +=09if (req->se->op.rmdir) +=09=09req->se->op.rmdir(req, nodeid, name); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inar= g) +{ +=09char *name =3D (char *) inarg; +=09char *linkname =3D ((char *) inarg) + strlen((char *) inarg) + 1; + +=09if (req->se->op.symlink) +=09=09req->se->op.symlink(req, linkname, nodeid, name); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg= ) +{ +=09struct fuse_rename_in *arg =3D (struct fuse_rename_in *) inarg; +=09char *oldname =3D PARAM(arg); +=09char *newname =3D oldname + strlen(oldname) + 1; + +=09if (req->se->op.rename) +=09=09req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, +=09=09=09=09 0); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inar= g) +{ +=09struct fuse_rename2_in *arg =3D (struct fuse_rename2_in *) inarg; +=09char *oldname =3D PARAM(arg); +=09char *newname =3D oldname + strlen(oldname) + 1; + +=09if (req->se->op.rename) +=09=09req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, +=09=09=09=09 arg->flags); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_link_in *arg =3D (struct fuse_link_in *) inarg; + +=09if (req->se->op.link) +=09=09req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg= ) +{ +=09struct fuse_create_in *arg =3D (struct fuse_create_in *) inarg; + +=09if (req->se->op.create) { +=09=09struct fuse_file_info fi; +=09=09char *name =3D PARAM(arg); + +=09=09memset(&fi, 0, sizeof(fi)); +=09=09fi.flags =3D arg->flags; + +=09=09if (req->se->conn.proto_minor >=3D 12) +=09=09=09req->ctx.umask =3D arg->umask; +=09=09else +=09=09=09name =3D (char *) inarg + sizeof(struct fuse_open_in); + +=09=09req->se->op.create(req, nodeid, name, arg->mode, &fi); +=09} else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_open_in *arg =3D (struct fuse_open_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.flags =3D arg->flags; + +=09if (req->se->op.open) +=09=09req->se->op.open(req, nodeid, &fi); +=09else +=09=09fuse_reply_open(req, &fi); +} + +static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_read_in *arg =3D (struct fuse_read_in *) inarg; + +=09if (req->se->op.read) { +=09=09struct fuse_file_info fi; + +=09=09memset(&fi, 0, sizeof(fi)); +=09=09fi.fh =3D arg->fh; +=09=09if (req->se->conn.proto_minor >=3D 9) { +=09=09=09fi.lock_owner =3D arg->lock_owner; +=09=09=09fi.flags =3D arg->flags; +=09=09} +=09=09req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); +=09} else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_write_in *arg =3D (struct fuse_write_in *) inarg; +=09struct fuse_file_info fi; +=09char *param; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; +=09fi.writepage =3D (arg->write_flags & FUSE_WRITE_CACHE) !=3D 0; + +=09if (req->se->conn.proto_minor < 9) { +=09=09param =3D ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; +=09} else { +=09=09fi.lock_owner =3D arg->lock_owner; +=09=09fi.flags =3D arg->flags; +=09=09param =3D PARAM(arg); +=09} + +=09if (req->se->op.write) +=09=09req->se->op.write(req, nodeid, param, arg->size, +=09=09=09=09 arg->offset, &fi); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *in= arg, +=09=09=09 const struct fuse_buf *ibuf) +{ +=09struct fuse_session *se =3D req->se; +=09struct fuse_bufvec bufv =3D { +=09=09.buf[0] =3D *ibuf, +=09=09.count =3D 1, +=09}; +=09struct fuse_write_in *arg =3D (struct fuse_write_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; +=09fi.writepage =3D arg->write_flags & FUSE_WRITE_CACHE; + +=09if (se->conn.proto_minor < 9) { +=09=09bufv.buf[0].mem =3D ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; +=09=09bufv.buf[0].size -=3D sizeof(struct fuse_in_header) + +=09=09=09FUSE_COMPAT_WRITE_IN_SIZE; +=09=09assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); +=09} else { +=09=09fi.lock_owner =3D arg->lock_owner; +=09=09fi.flags =3D arg->flags; +=09=09if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) +=09=09=09bufv.buf[0].mem =3D PARAM(arg); + +=09=09bufv.buf[0].size -=3D sizeof(struct fuse_in_header) + +=09=09=09sizeof(struct fuse_write_in); +=09} +=09if (bufv.buf[0].size < arg->size) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"= ); +=09=09fuse_reply_err(req, EIO); +=09=09goto out; +=09} +=09bufv.buf[0].size =3D arg->size; + +=09se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); + +out: +=09/* Need to reset the pipe if ->write_buf() didn't consume all data */ +=09if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) +=09=09fuse_ll_clear_pipe(se); +} + +static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_flush_in *arg =3D (struct fuse_flush_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; +=09fi.flush =3D 1; +=09if (req->se->conn.proto_minor >=3D 7) +=09=09fi.lock_owner =3D arg->lock_owner; + +=09if (req->se->op.flush) +=09=09req->se->op.flush(req, nodeid, &fi); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inar= g) +{ +=09struct fuse_release_in *arg =3D (struct fuse_release_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.flags =3D arg->flags; +=09fi.fh =3D arg->fh; +=09if (req->se->conn.proto_minor >=3D 8) { +=09=09fi.flush =3D (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; +=09=09fi.lock_owner =3D arg->lock_owner; +=09} +=09if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { +=09=09fi.flock_release =3D 1; +=09=09fi.lock_owner =3D arg->lock_owner; +=09} + +=09if (req->se->op.release) +=09=09req->se->op.release(req, nodeid, &fi); +=09else +=09=09fuse_reply_err(req, 0); +} + +static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_fsync_in *arg =3D (struct fuse_fsync_in *) inarg; +=09struct fuse_file_info fi; +=09int datasync =3D arg->fsync_flags & 1; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; + +=09if (req->se->op.fsync) +=09=09req->se->op.fsync(req, nodeid, datasync, &fi); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inar= g) +{ +=09struct fuse_open_in *arg =3D (struct fuse_open_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.flags =3D arg->flags; + +=09if (req->se->op.opendir) +=09=09req->se->op.opendir(req, nodeid, &fi); +=09else +=09=09fuse_reply_open(req, &fi); +} + +static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inar= g) +{ +=09struct fuse_read_in *arg =3D (struct fuse_read_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; + +=09if (req->se->op.readdir) +=09=09req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *= inarg) +{ +=09struct fuse_read_in *arg =3D (struct fuse_read_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; + +=09if (req->se->op.readdirplus) +=09=09req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *i= narg) +{ +=09struct fuse_release_in *arg =3D (struct fuse_release_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.flags =3D arg->flags; +=09fi.fh =3D arg->fh; + +=09if (req->se->op.releasedir) +=09=09req->se->op.releasedir(req, nodeid, &fi); +=09else +=09=09fuse_reply_err(req, 0); +} + +static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *ina= rg) +{ +=09struct fuse_fsync_in *arg =3D (struct fuse_fsync_in *) inarg; +=09struct fuse_file_info fi; +=09int datasync =3D arg->fsync_flags & 1; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; + +=09if (req->se->op.fsyncdir) +=09=09req->se->op.fsyncdir(req, nodeid, datasync, &fi); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg= ) +{ +=09(void) nodeid; +=09(void) inarg; + +=09if (req->se->op.statfs) +=09=09req->se->op.statfs(req, nodeid); +=09else { +=09=09struct statvfs buf =3D { +=09=09=09.f_namemax =3D 255, +=09=09=09.f_bsize =3D 512, +=09=09}; +=09=09fuse_reply_statfs(req, &buf); +=09} +} + +static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *ina= rg) +{ +=09struct fuse_setxattr_in *arg =3D (struct fuse_setxattr_in *) inarg; +=09char *name =3D PARAM(arg); +=09char *value =3D name + strlen(name) + 1; + +=09if (req->se->op.setxattr) +=09=09req->se->op.setxattr(req, nodeid, name, value, arg->size, +=09=09=09=09 arg->flags); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *ina= rg) +{ +=09struct fuse_getxattr_in *arg =3D (struct fuse_getxattr_in *) inarg; + +=09if (req->se->op.getxattr) +=09=09req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *in= arg) +{ +=09struct fuse_getxattr_in *arg =3D (struct fuse_getxattr_in *) inarg; + +=09if (req->se->op.listxattr) +=09=09req->se->op.listxattr(req, nodeid, arg->size); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *= inarg) +{ +=09char *name =3D (char *) inarg; + +=09if (req->se->op.removexattr) +=09=09req->se->op.removexattr(req, nodeid, name); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void convert_fuse_file_lock(struct fuse_file_lock *fl, +=09=09=09=09 struct flock *flock) +{ +=09memset(flock, 0, sizeof(struct flock)); +=09flock->l_type =3D fl->type; +=09flock->l_whence =3D SEEK_SET; +=09flock->l_start =3D fl->start; +=09if (fl->end =3D=3D OFFSET_MAX) +=09=09flock->l_len =3D 0; +=09else +=09=09flock->l_len =3D fl->end - fl->start + 1; +=09flock->l_pid =3D fl->pid; +} + +static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_lk_in *arg =3D (struct fuse_lk_in *) inarg; +=09struct fuse_file_info fi; +=09struct flock flock; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; +=09fi.lock_owner =3D arg->owner; + +=09convert_fuse_file_lock(&arg->lk, &flock); +=09if (req->se->op.getlk) +=09=09req->se->op.getlk(req, nodeid, &fi, &flock); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, +=09=09=09 const void *inarg, int sleep) +{ +=09struct fuse_lk_in *arg =3D (struct fuse_lk_in *) inarg; +=09struct fuse_file_info fi; +=09struct flock flock; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; +=09fi.lock_owner =3D arg->owner; + +=09if (arg->lk_flags & FUSE_LK_FLOCK) { +=09=09int op =3D 0; + +=09=09switch (arg->lk.type) { +=09=09case F_RDLCK: +=09=09=09op =3D LOCK_SH; +=09=09=09break; +=09=09case F_WRLCK: +=09=09=09op =3D LOCK_EX; +=09=09=09break; +=09=09case F_UNLCK: +=09=09=09op =3D LOCK_UN; +=09=09=09break; +=09=09} +=09=09if (!sleep) +=09=09=09op |=3D LOCK_NB; + +=09=09if (req->se->op.flock) +=09=09=09req->se->op.flock(req, nodeid, &fi, op); +=09=09else +=09=09=09fuse_reply_err(req, ENOSYS); +=09} else { +=09=09convert_fuse_file_lock(&arg->lk, &flock); +=09=09if (req->se->op.setlk) +=09=09=09req->se->op.setlk(req, nodeid, &fi, &flock, sleep); +=09=09else +=09=09=09fuse_reply_err(req, ENOSYS); +=09} +} + +static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09do_setlk_common(req, nodeid, inarg, 0); +} + +static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg= ) +{ +=09do_setlk_common(req, nodeid, inarg, 1); +} + +static int find_interrupted(struct fuse_session *se, struct fuse_req *req) +{ +=09struct fuse_req *curr; + +=09for (curr =3D se->list.next; curr !=3D &se->list; curr =3D curr->next) = { +=09=09if (curr->unique =3D=3D req->u.i.unique) { +=09=09=09fuse_interrupt_func_t func; +=09=09=09void *data; + +=09=09=09curr->ctr++; +=09=09=09pthread_mutex_unlock(&se->lock); + +=09=09=09/* Ugh, ugly locking */ +=09=09=09pthread_mutex_lock(&curr->lock); +=09=09=09pthread_mutex_lock(&se->lock); +=09=09=09curr->interrupted =3D 1; +=09=09=09func =3D curr->u.ni.func; +=09=09=09data =3D curr->u.ni.data; +=09=09=09pthread_mutex_unlock(&se->lock); +=09=09=09if (func) +=09=09=09=09func(curr, data); +=09=09=09pthread_mutex_unlock(&curr->lock); + +=09=09=09pthread_mutex_lock(&se->lock); +=09=09=09curr->ctr--; +=09=09=09if (!curr->ctr) +=09=09=09=09destroy_req(curr); + +=09=09=09return 1; +=09=09} +=09} +=09for (curr =3D se->interrupts.next; curr !=3D &se->interrupts; +=09 curr =3D curr->next) { +=09=09if (curr->u.i.unique =3D=3D req->u.i.unique) +=09=09=09return 1; +=09} +=09return 0; +} + +static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *in= arg) +{ +=09struct fuse_interrupt_in *arg =3D (struct fuse_interrupt_in *) inarg; +=09struct fuse_session *se =3D req->se; + +=09(void) nodeid; +=09if (se->debug) +=09=09fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", +=09=09=09(unsigned long long) arg->unique); + +=09req->u.i.unique =3D arg->unique; + +=09pthread_mutex_lock(&se->lock); +=09if (find_interrupted(se, req)) +=09=09destroy_req(req); +=09else +=09=09list_add_req(req, &se->interrupts); +=09pthread_mutex_unlock(&se->lock); +} + +static struct fuse_req *check_interrupt(struct fuse_session *se, +=09=09=09=09=09struct fuse_req *req) +{ +=09struct fuse_req *curr; + +=09for (curr =3D se->interrupts.next; curr !=3D &se->interrupts; +=09 curr =3D curr->next) { +=09=09if (curr->u.i.unique =3D=3D req->unique) { +=09=09=09req->interrupted =3D 1; +=09=09=09list_del_req(curr); +=09=09=09free(curr); +=09=09=09return NULL; +=09=09} +=09} +=09curr =3D se->interrupts.next; +=09if (curr !=3D &se->interrupts) { +=09=09list_del_req(curr); +=09=09list_init_req(curr); +=09=09return curr; +=09} else +=09=09return NULL; +} + +static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_bmap_in *arg =3D (struct fuse_bmap_in *) inarg; + +=09if (req->se->op.bmap) +=09=09req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_ioctl_in *arg =3D (struct fuse_ioctl_in *) inarg; +=09unsigned int flags =3D arg->flags; +=09void *in_buf =3D arg->in_size ? PARAM(arg) : NULL; +=09struct fuse_file_info fi; + +=09if (flags & FUSE_IOCTL_DIR && +=09 !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { +=09=09fuse_reply_err(req, ENOTTY); +=09=09return; +=09} + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; + +=09if (sizeof(void *) =3D=3D 4 && req->se->conn.proto_minor >=3D 16 && +=09 !(flags & FUSE_IOCTL_32BIT)) { +=09=09req->ioctl_64bit =3D 1; +=09} + +=09if (req->se->op.ioctl) +=09=09req->se->op.ioctl(req, nodeid, arg->cmd, +=09=09=09=09 (void *)(uintptr_t)arg->arg, &fi, flags, +=09=09=09=09 in_buf, arg->in_size, arg->out_size); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) +{ +=09free(ph); +} + +static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_poll_in *arg =3D (struct fuse_poll_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; +=09fi.poll_events =3D arg->events; + +=09if (req->se->op.poll) { +=09=09struct fuse_pollhandle *ph =3D NULL; + +=09=09if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) { +=09=09=09ph =3D malloc(sizeof(struct fuse_pollhandle)); +=09=09=09if (ph =3D=3D NULL) { +=09=09=09=09fuse_reply_err(req, ENOMEM); +=09=09=09=09return; +=09=09=09} +=09=09=09ph->kh =3D arg->kh; +=09=09=09ph->se =3D req->se; +=09=09} + +=09=09req->se->op.poll(req, nodeid, &fi, ph); +=09} else { +=09=09fuse_reply_err(req, ENOSYS); +=09} +} + +static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *in= arg) +{ +=09struct fuse_fallocate_in *arg =3D (struct fuse_fallocate_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; + +=09if (req->se->op.fallocate) +=09=09req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->leng= th, &fi); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const= void *inarg) +{ +=09struct fuse_copy_file_range_in *arg =3D (struct fuse_copy_file_range_in= *) inarg; +=09struct fuse_file_info fi_in, fi_out; + +=09memset(&fi_in, 0, sizeof(fi_in)); +=09fi_in.fh =3D arg->fh_in; + +=09memset(&fi_out, 0, sizeof(fi_out)); +=09fi_out.fh =3D arg->fh_out; + + +=09if (req->se->op.copy_file_range) +=09=09req->se->op.copy_file_range(req, nodeid_in, arg->off_in, +=09=09=09=09=09 &fi_in, arg->nodeid_out, +=09=09=09=09=09 arg->off_out, &fi_out, arg->len, +=09=09=09=09=09 arg->flags); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_lseek_in *arg =3D (struct fuse_lseek_in *) inarg; +=09struct fuse_file_info fi; + +=09memset(&fi, 0, sizeof(fi)); +=09fi.fh =3D arg->fh; + +=09if (req->se->op.lseek) +=09=09req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi); +=09else +=09=09fuse_reply_err(req, ENOSYS); +} + +static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ +=09struct fuse_init_in *arg =3D (struct fuse_init_in *) inarg; +=09struct fuse_init_out outarg; +=09struct fuse_session *se =3D req->se; +=09size_t bufsize =3D se->bufsize; +=09size_t outargsize =3D sizeof(outarg); + +=09(void) nodeid; +=09if (se->debug) { +=09=09fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); +=09=09if (arg->major =3D=3D 7 && arg->minor >=3D 6) { +=09=09=09fuse_log(FUSE_LOG_DEBUG, "flags=3D0x%08x\n", arg->flags); +=09=09=09fuse_log(FUSE_LOG_DEBUG, "max_readahead=3D0x%08x\n", +=09=09=09=09arg->max_readahead); +=09=09} +=09} +=09se->conn.proto_major =3D arg->major; +=09se->conn.proto_minor =3D arg->minor; +=09se->conn.capable =3D 0; +=09se->conn.want =3D 0; + +=09memset(&outarg, 0, sizeof(outarg)); +=09outarg.major =3D FUSE_KERNEL_VERSION; +=09outarg.minor =3D FUSE_KERNEL_MINOR_VERSION; + +=09if (arg->major < 7) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n"= , +=09=09=09arg->major, arg->minor); +=09=09fuse_reply_err(req, EPROTO); +=09=09return; +=09} + +=09if (arg->major > 7) { +=09=09/* Wait for a second INIT request with a 7.X version */ +=09=09send_reply_ok(req, &outarg, sizeof(outarg)); +=09=09return; +=09} + +=09if (arg->minor >=3D 6) { +=09=09if (arg->max_readahead < se->conn.max_readahead) +=09=09=09se->conn.max_readahead =3D arg->max_readahead; +=09=09if (arg->flags & FUSE_ASYNC_READ) +=09=09=09se->conn.capable |=3D FUSE_CAP_ASYNC_READ; +=09=09if (arg->flags & FUSE_POSIX_LOCKS) +=09=09=09se->conn.capable |=3D FUSE_CAP_POSIX_LOCKS; +=09=09if (arg->flags & FUSE_ATOMIC_O_TRUNC) +=09=09=09se->conn.capable |=3D FUSE_CAP_ATOMIC_O_TRUNC; +=09=09if (arg->flags & FUSE_EXPORT_SUPPORT) +=09=09=09se->conn.capable |=3D FUSE_CAP_EXPORT_SUPPORT; +=09=09if (arg->flags & FUSE_DONT_MASK) +=09=09=09se->conn.capable |=3D FUSE_CAP_DONT_MASK; +=09=09if (arg->flags & FUSE_FLOCK_LOCKS) +=09=09=09se->conn.capable |=3D FUSE_CAP_FLOCK_LOCKS; +=09=09if (arg->flags & FUSE_AUTO_INVAL_DATA) +=09=09=09se->conn.capable |=3D FUSE_CAP_AUTO_INVAL_DATA; +=09=09if (arg->flags & FUSE_DO_READDIRPLUS) +=09=09=09se->conn.capable |=3D FUSE_CAP_READDIRPLUS; +=09=09if (arg->flags & FUSE_READDIRPLUS_AUTO) +=09=09=09se->conn.capable |=3D FUSE_CAP_READDIRPLUS_AUTO; +=09=09if (arg->flags & FUSE_ASYNC_DIO) +=09=09=09se->conn.capable |=3D FUSE_CAP_ASYNC_DIO; +=09=09if (arg->flags & FUSE_WRITEBACK_CACHE) +=09=09=09se->conn.capable |=3D FUSE_CAP_WRITEBACK_CACHE; +=09=09if (arg->flags & FUSE_NO_OPEN_SUPPORT) +=09=09=09se->conn.capable |=3D FUSE_CAP_NO_OPEN_SUPPORT; +=09=09if (arg->flags & FUSE_PARALLEL_DIROPS) +=09=09=09se->conn.capable |=3D FUSE_CAP_PARALLEL_DIROPS; +=09=09if (arg->flags & FUSE_POSIX_ACL) +=09=09=09se->conn.capable |=3D FUSE_CAP_POSIX_ACL; +=09=09if (arg->flags & FUSE_HANDLE_KILLPRIV) +=09=09=09se->conn.capable |=3D FUSE_CAP_HANDLE_KILLPRIV; +=09=09if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) +=09=09=09se->conn.capable |=3D FUSE_CAP_NO_OPENDIR_SUPPORT; +=09=09if (!(arg->flags & FUSE_MAX_PAGES)) { +=09=09=09size_t max_bufsize =3D +=09=09=09=09FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() +=09=09=09=09+ FUSE_BUFFER_HEADER_SIZE; +=09=09=09if (bufsize > max_bufsize) { +=09=09=09=09bufsize =3D max_bufsize; +=09=09=09} +=09=09} +=09} else { +=09=09se->conn.max_readahead =3D 0; +=09} + +=09if (se->conn.proto_minor >=3D 14) { +#ifdef HAVE_SPLICE +#ifdef HAVE_VMSPLICE +=09=09se->conn.capable |=3D FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; +#endif +=09=09se->conn.capable |=3D FUSE_CAP_SPLICE_READ; +#endif +=09} +=09if (se->conn.proto_minor >=3D 18) +=09=09se->conn.capable |=3D FUSE_CAP_IOCTL_DIR; + +=09/* Default settings for modern filesystems. +=09 * +=09 * Most of these capabilities were disabled by default in +=09 * libfuse2 for backwards compatibility reasons. In libfuse3, +=09 * we can finally enable them by default (as long as they're +=09 * supported by the kernel). +=09 */ +#define LL_SET_DEFAULT(cond, cap) \ +=09if ((cond) && (se->conn.capable & (cap))) \ +=09=09se->conn.want |=3D (cap) +=09LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); +=09LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS); +=09LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); +=09LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV); +=09LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); +=09LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR); +=09LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC); +=09LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ); +=09LL_SET_DEFAULT(se->op.getlk && se->op.setlk, +=09=09 FUSE_CAP_POSIX_LOCKS); +=09LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS); +=09LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS); +=09LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir, +=09=09 FUSE_CAP_READDIRPLUS_AUTO); +=09se->conn.time_gran =3D 1; +=09 +=09if (bufsize < FUSE_MIN_READ_BUFFER) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n"= , +=09=09=09bufsize); +=09=09bufsize =3D FUSE_MIN_READ_BUFFER; +=09} +=09se->bufsize =3D bufsize; + +=09if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE) +=09=09se->conn.max_write =3D bufsize - FUSE_BUFFER_HEADER_SIZE; + +=09se->got_init =3D 1; +=09if (se->op.init) +=09=09se->op.init(se->userdata, &se->conn); + +=09if (se->conn.want & (~se->conn.capable)) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: error: filesystem requested capabiliti= es " +=09=09=09"0x%x that are not supported by kernel, aborting.\n", +=09=09=09se->conn.want & (~se->conn.capable)); +=09=09fuse_reply_err(req, EPROTO); +=09=09se->error =3D -EPROTO; +=09=09fuse_session_exit(se); +=09=09return; +=09} + +=09unsigned max_read_mo =3D get_max_read(se->mo); +=09if (se->conn.max_read !=3D max_read_mo) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() " +=09=09=09"requested different maximum read size (%u vs %u)\n", +=09=09=09se->conn.max_read, max_read_mo); +=09=09fuse_reply_err(req, EPROTO); +=09=09se->error =3D -EPROTO; +=09=09fuse_session_exit(se); +=09=09return; +=09} + +=09if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { +=09=09se->bufsize =3D se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; +=09} +=09if (arg->flags & FUSE_MAX_PAGES) { +=09=09outarg.flags |=3D FUSE_MAX_PAGES; +=09=09outarg.max_pages =3D (se->conn.max_write - 1) / getpagesize() + 1; +=09} + +=09/* Always enable big writes, this is superseded +=09 by the max_write option */ +=09outarg.flags |=3D FUSE_BIG_WRITES; + +=09if (se->conn.want & FUSE_CAP_ASYNC_READ) +=09=09outarg.flags |=3D FUSE_ASYNC_READ; +=09if (se->conn.want & FUSE_CAP_POSIX_LOCKS) +=09=09outarg.flags |=3D FUSE_POSIX_LOCKS; +=09if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) +=09=09outarg.flags |=3D FUSE_ATOMIC_O_TRUNC; +=09if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) +=09=09outarg.flags |=3D FUSE_EXPORT_SUPPORT; +=09if (se->conn.want & FUSE_CAP_DONT_MASK) +=09=09outarg.flags |=3D FUSE_DONT_MASK; +=09if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) +=09=09outarg.flags |=3D FUSE_FLOCK_LOCKS; +=09if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) +=09=09outarg.flags |=3D FUSE_AUTO_INVAL_DATA; +=09if (se->conn.want & FUSE_CAP_READDIRPLUS) +=09=09outarg.flags |=3D FUSE_DO_READDIRPLUS; +=09if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) +=09=09outarg.flags |=3D FUSE_READDIRPLUS_AUTO; +=09if (se->conn.want & FUSE_CAP_ASYNC_DIO) +=09=09outarg.flags |=3D FUSE_ASYNC_DIO; +=09if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) +=09=09outarg.flags |=3D FUSE_WRITEBACK_CACHE; +=09if (se->conn.want & FUSE_CAP_POSIX_ACL) +=09=09outarg.flags |=3D FUSE_POSIX_ACL; +=09outarg.max_readahead =3D se->conn.max_readahead; +=09outarg.max_write =3D se->conn.max_write; +=09if (se->conn.proto_minor >=3D 13) { +=09=09if (se->conn.max_background >=3D (1 << 16)) +=09=09=09se->conn.max_background =3D (1 << 16) - 1; +=09=09if (se->conn.congestion_threshold > se->conn.max_background) +=09=09=09se->conn.congestion_threshold =3D se->conn.max_background; +=09=09if (!se->conn.congestion_threshold) { +=09=09=09se->conn.congestion_threshold =3D +=09=09=09=09se->conn.max_background * 3 / 4; +=09=09} + +=09=09outarg.max_background =3D se->conn.max_background; +=09=09outarg.congestion_threshold =3D se->conn.congestion_threshold; +=09} +=09if (se->conn.proto_minor >=3D 23) +=09=09outarg.time_gran =3D se->conn.time_gran; + +=09if (se->debug) { +=09=09fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.mi= nor); +=09=09fuse_log(FUSE_LOG_DEBUG, " flags=3D0x%08x\n", outarg.flags); +=09=09fuse_log(FUSE_LOG_DEBUG, " max_readahead=3D0x%08x\n", +=09=09=09outarg.max_readahead); +=09=09fuse_log(FUSE_LOG_DEBUG, " max_write=3D0x%08x\n", outarg.max_write= ); +=09=09fuse_log(FUSE_LOG_DEBUG, " max_background=3D%i\n", +=09=09=09outarg.max_background); +=09=09fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=3D%i\n", +=09=09=09outarg.congestion_threshold); +=09=09fuse_log(FUSE_LOG_DEBUG, " time_gran=3D%u\n", +=09=09=09outarg.time_gran); +=09} +=09if (arg->minor < 5) +=09=09outargsize =3D FUSE_COMPAT_INIT_OUT_SIZE; +=09else if (arg->minor < 23) +=09=09outargsize =3D FUSE_COMPAT_22_INIT_OUT_SIZE; + +=09send_reply_ok(req, &outarg, outargsize); +} + +static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inar= g) +{ +=09struct fuse_session *se =3D req->se; + +=09(void) nodeid; +=09(void) inarg; + +=09se->got_destroy =3D 1; +=09if (se->op.destroy) +=09=09se->op.destroy(se->userdata); + +=09send_reply_ok(req, NULL, 0); +} + +static void list_del_nreq(struct fuse_notify_req *nreq) +{ +=09struct fuse_notify_req *prev =3D nreq->prev; +=09struct fuse_notify_req *next =3D nreq->next; +=09prev->next =3D next; +=09next->prev =3D prev; +} + +static void list_add_nreq(struct fuse_notify_req *nreq, +=09=09=09 struct fuse_notify_req *next) +{ +=09struct fuse_notify_req *prev =3D next->prev; +=09nreq->next =3D next; +=09nreq->prev =3D prev; +=09prev->next =3D nreq; +=09next->prev =3D nreq; +} + +static void list_init_nreq(struct fuse_notify_req *nreq) +{ +=09nreq->next =3D nreq; +=09nreq->prev =3D nreq; +} + +static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, +=09=09=09 const void *inarg, const struct fuse_buf *buf) +{ +=09struct fuse_session *se =3D req->se; +=09struct fuse_notify_req *nreq; +=09struct fuse_notify_req *head; + +=09pthread_mutex_lock(&se->lock); +=09head =3D &se->notify_list; +=09for (nreq =3D head->next; nreq !=3D head; nreq =3D nreq->next) { +=09=09if (nreq->unique =3D=3D req->unique) { +=09=09=09list_del_nreq(nreq); +=09=09=09break; +=09=09} +=09} +=09pthread_mutex_unlock(&se->lock); + +=09if (nreq !=3D head) +=09=09nreq->reply(nreq, req, nodeid, inarg, buf); +} + +static int send_notify_iov(struct fuse_session *se, int notify_code, +=09=09=09 struct iovec *iov, int count) +{ +=09struct fuse_out_header out; + +=09if (!se->got_init) +=09=09return -ENOTCONN; + +=09out.unique =3D 0; +=09out.error =3D notify_code; +=09iov[0].iov_base =3D &out; +=09iov[0].iov_len =3D sizeof(struct fuse_out_header); + +=09return fuse_send_msg(se, NULL, iov, count); +} + +int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) +{ +=09if (ph !=3D NULL) { +=09=09struct fuse_notify_poll_wakeup_out outarg; +=09=09struct iovec iov[2]; + +=09=09outarg.kh =3D ph->kh; + +=09=09iov[1].iov_base =3D &outarg; +=09=09iov[1].iov_len =3D sizeof(outarg); + +=09=09return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); +=09} else { +=09=09return 0; +=09} +} + +int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t i= no, +=09=09=09=09 off_t off, off_t len) +{ +=09struct fuse_notify_inval_inode_out outarg; +=09struct iovec iov[2]; + +=09if (!se) +=09=09return -EINVAL; + +=09if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) +=09=09return -ENOSYS; +=09 +=09outarg.ino =3D ino; +=09outarg.off =3D off; +=09outarg.len =3D len; + +=09iov[1].iov_base =3D &outarg; +=09iov[1].iov_len =3D sizeof(outarg); + +=09return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); +} + +int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t p= arent, +=09=09=09=09 const char *name, size_t namelen) +{ +=09struct fuse_notify_inval_entry_out outarg; +=09struct iovec iov[3]; + +=09if (!se) +=09=09return -EINVAL; +=09 +=09if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) +=09=09return -ENOSYS; + +=09outarg.parent =3D parent; +=09outarg.namelen =3D namelen; +=09outarg.padding =3D 0; + +=09iov[1].iov_base =3D &outarg; +=09iov[1].iov_len =3D sizeof(outarg); +=09iov[2].iov_base =3D (void *)name; +=09iov[2].iov_len =3D namelen + 1; + +=09return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); +} + +int fuse_lowlevel_notify_delete(struct fuse_session *se, +=09=09=09=09fuse_ino_t parent, fuse_ino_t child, +=09=09=09=09const char *name, size_t namelen) +{ +=09struct fuse_notify_delete_out outarg; +=09struct iovec iov[3]; + +=09if (!se) +=09=09return -EINVAL; + +=09if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) +=09=09return -ENOSYS; + +=09outarg.parent =3D parent; +=09outarg.child =3D child; +=09outarg.namelen =3D namelen; +=09outarg.padding =3D 0; + +=09iov[1].iov_base =3D &outarg; +=09iov[1].iov_len =3D sizeof(outarg); +=09iov[2].iov_base =3D (void *)name; +=09iov[2].iov_len =3D namelen + 1; + +=09return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); +} + +int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, +=09=09=09 off_t offset, struct fuse_bufvec *bufv, +=09=09=09 enum fuse_buf_copy_flags flags) +{ +=09struct fuse_out_header out; +=09struct fuse_notify_store_out outarg; +=09struct iovec iov[3]; +=09size_t size =3D fuse_buf_size(bufv); +=09int res; + +=09if (!se) +=09=09return -EINVAL; + +=09if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) +=09=09return -ENOSYS; + +=09out.unique =3D 0; +=09out.error =3D FUSE_NOTIFY_STORE; + +=09outarg.nodeid =3D ino; +=09outarg.offset =3D offset; +=09outarg.size =3D size; +=09outarg.padding =3D 0; + +=09iov[0].iov_base =3D &out; +=09iov[0].iov_len =3D sizeof(out); +=09iov[1].iov_base =3D &outarg; +=09iov[1].iov_len =3D sizeof(outarg); + +=09res =3D fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); +=09if (res > 0) +=09=09res =3D -res; + +=09return res; +} + +struct fuse_retrieve_req { +=09struct fuse_notify_req nreq; +=09void *cookie; +}; + +static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, +=09=09=09=09 fuse_req_t req, fuse_ino_t ino, +=09=09=09=09 const void *inarg, +=09=09=09=09 const struct fuse_buf *ibuf) +{ +=09struct fuse_session *se =3D req->se; +=09struct fuse_retrieve_req *rreq =3D +=09=09container_of(nreq, struct fuse_retrieve_req, nreq); +=09const struct fuse_notify_retrieve_in *arg =3D inarg; +=09struct fuse_bufvec bufv =3D { +=09=09.buf[0] =3D *ibuf, +=09=09.count =3D 1, +=09}; + +=09if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) +=09=09bufv.buf[0].mem =3D PARAM(arg); + +=09bufv.buf[0].size -=3D sizeof(struct fuse_in_header) + +=09=09sizeof(struct fuse_notify_retrieve_in); + +=09if (bufv.buf[0].size < arg->size) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\= n"); +=09=09fuse_reply_none(req); +=09=09goto out; +=09} +=09bufv.buf[0].size =3D arg->size; + +=09if (se->op.retrieve_reply) { +=09=09se->op.retrieve_reply(req, rreq->cookie, ino, +=09=09=09=09=09 arg->offset, &bufv); +=09} else { +=09=09fuse_reply_none(req); +=09} +out: +=09free(rreq); +=09if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) +=09=09fuse_ll_clear_pipe(se); +} + +int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, +=09=09=09=09 size_t size, off_t offset, void *cookie) +{ +=09struct fuse_notify_retrieve_out outarg; +=09struct iovec iov[2]; +=09struct fuse_retrieve_req *rreq; +=09int err; + +=09if (!se) +=09=09return -EINVAL; + +=09if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) +=09=09return -ENOSYS; + +=09rreq =3D malloc(sizeof(*rreq)); +=09if (rreq =3D=3D NULL) +=09=09return -ENOMEM; + +=09pthread_mutex_lock(&se->lock); +=09rreq->cookie =3D cookie; +=09rreq->nreq.unique =3D se->notify_ctr++; +=09rreq->nreq.reply =3D fuse_ll_retrieve_reply; +=09list_add_nreq(&rreq->nreq, &se->notify_list); +=09pthread_mutex_unlock(&se->lock); + +=09outarg.notify_unique =3D rreq->nreq.unique; +=09outarg.nodeid =3D ino; +=09outarg.offset =3D offset; +=09outarg.size =3D size; +=09outarg.padding =3D 0; + +=09iov[1].iov_base =3D &outarg; +=09iov[1].iov_len =3D sizeof(outarg); + +=09err =3D send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); +=09if (err) { +=09=09pthread_mutex_lock(&se->lock); +=09=09list_del_nreq(&rreq->nreq); +=09=09pthread_mutex_unlock(&se->lock); +=09=09free(rreq); +=09} + +=09return err; +} + +void *fuse_req_userdata(fuse_req_t req) +{ +=09return req->se->userdata; +} + +const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) +{ +=09return &req->ctx; +} + +void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, +=09=09=09 void *data) +{ +=09pthread_mutex_lock(&req->lock); +=09pthread_mutex_lock(&req->se->lock); +=09req->u.ni.func =3D func; +=09req->u.ni.data =3D data; +=09pthread_mutex_unlock(&req->se->lock); +=09if (req->interrupted && func) +=09=09func(req, data); +=09pthread_mutex_unlock(&req->lock); +} + +int fuse_req_interrupted(fuse_req_t req) +{ +=09int interrupted; + +=09pthread_mutex_lock(&req->se->lock); +=09interrupted =3D req->interrupted; +=09pthread_mutex_unlock(&req->se->lock); + +=09return interrupted; +} + +static struct { +=09void (*func)(fuse_req_t, fuse_ino_t, const void *); +=09const char *name; +} fuse_ll_ops[] =3D { +=09[FUSE_LOOKUP]=09 =3D { do_lookup, "LOOKUP"=09 }, +=09[FUSE_FORGET]=09 =3D { do_forget, "FORGET"=09 }, +=09[FUSE_GETATTR]=09 =3D { do_getattr, "GETATTR" }, +=09[FUSE_SETATTR]=09 =3D { do_setattr, "SETATTR" }, +=09[FUSE_READLINK]=09 =3D { do_readlink, "READLINK" }, +=09[FUSE_SYMLINK]=09 =3D { do_symlink, "SYMLINK" }, +=09[FUSE_MKNOD]=09 =3D { do_mknod, "MKNOD"=09 }, +=09[FUSE_MKDIR]=09 =3D { do_mkdir, "MKDIR"=09 }, +=09[FUSE_UNLINK]=09 =3D { do_unlink, "UNLINK"=09 }, +=09[FUSE_RMDIR]=09 =3D { do_rmdir, "RMDIR"=09 }, +=09[FUSE_RENAME]=09 =3D { do_rename, "RENAME"=09 }, +=09[FUSE_LINK]=09 =3D { do_link,=09 "LINK"=09 }, +=09[FUSE_OPEN]=09 =3D { do_open,=09 "OPEN"=09 }, +=09[FUSE_READ]=09 =3D { do_read,=09 "READ"=09 }, +=09[FUSE_WRITE]=09 =3D { do_write, "WRITE"=09 }, +=09[FUSE_STATFS]=09 =3D { do_statfs, "STATFS"=09 }, +=09[FUSE_RELEASE]=09 =3D { do_release, "RELEASE" }, +=09[FUSE_FSYNC]=09 =3D { do_fsync, "FSYNC"=09 }, +=09[FUSE_SETXATTR]=09 =3D { do_setxattr, "SETXATTR" }, +=09[FUSE_GETXATTR]=09 =3D { do_getxattr, "GETXATTR" }, +=09[FUSE_LISTXATTR] =3D { do_listxattr, "LISTXATTR" }, +=09[FUSE_REMOVEXATTR] =3D { do_removexattr, "REMOVEXATTR" }, +=09[FUSE_FLUSH]=09 =3D { do_flush, "FLUSH"=09 }, +=09[FUSE_INIT]=09 =3D { do_init,=09 "INIT"=09 }, +=09[FUSE_OPENDIR]=09 =3D { do_opendir, "OPENDIR" }, +=09[FUSE_READDIR]=09 =3D { do_readdir, "READDIR" }, +=09[FUSE_RELEASEDIR] =3D { do_releasedir, "RELEASEDIR" }, +=09[FUSE_FSYNCDIR]=09 =3D { do_fsyncdir, "FSYNCDIR" }, +=09[FUSE_GETLK]=09 =3D { do_getlk, "GETLK"=09 }, +=09[FUSE_SETLK]=09 =3D { do_setlk, "SETLK"=09 }, +=09[FUSE_SETLKW]=09 =3D { do_setlkw, "SETLKW"=09 }, +=09[FUSE_ACCESS]=09 =3D { do_access, "ACCESS"=09 }, +=09[FUSE_CREATE]=09 =3D { do_create, "CREATE"=09 }, +=09[FUSE_INTERRUPT] =3D { do_interrupt, "INTERRUPT" }, +=09[FUSE_BMAP]=09 =3D { do_bmap,=09 "BMAP"=09 }, +=09[FUSE_IOCTL]=09 =3D { do_ioctl, "IOCTL"=09 }, +=09[FUSE_POLL]=09 =3D { do_poll, "POLL"=09 }, +=09[FUSE_FALLOCATE] =3D { do_fallocate, "FALLOCATE" }, +=09[FUSE_DESTROY]=09 =3D { do_destroy, "DESTROY" }, +=09[FUSE_NOTIFY_REPLY] =3D { (void *) 1, "NOTIFY_REPLY" }, +=09[FUSE_BATCH_FORGET] =3D { do_batch_forget, "BATCH_FORGET" }, +=09[FUSE_READDIRPLUS] =3D { do_readdirplus,=09"READDIRPLUS"}, +=09[FUSE_RENAME2] =3D { do_rename2, "RENAME2" }, +=09[FUSE_COPY_FILE_RANGE] =3D { do_copy_file_range, "COPY_FILE_RANGE" }, +=09[FUSE_LSEEK]=09 =3D { do_lseek, "LSEEK"=09 }, +=09[CUSE_INIT]=09 =3D { cuse_lowlevel_init, "CUSE_INIT" }, +}; + +#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) + +static const char *opname(enum fuse_opcode opcode) +{ +=09if (opcode >=3D FUSE_MAXOP || !fuse_ll_ops[opcode].name) +=09=09return "???"; +=09else +=09=09return fuse_ll_ops[opcode].name; +} + +static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst, +=09=09=09=09 struct fuse_bufvec *src) +{ +=09ssize_t res =3D fuse_buf_copy(dst, src, 0); +=09if (res < 0) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res))= ; +=09=09return res; +=09} +=09if ((size_t)res < fuse_buf_size(dst)) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); +=09=09return -1; +=09} +=09return 0; +} + +void fuse_session_process_buf(struct fuse_session *se, +=09=09=09 const struct fuse_buf *buf) +{ +=09fuse_session_process_buf_int(se, buf, NULL); +} + +void fuse_session_process_buf_int(struct fuse_session *se, +=09=09=09=09 const struct fuse_buf *buf, struct fuse_chan *ch) +{ +=09const size_t write_header_size =3D sizeof(struct fuse_in_header) + +=09=09sizeof(struct fuse_write_in); +=09struct fuse_bufvec bufv =3D { .buf[0] =3D *buf, .count =3D 1 }; +=09struct fuse_bufvec tmpbuf =3D FUSE_BUFVEC_INIT(write_header_size); +=09struct fuse_in_header *in; +=09const void *inarg; +=09struct fuse_req *req; +=09void *mbuf =3D NULL; +=09int err; +=09int res; + +=09if (buf->flags & FUSE_BUF_IS_FD) { +=09=09if (buf->size < tmpbuf.buf[0].size) +=09=09=09tmpbuf.buf[0].size =3D buf->size; + +=09=09mbuf =3D malloc(tmpbuf.buf[0].size); +=09=09if (mbuf =3D=3D NULL) { +=09=09=09fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n"); +=09=09=09goto clear_pipe; +=09=09} +=09=09tmpbuf.buf[0].mem =3D mbuf; + +=09=09res =3D fuse_ll_copy_from_pipe(&tmpbuf, &bufv); +=09=09if (res < 0) +=09=09=09goto clear_pipe; + +=09=09in =3D mbuf; +=09} else { +=09=09in =3D buf->mem; +=09} + +=09if (se->debug) { +=09=09fuse_log(FUSE_LOG_DEBUG, +=09=09=09"unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %= u\n", +=09=09=09(unsigned long long) in->unique, +=09=09=09opname((enum fuse_opcode) in->opcode), in->opcode, +=09=09=09(unsigned long long) in->nodeid, buf->size, in->pid); +=09} + +=09req =3D fuse_ll_alloc_req(se); +=09if (req =3D=3D NULL) { +=09=09struct fuse_out_header out =3D { +=09=09=09.unique =3D in->unique, +=09=09=09.error =3D -ENOMEM, +=09=09}; +=09=09struct iovec iov =3D { +=09=09=09.iov_base =3D &out, +=09=09=09.iov_len =3D sizeof(struct fuse_out_header), +=09=09}; + +=09=09fuse_send_msg(se, ch, &iov, 1); +=09=09goto clear_pipe; +=09} + +=09req->unique =3D in->unique; +=09req->ctx.uid =3D in->uid; +=09req->ctx.gid =3D in->gid; +=09req->ctx.pid =3D in->pid; +=09req->ch =3D ch ? fuse_chan_get(ch) : NULL; + +=09err =3D EIO; +=09if (!se->got_init) { +=09=09enum fuse_opcode expected; + +=09=09expected =3D se->cuse_data ? CUSE_INIT : FUSE_INIT; +=09=09if (in->opcode !=3D expected) +=09=09=09goto reply_err; +=09} else if (in->opcode =3D=3D FUSE_INIT || in->opcode =3D=3D CUSE_INIT) +=09=09goto reply_err; + +=09err =3D EACCES; +=09/* Implement -o allow_root */ +=09if (se->deny_others && in->uid !=3D se->owner && in->uid !=3D 0 && +=09=09 in->opcode !=3D FUSE_INIT && in->opcode !=3D FUSE_READ && +=09=09 in->opcode !=3D FUSE_WRITE && in->opcode !=3D FUSE_FSYNC && +=09=09 in->opcode !=3D FUSE_RELEASE && in->opcode !=3D FUSE_READDIR && +=09=09 in->opcode !=3D FUSE_FSYNCDIR && in->opcode !=3D FUSE_RELEASEDIR && +=09=09 in->opcode !=3D FUSE_NOTIFY_REPLY && +=09=09 in->opcode !=3D FUSE_READDIRPLUS) +=09=09goto reply_err; + +=09err =3D ENOSYS; +=09if (in->opcode >=3D FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) +=09=09goto reply_err; +=09if (in->opcode !=3D FUSE_INTERRUPT) { +=09=09struct fuse_req *intr; +=09=09pthread_mutex_lock(&se->lock); +=09=09intr =3D check_interrupt(se, req); +=09=09list_add_req(req, &se->list); +=09=09pthread_mutex_unlock(&se->lock); +=09=09if (intr) +=09=09=09fuse_reply_err(intr, EAGAIN); +=09} + +=09if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size && +=09 (in->opcode !=3D FUSE_WRITE || !se->op.write_buf) && +=09 in->opcode !=3D FUSE_NOTIFY_REPLY) { +=09=09void *newmbuf; + +=09=09err =3D ENOMEM; +=09=09newmbuf =3D realloc(mbuf, buf->size); +=09=09if (newmbuf =3D=3D NULL) +=09=09=09goto reply_err; +=09=09mbuf =3D newmbuf; + +=09=09tmpbuf =3D FUSE_BUFVEC_INIT(buf->size - write_header_size); +=09=09tmpbuf.buf[0].mem =3D (char *)mbuf + write_header_size; + +=09=09res =3D fuse_ll_copy_from_pipe(&tmpbuf, &bufv); +=09=09err =3D -res; +=09=09if (res < 0) +=09=09=09goto reply_err; + +=09=09in =3D mbuf; +=09} + +=09inarg =3D (void *) &in[1]; +=09if (in->opcode =3D=3D FUSE_WRITE && se->op.write_buf) +=09=09do_write_buf(req, in->nodeid, inarg, buf); +=09else if (in->opcode =3D=3D FUSE_NOTIFY_REPLY) +=09=09do_notify_reply(req, in->nodeid, inarg, buf); +=09else +=09=09fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + +out_free: +=09free(mbuf); +=09return; + +reply_err: +=09fuse_reply_err(req, err); +clear_pipe: +=09if (buf->flags & FUSE_BUF_IS_FD) +=09=09fuse_ll_clear_pipe(se); +=09goto out_free; +} + +#define LL_OPTION(n,o,v) \ +=09{ n, offsetof(struct fuse_session, o), v } + +static const struct fuse_opt fuse_ll_opts[] =3D { +=09LL_OPTION("debug", debug, 1), +=09LL_OPTION("-d", debug, 1), +=09LL_OPTION("--debug", debug, 1), +=09LL_OPTION("allow_root", deny_others, 1), +=09FUSE_OPT_END +}; + +void fuse_lowlevel_version(void) +{ +=09printf("using FUSE kernel interface version %i.%i\n", +=09 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); +=09fuse_mount_version(); +} + +void fuse_lowlevel_help(void) +{ +=09/* These are not all options, but the ones that are +=09 potentially of interest to an end-user */ +=09printf( +" -o allow_other allow access by all users\n" +" -o allow_root allow access by root\n" +" -o auto_unmount auto unmount on process termination\n"); +} + +void fuse_session_destroy(struct fuse_session *se) +{ +=09struct fuse_ll_pipe *llp; + +=09if (se->got_init && !se->got_destroy) { +=09=09if (se->op.destroy) +=09=09=09se->op.destroy(se->userdata); +=09} +=09llp =3D pthread_getspecific(se->pipe_key); +=09if (llp !=3D NULL) +=09=09fuse_ll_pipe_free(llp); +=09pthread_key_delete(se->pipe_key); +=09pthread_mutex_destroy(&se->lock); +=09free(se->cuse_data); +=09if (se->fd !=3D -1) +=09=09close(se->fd); +=09destroy_mount_opts(se->mo); +=09free(se); +} + + +static void fuse_ll_pipe_destructor(void *data) +{ +=09struct fuse_ll_pipe *llp =3D data; +=09fuse_ll_pipe_free(llp); +} + +int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf= ) +{ +=09return fuse_session_receive_buf_int(se, buf, NULL); +} + +int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf = *buf, +=09=09=09=09 struct fuse_chan *ch) +{ +=09int err; +=09ssize_t res; +#ifdef HAVE_SPLICE +=09size_t bufsize =3D se->bufsize; +=09struct fuse_ll_pipe *llp; +=09struct fuse_buf tmpbuf; + +=09if (se->conn.proto_minor < 14 || !(se->conn.want & FUSE_CAP_SPLICE_READ= )) +=09=09goto fallback; + +=09llp =3D fuse_ll_get_pipe(se); +=09if (llp =3D=3D NULL) +=09=09goto fallback; + +=09if (llp->size < bufsize) { +=09=09if (llp->can_grow) { +=09=09=09res =3D fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize); +=09=09=09if (res =3D=3D -1) { +=09=09=09=09llp->can_grow =3D 0; +=09=09=09=09res =3D grow_pipe_to_max(llp->pipe[0]); +=09=09=09=09if (res > 0) +=09=09=09=09=09llp->size =3D res; +=09=09=09=09goto fallback; +=09=09=09} +=09=09=09llp->size =3D res; +=09=09} +=09=09if (llp->size < bufsize) +=09=09=09goto fallback; +=09} + +=09res =3D splice(ch ? ch->fd : se->fd, +=09=09 NULL, llp->pipe[1], NULL, bufsize, 0); +=09err =3D errno; + +=09if (fuse_session_exited(se)) +=09=09return 0; + +=09if (res =3D=3D -1) { +=09=09if (err =3D=3D ENODEV) { +=09=09=09/* Filesystem was unmounted, or connection was aborted +=09=09=09 via /sys/fs/fuse/connections */ +=09=09=09fuse_session_exit(se); +=09=09=09return 0; +=09=09} +=09=09if (err !=3D EINTR && err !=3D EAGAIN) +=09=09=09perror("fuse: splice from device"); +=09=09return -err; +=09} + +=09if (res < sizeof(struct fuse_in_header)) { +=09=09fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n"); +=09=09return -EIO; +=09} + +=09tmpbuf =3D (struct fuse_buf) { +=09=09.size =3D res, +=09=09.flags =3D FUSE_BUF_IS_FD, +=09=09.fd =3D llp->pipe[0], +=09}; + +=09/* +=09 * Don't bother with zero copy for small requests. +=09 * fuse_loop_mt() needs to check for FORGET so this more than +=09 * just an optimization. +=09 */ +=09if (res < sizeof(struct fuse_in_header) + +=09 sizeof(struct fuse_write_in) + pagesize) { +=09=09struct fuse_bufvec src =3D { .buf[0] =3D tmpbuf, .count =3D 1 }; +=09=09struct fuse_bufvec dst =3D { .count =3D 1 }; + +=09=09if (!buf->mem) { +=09=09=09buf->mem =3D malloc(se->bufsize); +=09=09=09if (!buf->mem) { +=09=09=09=09fuse_log(FUSE_LOG_ERR, +=09=09=09=09=09"fuse: failed to allocate read buffer\n"); +=09=09=09=09return -ENOMEM; +=09=09=09} +=09=09} +=09=09buf->size =3D se->bufsize; +=09=09buf->flags =3D 0; +=09=09dst.buf[0] =3D *buf; + +=09=09res =3D fuse_buf_copy(&dst, &src, 0); +=09=09if (res < 0) { +=09=09=09fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", +=09=09=09=09strerror(-res)); +=09=09=09fuse_ll_clear_pipe(se); +=09=09=09return res; +=09=09} +=09=09if (res < tmpbuf.size) { +=09=09=09fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); +=09=09=09fuse_ll_clear_pipe(se); +=09=09=09return -EIO; +=09=09} +=09=09assert(res =3D=3D tmpbuf.size); + +=09} else { +=09=09/* Don't overwrite buf->mem, as that would cause a leak */ +=09=09buf->fd =3D tmpbuf.fd; +=09=09buf->flags =3D tmpbuf.flags; +=09} +=09buf->size =3D tmpbuf.size; + +=09return res; + +fallback: +#endif +=09if (!buf->mem) { +=09=09buf->mem =3D malloc(se->bufsize); +=09=09if (!buf->mem) { +=09=09=09fuse_log(FUSE_LOG_ERR, +=09=09=09=09"fuse: failed to allocate read buffer\n"); +=09=09=09return -ENOMEM; +=09=09} +=09} + +restart: +=09res =3D read(ch ? ch->fd : se->fd, buf->mem, se->bufsize); +=09err =3D errno; + +=09if (fuse_session_exited(se)) +=09=09return 0; +=09if (res =3D=3D -1) { +=09=09/* ENOENT means the operation was interrupted, it's safe +=09=09 to restart */ +=09=09if (err =3D=3D ENOENT) +=09=09=09goto restart; + +=09=09if (err =3D=3D ENODEV) { +=09=09=09/* Filesystem was unmounted, or connection was aborted +=09=09=09 via /sys/fs/fuse/connections */ +=09=09=09fuse_session_exit(se); +=09=09=09return 0; +=09=09} +=09=09/* Errors occurring during normal operation: EINTR (read +=09=09 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem +=09=09 umounted) */ +=09=09if (err !=3D EINTR && err !=3D EAGAIN) +=09=09=09perror("fuse: reading device"); +=09=09return -err; +=09} +=09if ((size_t) res < sizeof(struct fuse_in_header)) { +=09=09fuse_log(FUSE_LOG_ERR, "short read on fuse device\n"); +=09=09return -EIO; +=09} + +=09buf->size =3D res; + +=09return res; +} + +struct fuse_session *fuse_session_new(struct fuse_args *args, +=09=09=09=09 const struct fuse_lowlevel_ops *op, +=09=09=09=09 size_t op_size, void *userdata) +{ +=09int err; +=09struct fuse_session *se; +=09struct mount_opts *mo; + +=09if (sizeof(struct fuse_lowlevel_ops) < op_size) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operati= ons may not work\n"); +=09=09op_size =3D sizeof(struct fuse_lowlevel_ops); +=09} + +=09if (args->argc =3D=3D 0) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new(= ).\n"); +=09=09return NULL; +=09} + +=09se =3D (struct fuse_session *) calloc(1, sizeof(struct fuse_session)); +=09if (se =3D=3D NULL) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); +=09=09goto out1; +=09} +=09se->fd =3D -1; +=09se->conn.max_write =3D UINT_MAX; +=09se->conn.max_readahead =3D UINT_MAX; + +=09/* Parse options */ +=09if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) =3D=3D -1) +=09=09goto out2; +=09if(se->deny_others) { +=09=09/* Allowing access only by root is done by instructing +=09=09 * kernel to allow access by everyone, and then restricting +=09=09 * access to root and mountpoint owner in libfuse. +=09=09 */ +=09=09// We may be adding the option a second time, but +=09=09// that doesn't hurt. +=09=09if(fuse_opt_add_arg(args, "-oallow_other") =3D=3D -1) +=09=09=09goto out2; +=09} +=09mo =3D parse_mount_opts(args); +=09if (mo =3D=3D NULL) +=09=09goto out3; + +=09if(args->argc =3D=3D 1 && +=09 args->argv[0][0] =3D=3D '-') { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option,= but " +=09=09=09"will be ignored\n"); +=09} else if (args->argc !=3D 1) { +=09=09int i; +=09=09fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `"); +=09=09for(i =3D 1; i < args->argc-1; i++) +=09=09=09fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]); +=09=09fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]); +=09=09goto out4; +=09} + +=09if (se->debug) +=09=09fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSI= ON); + +=09se->bufsize =3D FUSE_MAX_MAX_PAGES * getpagesize() + +=09=09FUSE_BUFFER_HEADER_SIZE; + +=09list_init_req(&se->list); +=09list_init_req(&se->interrupts); +=09list_init_nreq(&se->notify_list); +=09se->notify_ctr =3D 1; +=09fuse_mutex_init(&se->lock); + +=09err =3D pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor); +=09if (err) { +=09=09fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: = %s\n", +=09=09=09strerror(err)); +=09=09goto out5; +=09} + +=09memcpy(&se->op, op, op_size); +=09se->owner =3D getuid(); +=09se->userdata =3D userdata; + +=09se->mo =3D mo; +=09return se; + +out5: +=09pthread_mutex_destroy(&se->lock); +out4: +=09fuse_opt_free_args(args); +out3: +=09free(mo); +out2: +=09free(se); +out1: +=09return NULL; +} + +int fuse_session_mount(struct fuse_session *se, const char *mountpoint) +{ +=09int fd; + +=09/* +=09 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos +=09 * would ensue. +=09 */ +=09do { +=09=09fd =3D open("/dev/null", O_RDWR); +=09=09if (fd > 2) +=09=09=09close(fd); +=09} while (fd >=3D 0 && fd <=3D 2); + +=09/* +=09 * To allow FUSE daemons to run without privileges, the caller may open +=09 * /dev/fuse before launching the file system and pass on the file +=09 * descriptor by specifying /dev/fd/N as the mount point. Note that the +=09 * parent process takes care of performing the mount in this case. +=09 */ +=09fd =3D fuse_mnt_parse_fuse_fd(mountpoint); +=09if (fd !=3D -1) { +=09=09if (fcntl(fd, F_GETFD) =3D=3D -1) { +=09=09=09fuse_log(FUSE_LOG_ERR, +=09=09=09=09"fuse: Invalid file descriptor /dev/fd/%u\n", +=09=09=09=09fd); +=09=09=09return -1; +=09=09} +=09=09se->fd =3D fd; +=09=09return 0; +=09} + +=09/* Open channel */ +=09fd =3D fuse_kern_mount(mountpoint, se->mo); +=09if (fd =3D=3D -1) +=09=09return -1; +=09se->fd =3D fd; + +=09/* Save mountpoint */ +=09se->mountpoint =3D strdup(mountpoint); +=09if (se->mountpoint =3D=3D NULL) +=09=09goto error_out; + +=09return 0; + +error_out: +=09fuse_kern_unmount(mountpoint, fd); +=09return -1; +} + +int fuse_session_fd(struct fuse_session *se) +{ +=09return se->fd; +} + +void fuse_session_unmount(struct fuse_session *se) +{ +=09if (se->mountpoint !=3D NULL) { +=09=09fuse_kern_unmount(se->mountpoint, se->fd); +=09=09free(se->mountpoint); +=09=09se->mountpoint =3D NULL; +=09} +} + +#ifdef linux +int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) +{ +=09char *buf; +=09size_t bufsize =3D 1024; +=09char path[128]; +=09int ret; +=09int fd; +=09unsigned long pid =3D req->ctx.pid; +=09char *s; + +=09sprintf(path, "/proc/%lu/task/%lu/status", pid, pid); + +retry: +=09buf =3D malloc(bufsize); +=09if (buf =3D=3D NULL) +=09=09return -ENOMEM; + +=09ret =3D -EIO; +=09fd =3D open(path, O_RDONLY); +=09if (fd =3D=3D -1) +=09=09goto out_free; + +=09ret =3D read(fd, buf, bufsize); +=09close(fd); +=09if (ret < 0) { +=09=09ret =3D -EIO; +=09=09goto out_free; +=09} + +=09if ((size_t)ret =3D=3D bufsize) { +=09=09free(buf); +=09=09bufsize *=3D 4; +=09=09goto retry; +=09} + +=09ret =3D -EIO; +=09s =3D strstr(buf, "\nGroups:"); +=09if (s =3D=3D NULL) +=09=09goto out_free; + +=09s +=3D 8; +=09ret =3D 0; +=09while (1) { +=09=09char *end; +=09=09unsigned long val =3D strtoul(s, &end, 0); +=09=09if (end =3D=3D s) +=09=09=09break; + +=09=09s =3D end; +=09=09if (ret < size) +=09=09=09list[ret] =3D val; +=09=09ret++; +=09} + +out_free: +=09free(buf); +=09return ret; +} +#else /* linux */ +/* + * This is currently not implemented on other than Linux... + */ +int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) +{ +=09(void) req; (void) size; (void) list; +=09return -ENOSYS; +} +#endif + +void fuse_session_exit(struct fuse_session *se) +{ +=09se->exited =3D 1; +} + +void fuse_session_reset(struct fuse_session *se) +{ +=09se->exited =3D 0; +=09se->error =3D 0; +} + +int fuse_session_exited(struct fuse_session *se) +{ +=09return se->exited; +} --=20 2.24.1