From nobody Mon Dec 15 00:32:44 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A2EF42F6579; Fri, 12 Dec 2025 18:13:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563196; cv=none; b=nvcwvpBr5RRHO1w904LUt0SyX4JyEN5r6K9FpV9eFrvugMCuRM8Q9XzmE2LpJOroGCQ4Cv30nrM1Zo/57WixLoz6Y9xQfjnrqqga3SM3YOOqmSdZdjup3TEn0KK005OyUTEl4WZ14cwju34Ridtm965rD8wmFLkHOIrB1SGhJnI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563196; c=relaxed/simple; bh=sE1U2JT29IIyaZXGY4gYUCAGTYj8W24heMfIOeCkHnA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nxR0gRQfksG/04AURm4AEL8GhxIlHraO3krU35Yj9U+D0weRl1CSPnG/aWKYJ0mqJZJ5wl6zOHsB1hTV+EBqKSk5qS991LDtLvVgGjQkry6S1t1EBVAxc/hGvRRLxcVGMy65zz7JX0Ij4SDH6DWl+Ph+zht5IMPvYs3xt0/EJiw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=AvTD13PT; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="AvTD13PT" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=Wh/bmeeiRnR220UJ6v12aSATo70mRqQdcfMFQLSOHB4=; b=AvTD13PTmVny7+UU1gZsU0ZPrI 1M4yvngLeCzAFkimDh/Wh+mT4zavqujlgPoMdo2zjeDKfMD5LGzLTexNxftu1gyF/P+sdxdTgGhMl Hf1q7FG+uqGzP5+mfk21MEg5Mnl28+UKdodL58LzHvWXNM1SaiI2HhLuB+vUF/lUoI57WONm2oJiC TC2AjAcQPtWtU8je++Q1l65iBIzboyUxcdaPd6/eM389sAeZ0HoELhTx9lZaVkXY/C1FwButns7th oqJMz3Wtlh8eZBa2RF1cPY6s40q0ly3oXYZ8p2hTFwzBiMBwiRCqFdMEMuWAeqFqBjZqf8DqRUVzf oupWD65A==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1vU7dR-00C798-40; Fri, 12 Dec 2025 19:12:57 +0100 From: Luis Henriques To: Miklos Szeredi Cc: Amir Goldstein , "Darrick J. Wong" , Bernd Schubert , Kevin Chen , Horst Birthelmer , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matt Harvey , kernel-dev@igalia.com, Luis Henriques Subject: [RFC PATCH v2 1/6] fuse: store index of the variable length argument Date: Fri, 12 Dec 2025 18:12:49 +0000 Message-ID: <20251212181254.59365-2-luis@igalia.com> In-Reply-To: <20251212181254.59365-1-luis@igalia.com> References: <20251212181254.59365-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Operations that have a variable length argument assume that it will always be the last argument on the list. This patch allows this assumption to be removed by keeping track of the index of variable length argument. Signed-off-by: Luis Henriques --- fs/fuse/cuse.c | 1 + fs/fuse/dev.c | 4 ++-- fs/fuse/dir.c | 1 + fs/fuse/file.c | 1 + fs/fuse/fuse_i.h | 2 ++ fs/fuse/inode.c | 1 + fs/fuse/ioctl.c | 1 + fs/fuse/virtio_fs.c | 6 +++--- fs/fuse/xattr.c | 2 ++ 9 files changed, 14 insertions(+), 5 deletions(-) diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 28c96961e85d..9d93a3023b19 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -460,6 +460,7 @@ static int cuse_send_init(struct cuse_conn *cc) ap->args.out_args[0].value =3D &ia->out; ap->args.out_args[1].size =3D CUSE_INIT_INFO_MAX; ap->args.out_argvar =3D true; + ap->args.out_argvar =3D 1; ap->args.out_pages =3D true; ap->num_folios =3D 1; ap->folios =3D &ia->folio; diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 132f38619d70..629e8a043079 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -694,7 +694,7 @@ ssize_t __fuse_simple_request(struct mnt_idmap *idmap, ret =3D req->out.h.error; if (!ret && args->out_argvar) { BUG_ON(args->out_numargs =3D=3D 0); - ret =3D args->out_args[args->out_numargs - 1].size; + ret =3D args->out_args[args->out_argvar_idx].size; } fuse_put_request(req); =20 @@ -2157,7 +2157,7 @@ int fuse_copy_out_args(struct fuse_copy_state *cs, st= ruct fuse_args *args, if (reqsize < nbytes || (reqsize > nbytes && !args->out_argvar)) return -EINVAL; else if (reqsize > nbytes) { - struct fuse_arg *lastarg =3D &args->out_args[args->out_numargs-1]; + struct fuse_arg *lastarg =3D &args->out_args[args->out_argvar_idx]; unsigned diffsize =3D reqsize - nbytes; =20 if (diffsize > lastarg->size) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index ecaec0fea3a1..4dfe964a491c 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1629,6 +1629,7 @@ static int fuse_readlink_folio(struct inode *inode, s= truct folio *folio) ap.args.nodeid =3D get_node_id(inode); ap.args.out_pages =3D true; ap.args.out_argvar =3D true; + ap.args.out_argvar_idx =3D 0; ap.args.page_zeroing =3D true; ap.args.out_numargs =3D 1; ap.args.out_args[0].size =3D desc.length; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index f1ef77a0be05..0fef3da1585c 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -581,6 +581,7 @@ void fuse_read_args_fill(struct fuse_io_args *ia, struc= t file *file, loff_t pos, args->in_args[0].size =3D sizeof(ia->read.in); args->in_args[0].value =3D &ia->read.in; args->out_argvar =3D true; + args->out_argvar_idx =3D 0; args->out_numargs =3D 1; args->out_args[0].size =3D count; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index c2f2a48156d6..a5714bae4c45 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -319,6 +319,8 @@ struct fuse_args { uint32_t opcode; uint8_t in_numargs; uint8_t out_numargs; + /* The index of the variable lenght out arg */ + uint8_t out_argvar_idx; uint8_t ext_idx; bool force:1; bool noreply:1; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index d1babf56f254..8917e5b7a009 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1534,6 +1534,7 @@ static struct fuse_init_args *fuse_new_init(struct fu= se_mount *fm) with interface version < 7.5. Rest of init_out is zeroed by do_get_request(), so a short reply is not a problem */ ia->args.out_argvar =3D true; + ia->args.out_argvar_idx =3D 0; ia->args.out_args[0].size =3D sizeof(ia->out); ia->args.out_args[0].value =3D &ia->out; ia->args.force =3D true; diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c index fdc175e93f74..03a2b321e611 100644 --- a/fs/fuse/ioctl.c +++ b/fs/fuse/ioctl.c @@ -337,6 +337,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd,= unsigned long arg, ap.args.out_args[1].size =3D out_size; ap.args.out_pages =3D true; ap.args.out_argvar =3D true; + ap.args.out_argvar_idx =3D 1; =20 transferred =3D fuse_send_ioctl(fm, &ap.args, &outarg); err =3D transferred; diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c index b2f6486fe1d5..26fb9c29f935 100644 --- a/fs/fuse/virtio_fs.c +++ b/fs/fuse/virtio_fs.c @@ -738,7 +738,7 @@ static void copy_args_from_argbuf(struct fuse_args *arg= s, struct fuse_req *req) unsigned int argsize =3D args->out_args[i].size; =20 if (args->out_argvar && - i =3D=3D args->out_numargs - 1 && + i =3D=3D args->out_argvar_idx && argsize > remaining) { argsize =3D remaining; } @@ -746,13 +746,13 @@ static void copy_args_from_argbuf(struct fuse_args *a= rgs, struct fuse_req *req) memcpy(args->out_args[i].value, req->argbuf + offset, argsize); offset +=3D argsize; =20 - if (i !=3D args->out_numargs - 1) + if (i !=3D args->out_argvar_idx) remaining -=3D argsize; } =20 /* Store the actual size of the variable-length arg */ if (args->out_argvar) - args->out_args[args->out_numargs - 1].size =3D remaining; + args->out_args[args->out_argvar_idx].size =3D remaining; =20 kfree(req->argbuf); req->argbuf =3D NULL; diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c index 93dfb06b6cea..f123446fe537 100644 --- a/fs/fuse/xattr.c +++ b/fs/fuse/xattr.c @@ -73,6 +73,7 @@ ssize_t fuse_getxattr(struct inode *inode, const char *na= me, void *value, args.out_numargs =3D 1; if (size) { args.out_argvar =3D true; + args.out_argvar_idx =3D 0; args.out_args[0].size =3D size; args.out_args[0].value =3D value; } else { @@ -135,6 +136,7 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list= , size_t size) args.out_numargs =3D 1; if (size) { args.out_argvar =3D true; + args.out_argvar_idx =3D 0; args.out_args[0].size =3D size; args.out_args[0].value =3D list; } else { From nobody Mon Dec 15 00:32:44 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ACE7B2F6587; Fri, 12 Dec 2025 18:13:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563197; cv=none; b=UI2SEJeA1p6qj8f3JPJMQbv9nzSrD9VR8SlRdI1WCh/r4heVkcaVBknsEJAIk3yKDhSkPZkdyYqoggTLRiM8FTE1FFalv8Pj7lIiQE36/BGE1f87LJFKuPMI7K258DlX7cL59UU3a/xqHNC4sPXQzQ2Qr3MewKZ8ph+FnsnKShU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563197; c=relaxed/simple; bh=HCZNSk0iYumkjmv9C9O/jj9YQkVOTpLW4OoYCTdohds=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lenLM7P+FUR4b8+gNEXTpbA8uszNGRKRjBqB+KdnKAUQQaPOyBQqbpO8x45a1Er+s8MCWDFrENnFxfXHsarAG+kxXeTXVP7uM/TrzEzFf2qwDGwxYtOKqOYg7+UdOOLw7kmzieBOQqApV2RSk42M2N61TNEA7wewF7/Sn38SGSo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=peMHZ0z6; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="peMHZ0z6" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=cJ2i33yjmDs56CHIlM6R8optmQ8CmHwvdBSdgk3g7No=; b=peMHZ0z6+mfSnrYOGuQ13bR6PV XSBTuTL9tTAovPBEYIkYWz7o4QDqU2iVjLXong5bIDgFqc+6uKzwpDJbCq29sMq2qZF3kXNXyvw8c r/qs1oF9yVAy/5rPepvKhjUstqG+TrSLLDIWnAm4ptLvb11kMDEFvsleRkeIs+kXQCsU+su1oC6wA OfTeI4KLY69ZsGth/MkDDwvzCYWwQ+KSe+w87isEzWDag4knbacohEMyxfzgmFowuX+RTyocEBYwm 1yKue+tcArDy0Yx6TRUdcyYdgcr32/si59yonrO7LZiyHmauHZIo4sfATmyNVpzWw7kQhx930Ykq1 LFTEUOmA==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1vU7dR-00C79A-Oa; Fri, 12 Dec 2025 19:12:57 +0100 From: Luis Henriques To: Miklos Szeredi Cc: Amir Goldstein , "Darrick J. Wong" , Bernd Schubert , Kevin Chen , Horst Birthelmer , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matt Harvey , kernel-dev@igalia.com, Luis Henriques Subject: [RFC PATCH v2 2/6] fuse: move fuse_entry_out structs out of the stack Date: Fri, 12 Dec 2025 18:12:50 +0000 Message-ID: <20251212181254.59365-3-luis@igalia.com> In-Reply-To: <20251212181254.59365-1-luis@igalia.com> References: <20251212181254.59365-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch simply turns all struct fuse_entry_out instances that are allocated in the stack into dynamically allocated structs. This is a preparation patch for further changes, including the extra helper function used to actually allocate the memory. Also, remove all the memset()s that are used to zero-out these structures, as kzalloc() is being used. Signed-off-by: Luis Henriques --- fs/fuse/dir.c | 139 ++++++++++++++++++++++++++++++----------------- fs/fuse/fuse_i.h | 9 +++ fs/fuse/inode.c | 20 +++++-- 3 files changed, 114 insertions(+), 54 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 4dfe964a491c..e3fd5d148741 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -172,7 +172,6 @@ static void fuse_lookup_init(struct fuse_conn *fc, stru= ct fuse_args *args, u64 nodeid, const struct qstr *name, struct fuse_entry_out *outarg) { - memset(outarg, 0, sizeof(struct fuse_entry_out)); args->opcode =3D FUSE_LOOKUP; args->nodeid =3D nodeid; args->in_numargs =3D 3; @@ -213,7 +212,7 @@ static int fuse_dentry_revalidate(struct inode *dir, co= nst struct qstr *name, goto invalid; else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) || (flags & (LOOKUP_EXCL | LOOKUP_REVAL | LOOKUP_RENAME_TARGET))) { - struct fuse_entry_out outarg; + struct fuse_entry_out *outarg; FUSE_ARGS(args); struct fuse_forget_link *forget; u64 attr_version; @@ -233,20 +232,27 @@ static int fuse_dentry_revalidate(struct inode *dir, = const struct qstr *name, if (!forget) goto out; =20 + outarg =3D fuse_entry_out_alloc(fc); + if (!outarg) { + kfree(forget); + goto out; + } + attr_version =3D fuse_get_attr_version(fm->fc); =20 fuse_lookup_init(fm->fc, &args, get_node_id(dir), - name, &outarg); + name, outarg); ret =3D fuse_simple_request(fm, &args); /* Zero nodeid is same as -ENOENT */ - if (!ret && !outarg.nodeid) + if (!ret && !outarg->nodeid) ret =3D -ENOENT; if (!ret) { fi =3D get_fuse_inode(inode); - if (outarg.nodeid !=3D get_node_id(inode) || - (bool) IS_AUTOMOUNT(inode) !=3D (bool) (outarg.attr.flags & FUSE_AT= TR_SUBMOUNT)) { + if (outarg->nodeid !=3D get_node_id(inode) || + (bool) IS_AUTOMOUNT(inode) !=3D (bool) (outarg->attr.flags & FUSE_A= TTR_SUBMOUNT)) { fuse_queue_forget(fm->fc, forget, - outarg.nodeid, 1); + outarg->nodeid, 1); + kfree(outarg); goto invalid; } spin_lock(&fi->lock); @@ -254,17 +260,22 @@ static int fuse_dentry_revalidate(struct inode *dir, = const struct qstr *name, spin_unlock(&fi->lock); } kfree(forget); - if (ret =3D=3D -ENOMEM || ret =3D=3D -EINTR) + if (ret =3D=3D -ENOMEM || ret =3D=3D -EINTR) { + kfree(outarg); goto out; - if (ret || fuse_invalid_attr(&outarg.attr) || - fuse_stale_inode(inode, outarg.generation, &outarg.attr)) + } + if (ret || fuse_invalid_attr(&outarg->attr) || + fuse_stale_inode(inode, outarg->generation, &outarg->attr)) { + kfree(outarg); goto invalid; + } =20 forget_all_cached_acls(inode); - fuse_change_attributes(inode, &outarg.attr, NULL, - ATTR_TIMEOUT(&outarg), + fuse_change_attributes(inode, &outarg->attr, NULL, + ATTR_TIMEOUT(outarg), attr_version); - fuse_change_entry_timeout(entry, &outarg); + fuse_change_entry_timeout(entry, outarg); + kfree(outarg); } else if (inode) { fi =3D get_fuse_inode(inode); if (flags & LOOKUP_RCU) { @@ -410,7 +421,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid= , const struct qstr *name static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, unsigned int flags) { - struct fuse_entry_out outarg; + struct fuse_entry_out *outarg; struct fuse_conn *fc; struct inode *inode; struct dentry *newent; @@ -424,9 +435,13 @@ static struct dentry *fuse_lookup(struct inode *dir, s= truct dentry *entry, fc =3D get_fuse_conn_super(dir->i_sb); epoch =3D atomic_read(&fc->epoch); =20 + outarg =3D fuse_entry_out_alloc(fc); + if (!outarg) + return ERR_PTR(-ENOMEM); + locked =3D fuse_lock_inode(dir); err =3D fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, - &outarg, &inode); + outarg, &inode); fuse_unlock_inode(dir, locked); if (err =3D=3D -ENOENT) { outarg_valid =3D false; @@ -447,17 +462,21 @@ static struct dentry *fuse_lookup(struct inode *dir, = struct dentry *entry, entry =3D newent ? newent : entry; entry->d_time =3D epoch; if (outarg_valid) - fuse_change_entry_timeout(entry, &outarg); + fuse_change_entry_timeout(entry, outarg); else fuse_invalidate_entry_cache(entry); =20 if (inode) fuse_advise_use_readdirplus(dir); + + kfree(outarg); + return newent; =20 - out_iput: +out_iput: iput(inode); - out_err: +out_err: + kfree(outarg); return ERR_PTR(err); } =20 @@ -625,7 +644,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, st= ruct inode *dir, struct fuse_forget_link *forget; struct fuse_create_in inarg; struct fuse_open_out *outopenp; - struct fuse_entry_out outentry; + struct fuse_entry_out *outentry; struct fuse_inode *fi; struct fuse_file *ff; int epoch, err; @@ -640,17 +659,19 @@ static int fuse_create_open(struct mnt_idmap *idmap, = struct inode *dir, if (!forget) goto out_err; =20 - err =3D -ENOMEM; ff =3D fuse_file_alloc(fm, true); if (!ff) goto out_put_forget_req; =20 + outentry =3D fuse_entry_out_alloc(fm->fc); + if (!outentry) + goto out_free_ff; + if (!fm->fc->dont_mask) mode &=3D ~current_umask(); =20 flags &=3D ~O_NOCTTY; memset(&inarg, 0, sizeof(inarg)); - memset(&outentry, 0, sizeof(outentry)); inarg.flags =3D flags; inarg.mode =3D mode; inarg.umask =3D current_umask(); @@ -668,8 +689,8 @@ static int fuse_create_open(struct mnt_idmap *idmap, st= ruct inode *dir, args.in_args[1].size =3D entry->d_name.len + 1; args.in_args[1].value =3D entry->d_name.name; args.out_numargs =3D 2; - args.out_args[0].size =3D sizeof(outentry); - args.out_args[0].value =3D &outentry; + args.out_args[0].size =3D sizeof(*outentry); + args.out_args[0].value =3D outentry; /* Store outarg for fuse_finish_open() */ outopenp =3D &ff->args->open_outarg; args.out_args[1].size =3D sizeof(*outopenp); @@ -677,34 +698,35 @@ static int fuse_create_open(struct mnt_idmap *idmap, = struct inode *dir, =20 err =3D get_create_ext(idmap, &args, dir, entry, mode); if (err) - goto out_free_ff; + goto out_free_outentry; =20 err =3D fuse_simple_idmap_request(idmap, fm, &args); free_ext_value(&args); if (err) - goto out_free_ff; + goto out_free_outentry; =20 err =3D -EIO; - if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid) || - fuse_invalid_attr(&outentry.attr)) - goto out_free_ff; + if (!S_ISREG(outentry->attr.mode) || invalid_nodeid(outentry->nodeid) || + fuse_invalid_attr(&outentry->attr)) + goto out_free_outentry; =20 ff->fh =3D outopenp->fh; - ff->nodeid =3D outentry.nodeid; + ff->nodeid =3D outentry->nodeid; ff->open_flags =3D outopenp->open_flags; - inode =3D fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, - &outentry.attr, ATTR_TIMEOUT(&outentry), 0, 0); + inode =3D fuse_iget(dir->i_sb, outentry->nodeid, outentry->generation, + &outentry->attr, ATTR_TIMEOUT(outentry), 0, 0); if (!inode) { flags &=3D ~(O_CREAT | O_EXCL | O_TRUNC); fuse_sync_release(NULL, ff, flags); - fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1); + fuse_queue_forget(fm->fc, forget, outentry->nodeid, 1); err =3D -ENOMEM; + kfree(outentry); goto out_err; } kfree(forget); d_instantiate(entry, inode); entry->d_time =3D epoch; - fuse_change_entry_timeout(entry, &outentry); + fuse_change_entry_timeout(entry, outentry); fuse_dir_changed(dir); err =3D generic_file_open(inode, file); if (!err) { @@ -720,8 +742,13 @@ static int fuse_create_open(struct mnt_idmap *idmap, s= truct inode *dir, else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) invalidate_inode_pages2(inode->i_mapping); } + + kfree(outentry); + return err; =20 +out_free_outentry: + kfree(outentry); out_free_ff: fuse_file_free(ff); out_put_forget_req: @@ -780,7 +807,7 @@ static struct dentry *create_new_entry(struct mnt_idmap= *idmap, struct fuse_moun struct fuse_args *args, struct inode *dir, struct dentry *entry, umode_t mode) { - struct fuse_entry_out outarg; + struct fuse_entry_out *outarg; struct inode *inode; struct dentry *d; struct fuse_forget_link *forget; @@ -795,54 +822,66 @@ static struct dentry *create_new_entry(struct mnt_idm= ap *idmap, struct fuse_moun if (!forget) return ERR_PTR(-ENOMEM); =20 - memset(&outarg, 0, sizeof(outarg)); + outarg =3D fuse_entry_out_alloc(fm->fc); + if (!outarg) { + err =3D -ENOMEM; + goto out_put_forget_req; + } + args->nodeid =3D get_node_id(dir); args->out_numargs =3D 1; - args->out_args[0].size =3D sizeof(outarg); - args->out_args[0].value =3D &outarg; + args->out_args[0].size =3D sizeof(*outarg); + args->out_args[0].value =3D outarg; =20 if (args->opcode !=3D FUSE_LINK) { err =3D get_create_ext(idmap, args, dir, entry, mode); if (err) - goto out_put_forget_req; + goto out_free_outarg; } =20 err =3D fuse_simple_idmap_request(idmap, fm, args); free_ext_value(args); if (err) - goto out_put_forget_req; + goto out_free_outarg; =20 err =3D -EIO; - if (invalid_nodeid(outarg.nodeid) || fuse_invalid_attr(&outarg.attr)) - goto out_put_forget_req; + if (invalid_nodeid(outarg->nodeid) || fuse_invalid_attr(&outarg->attr)) + goto out_free_outarg; =20 - if ((outarg.attr.mode ^ mode) & S_IFMT) - goto out_put_forget_req; + if ((outarg->attr.mode ^ mode) & S_IFMT) + goto out_free_outarg; =20 - inode =3D fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, - &outarg.attr, ATTR_TIMEOUT(&outarg), 0, 0); + inode =3D fuse_iget(dir->i_sb, outarg->nodeid, outarg->generation, + &outarg->attr, ATTR_TIMEOUT(outarg), 0, 0); if (!inode) { - fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1); + fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1); + kfree(outarg); return ERR_PTR(-ENOMEM); } kfree(forget); =20 d_drop(entry); d =3D d_splice_alias(inode, entry); - if (IS_ERR(d)) + if (IS_ERR(d)) { + kfree(outarg); return d; + } =20 if (d) { d->d_time =3D epoch; - fuse_change_entry_timeout(d, &outarg); + fuse_change_entry_timeout(d, outarg); } else { entry->d_time =3D epoch; - fuse_change_entry_timeout(entry, &outarg); + fuse_change_entry_timeout(entry, outarg); } fuse_dir_changed(dir); + kfree(outarg); + return d; =20 - out_put_forget_req: +out_free_outarg: + kfree(outarg); +out_put_forget_req: if (err =3D=3D -EEXIST) fuse_invalidate_entry(entry); kfree(forget); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index a5714bae4c45..1792ee6f5da6 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1090,6 +1090,15 @@ static inline bool fuse_is_bad(struct inode *inode) return unlikely(test_bit(FUSE_I_BAD, &get_fuse_inode(inode)->state)); } =20 +static inline struct fuse_entry_out *fuse_entry_out_alloc(struct fuse_conn= *fc) +{ + struct fuse_entry_out *entryout; + + entryout =3D kzalloc(sizeof(*entryout), GFP_KERNEL_ACCOUNT); + + return entryout; +} + static inline struct folio **fuse_folios_alloc(unsigned int nfolios, gfp_t= flags, struct fuse_folio_desc **desc) { diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 8917e5b7a009..ef63300c634f 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1080,14 +1080,21 @@ static struct dentry *fuse_get_dentry(struct super_= block *sb, =20 inode =3D ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); if (!inode) { - struct fuse_entry_out outarg; + struct fuse_entry_out *outarg; const struct qstr name =3D QSTR_INIT(".", 1); =20 if (!fc->export_support) goto out_err; =20 - err =3D fuse_lookup_name(sb, handle->nodeid, &name, &outarg, + outarg =3D fuse_entry_out_alloc(fc); + if (!outarg) { + err =3D -ENOMEM; + goto out_err; + } + + err =3D fuse_lookup_name(sb, handle->nodeid, &name, outarg, &inode); + kfree(outarg); if (err && err !=3D -ENOENT) goto out_err; if (err || !inode) { @@ -1181,14 +1188,19 @@ static struct dentry *fuse_get_parent(struct dentry= *child) struct fuse_conn *fc =3D get_fuse_conn(child_inode); struct inode *inode; struct dentry *parent; - struct fuse_entry_out outarg; + struct fuse_entry_out *outarg; int err; =20 if (!fc->export_support) return ERR_PTR(-ESTALE); =20 + outarg =3D fuse_entry_out_alloc(fc); + if (!outarg) + return ERR_PTR(-ENOMEM); + err =3D fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), - &dotdot_name, &outarg, &inode); + &dotdot_name, outarg, &inode); + kfree(outarg); if (err) { if (err =3D=3D -ENOENT) return ERR_PTR(-ESTALE); From nobody Mon Dec 15 00:32:44 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A2E782F656F; Fri, 12 Dec 2025 18:13:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563196; cv=none; b=LgsTVgfZZnThEabGq386k7d34qJQiLINJBqlxUPYNeVZVtdKESaaCIVsD9ohH9818GWBirsPHHTpEZY64vOwREdgGB7UTb0OAuWokEwQouhKisdh9wR9il40PFCCmkiZiS7oF20dM+hCDeW4/1zTlFsZ0s4JryOUFp8mSXmopd4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563196; c=relaxed/simple; bh=ynhgHfak6jkPwGyW5W89VK1SLurq4Dbqfs8lTbnxLQ0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CzsJy7/mzq32bsGQIVZpA1cfUX1OT+bUfd1cdxjDbS4oyc3C2qrw05ByacctmCBkQwD8XDN+4llsGoLsQIjjoeQlUa0UQIUT+/NEXmoJ6PzO6SFSb6Le1LTB3k4qKDG/k207GJGqFYXCcXPG9tZe3+t2Z3kCLQd9YCiQHKSE6FM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=g64myGf3; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="g64myGf3" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=vZrtZ0rYdYw9DSaqbNih5R5OgH1BU6sM72ak1569OCE=; b=g64myGf32DokboUe7Eu1J06T0v /Ae5lYdID8nvPHtckqIvhpGvQdHSRBM93ncL0lfq+6mFFDpP8RvoIAA/X3y/3milgpi+ng8Rbk4LH KemJfmpaotF8KWcoXBMQ5DGaSIpYS/iEHiTjkTncNRYwslj4cZR1Jx7O5lcv1k9Q8rHs9wdkH0wpP +ubwKzdN1Vbl+URh/UF2uK98w9R5SdBBopDmkFxpeVqQAvDDDb/FJwaRu0DQkBYG8nW/CT42NH2mR JaCK59g/PF98lu8CptfQlNNhptrCNQY9O795DCB15+wVxfMHMyB1SDtzROcAJrN4P+2gnEWYvzWos b7h0bS7g==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1vU7dS-00C79C-A7; Fri, 12 Dec 2025 19:12:58 +0100 From: Luis Henriques To: Miklos Szeredi Cc: Amir Goldstein , "Darrick J. Wong" , Bernd Schubert , Kevin Chen , Horst Birthelmer , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matt Harvey , kernel-dev@igalia.com, Luis Henriques Subject: [RFC PATCH v2 3/6] fuse: initial infrastructure for FUSE_LOOKUP_HANDLE support Date: Fri, 12 Dec 2025 18:12:51 +0000 Message-ID: <20251212181254.59365-4-luis@igalia.com> In-Reply-To: <20251212181254.59365-1-luis@igalia.com> References: <20251212181254.59365-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch adds the initial infrastructure to implement the LOOKUP_HANDLE operation. It simply defines the new operation and the extra fuse_init_out field to set the maximum handle size. Signed-off-by: Luis Henriques --- fs/fuse/fuse_i.h | 4 ++++ fs/fuse/inode.c | 9 ++++++++- include/uapi/linux/fuse.h | 8 +++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 1792ee6f5da6..fad05fae7e54 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -909,6 +909,10 @@ struct fuse_conn { /* Is synchronous FUSE_INIT allowed? */ unsigned int sync_init:1; =20 + /** Is LOOKUP_HANDLE implemented by fs? */ + unsigned int lookup_handle:1; + unsigned int max_handle_sz; + /* Use io_uring for communication */ unsigned int io_uring; =20 diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index ef63300c634f..bc84e7ed1e3d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1465,6 +1465,13 @@ static void process_init_reply(struct fuse_mount *fm= , struct fuse_args *args, =20 if (flags & FUSE_REQUEST_TIMEOUT) timeout =3D arg->request_timeout; + + if ((flags & FUSE_HAS_LOOKUP_HANDLE) && + (arg->max_handle_sz > 0) && + (arg->max_handle_sz <=3D FUSE_MAX_HANDLE_SZ)) { + fc->lookup_handle =3D 1; + fc->max_handle_sz =3D arg->max_handle_sz; + } } else { ra_pages =3D fc->max_read / PAGE_SIZE; fc->no_lock =3D 1; @@ -1515,7 +1522,7 @@ static struct fuse_init_args *fuse_new_init(struct fu= se_mount *fm) FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP | FUSE_HAS_EXPIRE_ONLY | FUSE_DIRECT_IO_ALLOW_MMAP | FUSE_NO_EXPORT_SUPPORT | FUSE_HAS_RESEND | FUSE_ALLOW_IDMAP | - FUSE_REQUEST_TIMEOUT; + FUSE_REQUEST_TIMEOUT | FUSE_LOOKUP_HANDLE; #ifdef CONFIG_FUSE_DAX if (fm->fc->dax) flags |=3D FUSE_MAP_ALIGNMENT; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index c13e1f9a2f12..4acf71b407c9 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -495,6 +495,7 @@ struct fuse_file_lock { #define FUSE_ALLOW_IDMAP (1ULL << 40) #define FUSE_OVER_IO_URING (1ULL << 41) #define FUSE_REQUEST_TIMEOUT (1ULL << 42) +#define FUSE_HAS_LOOKUP_HANDLE (1ULL << 43) =20 /** * CUSE INIT request/reply flags @@ -663,6 +664,7 @@ enum fuse_opcode { FUSE_TMPFILE =3D 51, FUSE_STATX =3D 52, FUSE_COPY_FILE_RANGE_64 =3D 53, + FUSE_LOOKUP_HANDLE =3D 54, =20 /* CUSE specific operations */ CUSE_INIT =3D 4096, @@ -908,6 +910,9 @@ struct fuse_init_in { uint32_t unused[11]; }; =20 +/* Same value as MAX_HANDLE_SZ */ +#define FUSE_MAX_HANDLE_SZ 128 + #define FUSE_COMPAT_INIT_OUT_SIZE 8 #define FUSE_COMPAT_22_INIT_OUT_SIZE 24 =20 @@ -925,7 +930,8 @@ struct fuse_init_out { uint32_t flags2; uint32_t max_stack_depth; uint16_t request_timeout; - uint16_t unused[11]; + uint16_t max_handle_sz; + uint16_t unused[10]; }; =20 #define CUSE_INIT_INFO_MAX 4096 From nobody Mon Dec 15 00:32:44 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ACDF72F657E; Fri, 12 Dec 2025 18:13:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563197; cv=none; b=q3qsK3ga3TUw0jqPnmnBXw7xbsAYrXYeEgrqFdnC2XXYXqPoqZzbZ8l7izDBlAENOEFHwJV+YfX7nvJ6idfSpXKWtTDAnAMOUls/FrP8iCwRM5kWBFW3JEMjBGKSasXLWcrGRkk3XHtZdih0AukHYxRWVE4UgLviZ0MRt6VHLiA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563197; c=relaxed/simple; bh=TymUcOiOH0JY+e73CjioFeS7FilzMIHKNfDASP6QBTw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=n6sWasn8p6BECid0SP0ZUNQxAAcxXiYcRHvqHw3ykA/NU198eMD5wuIqIz1My7cZO4qD6OKkt5v+rtUqz5UusY19CBZ36P9s1pHGLguUYSSZlu6foBGmiSTVi9P3a2lpWDmcYgSQzFv/LQ8nT/juapcMMeNM/EeL0jqAZn2C/Lk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=QrzKmi/5; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="QrzKmi/5" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=xhSRC1pRxYKtyL1yt7UXgURolEb1bnWsjEWJh2FNPJc=; b=QrzKmi/5fyHqE7bGAWLdfXPYui Wo4xQ+95WaIbAhAxyTD7+mErrJl92JUX25WmlUgd6dix4rm/kB6yA6Yr2m3ZUg/bPzyG8+xhR/gGX I+vYEhVwKmp9dVBtf9h8Gi/X2OOWz8wiE2epVk8jkdJsaFtYBFAIRGrY0eSiFfn/0KFOO9r2nOfWZ Wr/p1TUjooqgYQpkQonfZ0U3DcLdsPG66seC2GL5GXQ2WLCfkpHVSROutPxZMK4z/AtK5UJbPBhDd ribNvLQi3zeuPsi+D7KSflueq8pONnr/C9A2qaUpnO3XsiWF2BzpfmcMcO+aRWD2wHFsYbFyTAR/X Ox03svIw==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1vU7dS-00C79E-Sf; Fri, 12 Dec 2025 19:12:59 +0100 From: Luis Henriques To: Miklos Szeredi Cc: Amir Goldstein , "Darrick J. Wong" , Bernd Schubert , Kevin Chen , Horst Birthelmer , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matt Harvey , kernel-dev@igalia.com, Luis Henriques Subject: [RFC PATCH v2 4/6] fuse: implementation of the FUSE_LOOKUP_HANDLE operation Date: Fri, 12 Dec 2025 18:12:52 +0000 Message-ID: <20251212181254.59365-5-luis@igalia.com> In-Reply-To: <20251212181254.59365-1-luis@igalia.com> References: <20251212181254.59365-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The implementation of LOOKUP_HANDLE modifies the LOOKUP operation to include an extra inarg: the file handle for the parent directory (if it is available). Also, because fuse_entry_out now has a extra variable size struct (the actual handle), it also sets the out_argvar flag to true. Most of the other modifications in this patch are a fallout from these changes: because fuse_entry_out has been modified to include a variable size struct, every operation that receives such a parameter have to take this into account: CREATE, LINK, LOOKUP, MKDIR, MKNOD, READDIRPLUS, SYMLINK, TMPFILE Signed-off-by: Luis Henriques --- fs/fuse/dev.c | 16 +++++++ fs/fuse/dir.c | 87 ++++++++++++++++++++++++++++++--------- fs/fuse/fuse_i.h | 34 +++++++++++++-- fs/fuse/inode.c | 69 +++++++++++++++++++++++++++---- fs/fuse/readdir.c | 10 ++--- include/uapi/linux/fuse.h | 8 ++++ 6 files changed, 189 insertions(+), 35 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 629e8a043079..fc6acf45ae27 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -606,6 +606,22 @@ static void fuse_adjust_compat(struct fuse_conn *fc, s= truct fuse_args *args) if (fc->minor < 4 && args->opcode =3D=3D FUSE_STATFS) args->out_args[0].size =3D FUSE_COMPAT_STATFS_SIZE; =20 + if (fc->minor < 45) { + switch (args->opcode) { + case FUSE_CREATE: + case FUSE_LINK: + case FUSE_LOOKUP: + case FUSE_MKDIR: + case FUSE_MKNOD: + /* XXX case FUSE_READDIRPLUS: */ + case FUSE_SYMLINK: + case FUSE_TMPFILE: + if (!WARN_ON_ONCE(args->in_numargs =3D=3D 0)) + args->in_numargs--; + args->out_args[0].size =3D FUSE_COMPAT_45_ENTRY_OUT_SIZE; + break; + } + } if (fc->minor < 9) { switch (args->opcode) { case FUSE_LOOKUP: diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index e3fd5d148741..a6edb444180f 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -169,7 +169,8 @@ static void fuse_invalidate_entry(struct dentry *entry) } =20 static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args, - u64 nodeid, const struct qstr *name, + u64 nodeid, struct inode *dir, + const struct qstr *name, struct fuse_entry_out *outarg) { args->opcode =3D FUSE_LOOKUP; @@ -181,8 +182,24 @@ static void fuse_lookup_init(struct fuse_conn *fc, str= uct fuse_args *args, args->in_args[2].size =3D 1; args->in_args[2].value =3D ""; args->out_numargs =3D 1; - args->out_args[0].size =3D sizeof(struct fuse_entry_out); + args->out_args[0].size =3D sizeof(*outarg) + outarg->fh.size; args->out_args[0].value =3D outarg; + + if (fc->lookup_handle) { + struct fuse_inode *fi =3D NULL; + + args->opcode =3D FUSE_LOOKUP_HANDLE; + args->out_argvar =3D true; + + if (dir) + fi =3D get_fuse_inode(dir); + + if (fi && fi->fh) { + args->in_numargs =3D 4; + args->in_args[3].size =3D sizeof(*fi->fh) + fi->fh->size; + args->in_args[3].value =3D fi->fh; + } + } } =20 /* @@ -240,7 +257,7 @@ static int fuse_dentry_revalidate(struct inode *dir, co= nst struct qstr *name, =20 attr_version =3D fuse_get_attr_version(fm->fc); =20 - fuse_lookup_init(fm->fc, &args, get_node_id(dir), + fuse_lookup_init(fm->fc, &args, get_node_id(dir), dir, name, outarg); ret =3D fuse_simple_request(fm, &args); /* Zero nodeid is same as -ENOENT */ @@ -248,7 +265,8 @@ static int fuse_dentry_revalidate(struct inode *dir, co= nst struct qstr *name, ret =3D -ENOENT; if (!ret) { fi =3D get_fuse_inode(inode); - if (outarg->nodeid !=3D get_node_id(inode) || + if (!fuse_file_handle_is_equal(fm->fc, fi->fh, &outarg->fh) || + outarg->nodeid !=3D get_node_id(inode) || (bool) IS_AUTOMOUNT(inode) !=3D (bool) (outarg->attr.flags & FUSE_A= TTR_SUBMOUNT)) { fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1); @@ -365,8 +383,9 @@ bool fuse_invalid_attr(struct fuse_attr *attr) return !fuse_valid_type(attr->mode) || !fuse_valid_size(attr->size); } =20 -int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr= *name, - struct fuse_entry_out *outarg, struct inode **inode) +int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct inode *dir, + const struct qstr *name, struct fuse_entry_out *outarg, + struct inode **inode) { struct fuse_mount *fm =3D get_fuse_mount_super(sb); FUSE_ARGS(args); @@ -388,14 +407,15 @@ int fuse_lookup_name(struct super_block *sb, u64 node= id, const struct qstr *name attr_version =3D fuse_get_attr_version(fm->fc); evict_ctr =3D fuse_get_evict_ctr(fm->fc); =20 - fuse_lookup_init(fm->fc, &args, nodeid, name, outarg); + fuse_lookup_init(fm->fc, &args, nodeid, dir, name, outarg); err =3D fuse_simple_request(fm, &args); /* Zero nodeid is same as -ENOENT, but with valid timeout */ - if (err || !outarg->nodeid) + if (err < 0 || !outarg->nodeid) // XXX err =3D size if args->out_argvar = =3D true goto out_put_forget; =20 err =3D -EIO; - if (fuse_invalid_attr(&outarg->attr)) + if (fuse_invalid_attr(&outarg->attr) || + fuse_invalid_file_handle(fm->fc, &outarg->fh)) goto out_put_forget; if (outarg->nodeid =3D=3D FUSE_ROOT_ID && outarg->generation !=3D 0) { pr_warn_once("root generation should be zero\n"); @@ -404,7 +424,8 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid= , const struct qstr *name =20 *inode =3D fuse_iget(sb, outarg->nodeid, outarg->generation, &outarg->attr, ATTR_TIMEOUT(outarg), - attr_version, evict_ctr); + attr_version, evict_ctr, + &outarg->fh); err =3D -ENOMEM; if (!*inode) { fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1); @@ -440,14 +461,14 @@ static struct dentry *fuse_lookup(struct inode *dir, = struct dentry *entry, return ERR_PTR(-ENOMEM); =20 locked =3D fuse_lock_inode(dir); - err =3D fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, + err =3D fuse_lookup_name(dir->i_sb, get_node_id(dir), dir, &entry->d_name, outarg, &inode); fuse_unlock_inode(dir, locked); if (err =3D=3D -ENOENT) { outarg_valid =3D false; err =3D 0; } - if (err) + if (err < 0) // XXX err =3D size if args->out_argvar =3D true goto out_err; =20 err =3D -EIO; @@ -689,24 +710,36 @@ static int fuse_create_open(struct mnt_idmap *idmap, = struct inode *dir, args.in_args[1].size =3D entry->d_name.len + 1; args.in_args[1].value =3D entry->d_name.name; args.out_numargs =3D 2; - args.out_args[0].size =3D sizeof(*outentry); + args.out_args[0].size =3D sizeof(*outentry) + outentry->fh.size; args.out_args[0].value =3D outentry; /* Store outarg for fuse_finish_open() */ outopenp =3D &ff->args->open_outarg; args.out_args[1].size =3D sizeof(*outopenp); args.out_args[1].value =3D outopenp; =20 + if (fm->fc->lookup_handle) { + fi =3D get_fuse_inode(dir); + args.out_argvar =3D true; + args.out_argvar_idx =3D 0; + if (fi->fh) { + args.in_numargs =3D 3; + args.in_args[2].size =3D sizeof(*fi->fh) + fi->fh->size; + args.in_args[2].value =3D fi->fh; + } + } + err =3D get_create_ext(idmap, &args, dir, entry, mode); if (err) goto out_free_outentry; =20 err =3D fuse_simple_idmap_request(idmap, fm, &args); free_ext_value(&args); - if (err) + if (err < 0) // XXX err =3D size if args->out_argvar =3D true goto out_free_outentry; =20 err =3D -EIO; if (!S_ISREG(outentry->attr.mode) || invalid_nodeid(outentry->nodeid) || + fuse_invalid_file_handle(fm->fc, &outentry->fh) || fuse_invalid_attr(&outentry->attr)) goto out_free_outentry; =20 @@ -714,7 +747,8 @@ static int fuse_create_open(struct mnt_idmap *idmap, st= ruct inode *dir, ff->nodeid =3D outentry->nodeid; ff->open_flags =3D outopenp->open_flags; inode =3D fuse_iget(dir->i_sb, outentry->nodeid, outentry->generation, - &outentry->attr, ATTR_TIMEOUT(outentry), 0, 0); + &outentry->attr, ATTR_TIMEOUT(outentry), 0, 0, + &outentry->fh); if (!inode) { flags &=3D ~(O_CREAT | O_EXCL | O_TRUNC); fuse_sync_release(NULL, ff, flags); @@ -830,9 +864,22 @@ static struct dentry *create_new_entry(struct mnt_idma= p *idmap, struct fuse_moun =20 args->nodeid =3D get_node_id(dir); args->out_numargs =3D 1; - args->out_args[0].size =3D sizeof(*outarg); + args->out_args[0].size =3D sizeof(*outarg) + outarg->fh.size; args->out_args[0].value =3D outarg; =20 + if (fm->fc->lookup_handle) { + struct fuse_inode *fi =3D get_fuse_inode(dir); + int idx =3D args->in_numargs; + + args->out_argvar =3D true; + args->out_argvar_idx =3D 0; + if (fi->fh && !WARN_ON_ONCE(idx >=3D 4)) { =09 + args->in_args[idx].size =3D sizeof(*fi->fh) + fi->fh->size; + args->in_args[idx].value =3D fi->fh; + args->in_numargs++; + } + } + if (args->opcode !=3D FUSE_LINK) { err =3D get_create_ext(idmap, args, dir, entry, mode); if (err) @@ -841,18 +888,20 @@ static struct dentry *create_new_entry(struct mnt_idm= ap *idmap, struct fuse_moun =20 err =3D fuse_simple_idmap_request(idmap, fm, args); free_ext_value(args); - if (err) + if (err < 0) // XXX err =3D size if args->out_argvar =3D true goto out_free_outarg; =20 err =3D -EIO; - if (invalid_nodeid(outarg->nodeid) || fuse_invalid_attr(&outarg->attr)) + if (invalid_nodeid(outarg->nodeid) || fuse_invalid_attr(&outarg->attr) || + fuse_invalid_file_handle(fm->fc, &outarg->fh)) goto out_free_outarg; =20 if ((outarg->attr.mode ^ mode) & S_IFMT) goto out_free_outarg; =20 inode =3D fuse_iget(dir->i_sb, outarg->nodeid, outarg->generation, - &outarg->attr, ATTR_TIMEOUT(outarg), 0, 0); + &outarg->attr, ATTR_TIMEOUT(outarg), 0, 0, + &outarg->fh); if (!inode) { fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1); kfree(outarg); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index fad05fae7e54..d0f3c81b5612 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -216,6 +216,8 @@ struct fuse_inode { * so preserve the blocksize specified by the server. */ u8 cached_i_blkbits; + + struct fuse_file_handle *fh; }; =20 /** FUSE inode state bits */ @@ -1067,6 +1069,26 @@ static inline int invalid_nodeid(u64 nodeid) return !nodeid || nodeid =3D=3D FUSE_ROOT_ID; } =20 +static inline bool fuse_invalid_file_handle(struct fuse_conn *fc, + struct fuse_file_handle *handle) +{ + if (!fc->lookup_handle) + return false; + + return !handle->size || (handle->size >=3D FUSE_MAX_HANDLE_SZ); +} + +static inline bool fuse_file_handle_is_equal(struct fuse_conn *fc, + struct fuse_file_handle *fh1, + struct fuse_file_handle *fh2) +{ + if (!fc->lookup_handle || !fh2->size || // XXX more OPs without handle + ((fh1->size =3D=3D fh2->size) && + (!memcmp(fh1->handle, fh2->handle, fh1->size)))) + return true; + return false; +} + static inline u64 fuse_get_attr_version(struct fuse_conn *fc) { return atomic64_read(&fc->attr_version); @@ -1098,7 +1120,10 @@ static inline struct fuse_entry_out *fuse_entry_out_= alloc(struct fuse_conn *fc) { struct fuse_entry_out *entryout; =20 - entryout =3D kzalloc(sizeof(*entryout), GFP_KERNEL_ACCOUNT); + entryout =3D kzalloc(sizeof(*entryout) + fc->max_handle_sz, + GFP_KERNEL_ACCOUNT); + if (entryout) + entryout->fh.size =3D fc->max_handle_sz; =20 return entryout; } @@ -1145,10 +1170,11 @@ extern const struct dentry_operations fuse_dentry_o= perations; struct inode *fuse_iget(struct super_block *sb, u64 nodeid, int generation, struct fuse_attr *attr, u64 attr_valid, u64 attr_version, - u64 evict_ctr); + u64 evict_ctr, struct fuse_file_handle *fh); =20 -int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr= *name, - struct fuse_entry_out *outarg, struct inode **inode); +int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct inode *dir, + const struct qstr *name, struct fuse_entry_out *outarg, + struct inode **inode); =20 /** * Send FORGET command diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index bc84e7ed1e3d..f565f7e8118d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -95,6 +95,25 @@ static struct fuse_submount_lookup *fuse_alloc_submount_= lookup(void) return NULL; } =20 +/* + * XXX postpone this allocation and later use the real size instead of max + */ +static bool fuse_inode_handle_alloc(struct super_block *sb, + struct fuse_inode *fi) +{ + struct fuse_conn *fc =3D get_fuse_conn_super(sb); + + fi->fh =3D NULL; + if (fc->lookup_handle) { + fi->fh =3D kzalloc(sizeof(*fi->fh) + fc->max_handle_sz, + GFP_KERNEL_ACCOUNT); + if (!fi->fh) + return false; + } + + return true; +} + static struct inode *fuse_alloc_inode(struct super_block *sb) { struct fuse_inode *fi; @@ -120,8 +139,15 @@ static struct inode *fuse_alloc_inode(struct super_blo= ck *sb) if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) fuse_inode_backing_set(fi, NULL); =20 + if (!fuse_inode_handle_alloc(sb, fi)) + goto out_free_dax; + return &fi->inode; =20 +out_free_dax: +#ifdef CONFIG_FUSE_DAX + kfree(fi->dax); +#endif out_free_forget: kfree(fi->forget); out_free: @@ -132,6 +158,7 @@ static struct inode *fuse_alloc_inode(struct super_bloc= k *sb) static void fuse_free_inode(struct inode *inode) { struct fuse_inode *fi =3D get_fuse_inode(inode); + struct fuse_conn *fc =3D get_fuse_conn(inode); =20 mutex_destroy(&fi->mutex); kfree(fi->forget); @@ -141,6 +168,9 @@ static void fuse_free_inode(struct inode *inode) if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) fuse_backing_put(fuse_inode_backing(fi)); =20 + if (fc->lookup_handle) + kfree(fi->fh); + kmem_cache_free(fuse_inode_cachep, fi); } =20 @@ -465,7 +495,7 @@ static int fuse_inode_set(struct inode *inode, void *_n= odeidp) struct inode *fuse_iget(struct super_block *sb, u64 nodeid, int generation, struct fuse_attr *attr, u64 attr_valid, u64 attr_version, - u64 evict_ctr) + u64 evict_ctr, struct fuse_file_handle *fh) { struct inode *inode; struct fuse_inode *fi; @@ -505,6 +535,30 @@ struct inode *fuse_iget(struct super_block *sb, u64 no= deid, if (!inode) return NULL; =20 + fi =3D get_fuse_inode(inode); + if (fc->lookup_handle) { + if ((fh =3D=3D NULL) && (nodeid !=3D FUSE_ROOT_ID)) { + pr_err("NULL file handle for nodeid %llu\n", nodeid); + iput(inode); + return NULL; + } + if (fi->fh->size) + pr_warn_ratelimited( + "Handle already set for nodeid %llu (size: %u)\n", + nodeid, fi->fh->size); + if (fh) { + if (fh->size >=3D fc->max_handle_sz) { + pr_err("File handle too big (%u)\n", fh->size); + iput(inode); + return NULL; + } + fi->fh->size =3D fh->size; + memcpy(fi->fh->handle, fh->handle, fi->fh->size); + } else { + fi->fh->size =3D 0; + memset(fi->fh, 0, fc->max_handle_sz); + } + } if ((inode->i_state & I_NEW)) { inode->i_flags |=3D S_NOATIME; if (!fc->writeback_cache || !S_ISREG(attr->mode)) @@ -512,7 +566,8 @@ struct inode *fuse_iget(struct super_block *sb, u64 nod= eid, inode->i_generation =3D generation; fuse_init_inode(inode, attr, fc); unlock_new_inode(inode); - } else if (fuse_stale_inode(inode, generation, attr)) { + } else if (fuse_stale_inode(inode, generation, attr) || + !fuse_file_handle_is_equal(fc, fi->fh, fh)) { /* nodeid was reused, any I/O on the old inode should fail */ fuse_make_bad(inode); if (inode !=3D d_inode(sb->s_root)) { @@ -521,7 +576,6 @@ struct inode *fuse_iget(struct super_block *sb, u64 nod= eid, goto retry; } } - fi =3D get_fuse_inode(inode); spin_lock(&fi->lock); fi->nlookup++; spin_unlock(&fi->lock); @@ -1059,7 +1113,7 @@ static struct inode *fuse_get_root_inode(struct super= _block *sb, unsigned int mo attr.mode =3D mode; attr.ino =3D FUSE_ROOT_ID; attr.nlink =3D 1; - return fuse_iget(sb, FUSE_ROOT_ID, 0, &attr, 0, 0, 0); + return fuse_iget(sb, FUSE_ROOT_ID, 0, &attr, 0, 0, 0, NULL); // XXX } =20 struct fuse_inode_handle { @@ -1092,7 +1146,7 @@ static struct dentry *fuse_get_dentry(struct super_bl= ock *sb, goto out_err; } =20 - err =3D fuse_lookup_name(sb, handle->nodeid, &name, outarg, + err =3D fuse_lookup_name(sb, handle->nodeid, NULL, &name, outarg, &inode); kfree(outarg); if (err && err !=3D -ENOENT) @@ -1199,7 +1253,7 @@ static struct dentry *fuse_get_parent(struct dentry *= child) return ERR_PTR(-ENOMEM); =20 err =3D fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), - &dotdot_name, outarg, &inode); + child_inode, &dotdot_name, outarg, &inode); kfree(outarg); if (err) { if (err =3D=3D -ENOENT) @@ -1757,8 +1811,9 @@ static int fuse_fill_super_submount(struct super_bloc= k *sb, return -ENOMEM; =20 fuse_fill_attr_from_inode(&root_attr, parent_fi); + /* XXX using parent fh */ root =3D fuse_iget(sb, parent_fi->nodeid, 0, &root_attr, 0, 0, - fuse_get_evict_ctr(fm->fc)); + fuse_get_evict_ctr(fm->fc), parent_fi->fh); /* * This inode is just a duplicate, so it is not looked up and * its nlookup should not be incremented. fuse_iget() does diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index c2aae2eef086..04fb6636c4c0 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -185,12 +185,12 @@ static int fuse_direntplus_link(struct file *file, return 0; } =20 - if (invalid_nodeid(o->nodeid)) - return -EIO; - if (fuse_invalid_attr(&o->attr)) + fc =3D get_fuse_conn(dir); + + if (invalid_nodeid(o->nodeid) || fuse_invalid_attr(&o->attr) || + fuse_invalid_file_handle(fc, &o->fh)) return -EIO; =20 - fc =3D get_fuse_conn(dir); epoch =3D atomic_read(&fc->epoch); =20 name.hash =3D full_name_hash(parent, name.name, name.len); @@ -235,7 +235,7 @@ static int fuse_direntplus_link(struct file *file, } else { inode =3D fuse_iget(dir->i_sb, o->nodeid, o->generation, &o->attr, ATTR_TIMEOUT(o), - attr_version, evict_ctr); + attr_version, evict_ctr, &o->fh); if (!inode) inode =3D ERR_PTR(-ENOMEM); =20 diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 4acf71b407c9..b75744d2d75d 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -690,6 +690,13 @@ enum fuse_notify_code { #define FUSE_MIN_READ_BUFFER 8192 =20 #define FUSE_COMPAT_ENTRY_OUT_SIZE 120 +#define FUSE_COMPAT_45_ENTRY_OUT_SIZE 128 + +struct fuse_file_handle { + uint32_t size; + uint32_t type; + char handle[0]; +}; =20 struct fuse_entry_out { uint64_t nodeid; /* Inode ID */ @@ -700,6 +707,7 @@ struct fuse_entry_out { uint32_t entry_valid_nsec; uint32_t attr_valid_nsec; struct fuse_attr attr; + struct fuse_file_handle fh; }; =20 struct fuse_forget_in { From nobody Mon Dec 15 00:32:44 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 96C192F6913; Fri, 12 Dec 2025 18:13:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563198; cv=none; b=RIyxBU8A+THt4Lp+CC//mfkLVzS1DqCGlX4OgVpxveDVna3X7ivzFLHtOYsuY5wFHJdaCzTaRD+ajhJ7tnqUqxhZ3c82lZsykiBxNKipNa5k4aYhQyzFatTwmFK5+xPrfegAJv6KH2NVvdhWJZCpSvYPPs5FUBg0kA1/OmdKjfM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563198; c=relaxed/simple; bh=U0padkSIi7nWVb5z/h/gvl9qH1RnLQWyvgJI6ouZN1s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HV6rODNq0oX2N0XSDgEkZfOnAKqfajtKOBl3iQDm8s7eSQm2Ocr+Sppc5w3ekcRuSiVWtZm27s5OkXEFF4NAkAiTNr9viNxwhbcwrE19eofXPHYQT/6iyxhepNuprIQfrqOxFcsPFvX4uuBWClRKyZT91s+SH2bh6UnSMs1dZVA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=kEVy5Y3Y; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="kEVy5Y3Y" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=5Rhscirf1twEkqup/yuNBQoBsWNxHoN+tCBpQDRwCkg=; b=kEVy5Y3Yl2HfzRnnm/or6SBWvN Xcq1sMtE8hYM6G+67TP8ioayeToGY6pab+a61dutoMfbeIqSChKNPpEkzd1R25coOSPpHCOrQf/n/ FSq0sjjuiaxjG2q51O4PBzoEB3a5+Ra9dhqTcHFQN3aMqVpfMzJQr6ql2kSni+TDUcpzBawtVS3C/ Lonwv71KnWJkYOsjC0oeJnqEraVzpWpUfiIgBzQ4rE884xOFsFh4LZGl5/jI2hv9xzZWnC/eC6HeI AFN7J/yQV+BrvKTg4nXhBhuq12+FJeEXpzeLAzlPImKZ+Ab3WCaL+FepbVSJpFN6FNPKswE0bx6FQ 5XIVNMhA==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1vU7dT-00C79Q-Gr; Fri, 12 Dec 2025 19:12:59 +0100 From: Luis Henriques To: Miklos Szeredi Cc: Amir Goldstein , "Darrick J. Wong" , Bernd Schubert , Kevin Chen , Horst Birthelmer , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matt Harvey , kernel-dev@igalia.com, Luis Henriques Subject: [RFC PATCH v2 5/6] fuse: factor out NFS export related code Date: Fri, 12 Dec 2025 18:12:53 +0000 Message-ID: <20251212181254.59365-6-luis@igalia.com> In-Reply-To: <20251212181254.59365-1-luis@igalia.com> References: <20251212181254.59365-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move all the NFS-related code into a different file. This is just preparatory work to be able to use the LOOKUP_HANDLE file handles as the NFS handles. Signed-off-by: Luis Henriques Reviewed-by: Amir Goldstein --- fs/fuse/Makefile | 2 +- fs/fuse/dir.c | 1 + fs/fuse/export.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 6 ++ fs/fuse/inode.c | 167 +-------------------------------------------- 5 files changed, 183 insertions(+), 167 deletions(-) create mode 100644 fs/fuse/export.c diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index 22ad9538dfc4..1d1401658278 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_VIRTIO_FS) +=3D virtiofs.o =20 fuse-y :=3D trace.o # put trace.o first so we see ftrace errors sooner fuse-y +=3D dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o i= octl.o -fuse-y +=3D iomode.o +fuse-y +=3D iomode.o export.o fuse-$(CONFIG_FUSE_DAX) +=3D dax.o fuse-$(CONFIG_FUSE_PASSTHROUGH) +=3D passthrough.o backing.o fuse-$(CONFIG_SYSCTL) +=3D sysctl.o diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index a6edb444180f..a885f1dc61eb 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -190,6 +190,7 @@ static void fuse_lookup_init(struct fuse_conn *fc, stru= ct fuse_args *args, =20 args->opcode =3D FUSE_LOOKUP_HANDLE; args->out_argvar =3D true; + args->out_argvar_idx =3D 0; =20 if (dir) fi =3D get_fuse_inode(dir); diff --git a/fs/fuse/export.c b/fs/fuse/export.c new file mode 100644 index 000000000000..4a9c95fe578e --- /dev/null +++ b/fs/fuse/export.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FUSE NFS export support. + * + * Copyright (C) 2001-2008 Miklos Szeredi + */ + +#include "fuse_i.h" +#include + +struct fuse_inode_handle { + u64 nodeid; + u32 generation; +}; + +static struct dentry *fuse_get_dentry(struct super_block *sb, + struct fuse_inode_handle *handle) +{ + struct fuse_conn *fc =3D get_fuse_conn_super(sb); + struct inode *inode; + struct dentry *entry; + int err =3D -ESTALE; + + if (handle->nodeid =3D=3D 0) + goto out_err; + + inode =3D ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); + if (!inode) { + struct fuse_entry_out *outarg; + const struct qstr name =3D QSTR_INIT(".", 1); + + if (!fc->export_support) + goto out_err; + + outarg =3D fuse_entry_out_alloc(fc); + if (!outarg) { + err =3D -ENOMEM; + goto out_err; + } + + err =3D fuse_lookup_name(sb, handle->nodeid, NULL, &name, outarg, + &inode); + kfree(outarg); + if (err && err !=3D -ENOENT) + goto out_err; + if (err || !inode) { + err =3D -ESTALE; + goto out_err; + } + err =3D -EIO; + if (get_node_id(inode) !=3D handle->nodeid) + goto out_iput; + } + err =3D -ESTALE; + if (inode->i_generation !=3D handle->generation) + goto out_iput; + + entry =3D d_obtain_alias(inode); + if (!IS_ERR(entry) && get_node_id(inode) !=3D FUSE_ROOT_ID) + fuse_invalidate_entry_cache(entry); + + return entry; + + out_iput: + iput(inode); + out_err: + return ERR_PTR(err); +} + +static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len, + struct inode *parent) +{ + int len =3D parent ? 6 : 3; + u64 nodeid; + u32 generation; + + if (*max_len < len) { + *max_len =3D len; + return FILEID_INVALID; + } + + nodeid =3D get_fuse_inode(inode)->nodeid; + generation =3D inode->i_generation; + + fh[0] =3D (u32)(nodeid >> 32); + fh[1] =3D (u32)(nodeid & 0xffffffff); + fh[2] =3D generation; + + if (parent) { + nodeid =3D get_fuse_inode(parent)->nodeid; + generation =3D parent->i_generation; + + fh[3] =3D (u32)(nodeid >> 32); + fh[4] =3D (u32)(nodeid & 0xffffffff); + fh[5] =3D generation; + } + + *max_len =3D len; + return parent ? FILEID_INO64_GEN_PARENT : FILEID_INO64_GEN; +} + +static struct dentry *fuse_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + struct fuse_inode_handle handle; + + if ((fh_type !=3D FILEID_INO64_GEN && + fh_type !=3D FILEID_INO64_GEN_PARENT) || fh_len < 3) + return NULL; + + handle.nodeid =3D (u64) fid->raw[0] << 32; + handle.nodeid |=3D (u64) fid->raw[1]; + handle.generation =3D fid->raw[2]; + return fuse_get_dentry(sb, &handle); +} + +static struct dentry *fuse_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + struct fuse_inode_handle parent; + + if (fh_type !=3D FILEID_INO64_GEN_PARENT || fh_len < 6) + return NULL; + + parent.nodeid =3D (u64) fid->raw[3] << 32; + parent.nodeid |=3D (u64) fid->raw[4]; + parent.generation =3D fid->raw[5]; + return fuse_get_dentry(sb, &parent); +} + +static struct dentry *fuse_get_parent(struct dentry *child) +{ + struct inode *child_inode =3D d_inode(child); + struct fuse_conn *fc =3D get_fuse_conn(child_inode); + struct inode *inode; + struct dentry *parent; + struct fuse_entry_out *outarg; + int err; + + if (!fc->export_support) + return ERR_PTR(-ESTALE); + + outarg =3D fuse_entry_out_alloc(fc); + if (!outarg) + return ERR_PTR(-ENOMEM); + + err =3D fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), + child_inode, &dotdot_name, outarg, &inode); + kfree(outarg); + if (err) { + if (err =3D=3D -ENOENT) + return ERR_PTR(-ESTALE); + return ERR_PTR(err); + } + + parent =3D d_obtain_alias(inode); + if (!IS_ERR(parent) && get_node_id(inode) !=3D FUSE_ROOT_ID) + fuse_invalidate_entry_cache(parent); + + return parent; +} + +/* only for fid encoding; no support for file handle */ +const struct export_operations fuse_export_fid_operations =3D { + .encode_fh =3D fuse_encode_fh, +}; + +const struct export_operations fuse_export_operations =3D { + .fh_to_dentry =3D fuse_fh_to_dentry, + .fh_to_parent =3D fuse_fh_to_parent, + .encode_fh =3D fuse_encode_fh, + .get_parent =3D fuse_get_parent, +}; + diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index d0f3c81b5612..b6afd909c857 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1164,6 +1164,8 @@ extern const struct file_operations fuse_dev_operatio= ns; =20 extern const struct dentry_operations fuse_dentry_operations; =20 +extern int fuse_inode_eq(struct inode *inode, void *_nodeidp); + /** * Get a filled in inode */ @@ -1647,4 +1649,8 @@ extern void fuse_sysctl_unregister(void); #define fuse_sysctl_unregister() do { } while (0) #endif /* CONFIG_SYSCTL */ =20 +/* export.c */ +extern const struct export_operations fuse_export_operations; +extern const struct export_operations fuse_export_fid_operations; + #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index f565f7e8118d..60715d6476c9 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -476,7 +475,7 @@ static void fuse_init_inode(struct inode *inode, struct= fuse_attr *attr, inode->i_acl =3D inode->i_default_acl =3D ACL_DONT_CACHE; } =20 -static int fuse_inode_eq(struct inode *inode, void *_nodeidp) +int fuse_inode_eq(struct inode *inode, void *_nodeidp) { u64 nodeid =3D *(u64 *) _nodeidp; if (get_node_id(inode) =3D=3D nodeid) @@ -1116,170 +1115,6 @@ static struct inode *fuse_get_root_inode(struct sup= er_block *sb, unsigned int mo return fuse_iget(sb, FUSE_ROOT_ID, 0, &attr, 0, 0, 0, NULL); // XXX } =20 -struct fuse_inode_handle { - u64 nodeid; - u32 generation; -}; - -static struct dentry *fuse_get_dentry(struct super_block *sb, - struct fuse_inode_handle *handle) -{ - struct fuse_conn *fc =3D get_fuse_conn_super(sb); - struct inode *inode; - struct dentry *entry; - int err =3D -ESTALE; - - if (handle->nodeid =3D=3D 0) - goto out_err; - - inode =3D ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); - if (!inode) { - struct fuse_entry_out *outarg; - const struct qstr name =3D QSTR_INIT(".", 1); - - if (!fc->export_support) - goto out_err; - - outarg =3D fuse_entry_out_alloc(fc); - if (!outarg) { - err =3D -ENOMEM; - goto out_err; - } - - err =3D fuse_lookup_name(sb, handle->nodeid, NULL, &name, outarg, - &inode); - kfree(outarg); - if (err && err !=3D -ENOENT) - goto out_err; - if (err || !inode) { - err =3D -ESTALE; - goto out_err; - } - err =3D -EIO; - if (get_node_id(inode) !=3D handle->nodeid) - goto out_iput; - } - err =3D -ESTALE; - if (inode->i_generation !=3D handle->generation) - goto out_iput; - - entry =3D d_obtain_alias(inode); - if (!IS_ERR(entry) && get_node_id(inode) !=3D FUSE_ROOT_ID) - fuse_invalidate_entry_cache(entry); - - return entry; - - out_iput: - iput(inode); - out_err: - return ERR_PTR(err); -} - -static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len, - struct inode *parent) -{ - int len =3D parent ? 6 : 3; - u64 nodeid; - u32 generation; - - if (*max_len < len) { - *max_len =3D len; - return FILEID_INVALID; - } - - nodeid =3D get_fuse_inode(inode)->nodeid; - generation =3D inode->i_generation; - - fh[0] =3D (u32)(nodeid >> 32); - fh[1] =3D (u32)(nodeid & 0xffffffff); - fh[2] =3D generation; - - if (parent) { - nodeid =3D get_fuse_inode(parent)->nodeid; - generation =3D parent->i_generation; - - fh[3] =3D (u32)(nodeid >> 32); - fh[4] =3D (u32)(nodeid & 0xffffffff); - fh[5] =3D generation; - } - - *max_len =3D len; - return parent ? FILEID_INO64_GEN_PARENT : FILEID_INO64_GEN; -} - -static struct dentry *fuse_fh_to_dentry(struct super_block *sb, - struct fid *fid, int fh_len, int fh_type) -{ - struct fuse_inode_handle handle; - - if ((fh_type !=3D FILEID_INO64_GEN && - fh_type !=3D FILEID_INO64_GEN_PARENT) || fh_len < 3) - return NULL; - - handle.nodeid =3D (u64) fid->raw[0] << 32; - handle.nodeid |=3D (u64) fid->raw[1]; - handle.generation =3D fid->raw[2]; - return fuse_get_dentry(sb, &handle); -} - -static struct dentry *fuse_fh_to_parent(struct super_block *sb, - struct fid *fid, int fh_len, int fh_type) -{ - struct fuse_inode_handle parent; - - if (fh_type !=3D FILEID_INO64_GEN_PARENT || fh_len < 6) - return NULL; - - parent.nodeid =3D (u64) fid->raw[3] << 32; - parent.nodeid |=3D (u64) fid->raw[4]; - parent.generation =3D fid->raw[5]; - return fuse_get_dentry(sb, &parent); -} - -static struct dentry *fuse_get_parent(struct dentry *child) -{ - struct inode *child_inode =3D d_inode(child); - struct fuse_conn *fc =3D get_fuse_conn(child_inode); - struct inode *inode; - struct dentry *parent; - struct fuse_entry_out *outarg; - int err; - - if (!fc->export_support) - return ERR_PTR(-ESTALE); - - outarg =3D fuse_entry_out_alloc(fc); - if (!outarg) - return ERR_PTR(-ENOMEM); - - err =3D fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), - child_inode, &dotdot_name, outarg, &inode); - kfree(outarg); - if (err) { - if (err =3D=3D -ENOENT) - return ERR_PTR(-ESTALE); - return ERR_PTR(err); - } - - parent =3D d_obtain_alias(inode); - if (!IS_ERR(parent) && get_node_id(inode) !=3D FUSE_ROOT_ID) - fuse_invalidate_entry_cache(parent); - - return parent; -} - -/* only for fid encoding; no support for file handle */ -static const struct export_operations fuse_export_fid_operations =3D { - .encode_fh =3D fuse_encode_fh, -}; - -static const struct export_operations fuse_export_operations =3D { - .fh_to_dentry =3D fuse_fh_to_dentry, - .fh_to_parent =3D fuse_fh_to_parent, - .encode_fh =3D fuse_encode_fh, - .get_parent =3D fuse_get_parent, -}; - static const struct super_operations fuse_super_operations =3D { .alloc_inode =3D fuse_alloc_inode, .free_inode =3D fuse_free_inode, From nobody Mon Dec 15 00:32:44 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 013E11C860B; Fri, 12 Dec 2025 18:13:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563195; cv=none; b=d6bO++LOTjixgxWC7wBIFjzQMBLdBcRe9n38p1M+PJQDnavuKjM7oaLV1tKIXQFtxNyQ2JJhc4kNV8PTzdSDfGlHRRlLHFEqMCaRn4ra7x9/x9G/7G56B6TX5vkVByzdYSVfX0HuHR89SxDnY8AlZzm5I8PFR5cm5oZn22k4SpI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765563195; c=relaxed/simple; bh=QgPc7rTiFtExsyCw6e54RWJk8iRZfxNb4KhYrBVHGXk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HTgmSSfqKxnwY+5KOxBebhQKqcwDr69lyrXYB3x5envlY25c3Anufn0fSkk16a/w2HIiPhcLpsAXhUUC20cZ9wugjmTEZZD2+or4JiH+3jACMfSGFB24k6Zzyictwtxlcb7nFu6/XCzyvO15Qn35mLUkkohGDqmyG4bHJFmaRyc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=scpjwKKT; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="scpjwKKT" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=bzHcDz2DUIi7I/EbwBbO3JlJ8/P5L8jnGcFP6eoP2ug=; b=scpjwKKTSzBjQ/i1xsHvQnUQEW q03/af846pzOfF602y6O/y74+1LEHSx0LUZpNJwT4LXMK9AL+YEXy2YvU0a4NFz9CPiV4pgAWBGWM pm+7voaKlQ2OzevL9o7eUwXyBmGTMqPPBVFO8DtXw4Qcs7NoDiZCImaoab65+6xFoHUtWKGDjz3J7 GVSjplRFjAmC8C1EQEghFlXuX6EC5p+P4ES348z1gsWgIVa9B8uFT+9IyXgdfp27G+cmeu3Lv/apC r5byNOaCKDtnAJt9pXvopgRlIaVAwqkDQF4nhpjGJnZDUhd58fxd61XDV1ZQYBKY+Jy5/cgI9MPuF 3ulRn6Bg==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1vU7dU-00C79U-4N; Fri, 12 Dec 2025 19:13:00 +0100 From: Luis Henriques To: Miklos Szeredi Cc: Amir Goldstein , "Darrick J. Wong" , Bernd Schubert , Kevin Chen , Horst Birthelmer , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Matt Harvey , kernel-dev@igalia.com, Luis Henriques Subject: [RFC PATCH v2 6/6] fuse: implementation of export_operations with FUSE_LOOKUP_HANDLE Date: Fri, 12 Dec 2025 18:12:54 +0000 Message-ID: <20251212181254.59365-7-luis@igalia.com> In-Reply-To: <20251212181254.59365-1-luis@igalia.com> References: <20251212181254.59365-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch allows the NFS handle to use the new file handle provided by the LOOKUP_HANDLE operation. It still allows the usage of nodeid+generation as an handle if this operation is not supported by the FUSE server or if no handle is available for a specific inode. I.e. it can mix both file handle types FILEID_INO64_GEN{_PARENT} and FILEID_FUSE_WITH{OUT}_PARENT. Signed-off-by: Luis Henriques --- fs/fuse/export.c | 162 ++++++++++++++++++++++++++++++++++++--- include/linux/exportfs.h | 7 ++ 2 files changed, 160 insertions(+), 9 deletions(-) diff --git a/fs/fuse/export.c b/fs/fuse/export.c index 4a9c95fe578e..b40d146a32f2 100644 --- a/fs/fuse/export.c +++ b/fs/fuse/export.c @@ -3,6 +3,7 @@ * FUSE NFS export support. * * Copyright (C) 2001-2008 Miklos Szeredi + * Copyright (C) 2025 Jump Trading LLC, author: Luis Henriques */ =20 #include "fuse_i.h" @@ -10,7 +11,8 @@ =20 struct fuse_inode_handle { u64 nodeid; - u32 generation; + u32 generation; /* XXX change to u64, and use fid->i64.ino in encode/deco= de? */ + struct fuse_file_handle fh; }; =20 static struct dentry *fuse_get_dentry(struct super_block *sb, @@ -67,8 +69,8 @@ static struct dentry *fuse_get_dentry(struct super_block = *sb, return ERR_PTR(err); } =20 -static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len, - struct inode *parent) +static int fuse_encode_gen_fh(struct inode *inode, u32 *fh, int *max_len, + struct inode *parent) { int len =3D parent ? 6 : 3; u64 nodeid; @@ -96,38 +98,180 @@ static int fuse_encode_fh(struct inode *inode, u32 *fh= , int *max_len, } =20 *max_len =3D len; + return parent ? FILEID_INO64_GEN_PARENT : FILEID_INO64_GEN; } =20 -static struct dentry *fuse_fh_to_dentry(struct super_block *sb, - struct fid *fid, int fh_len, int fh_type) +static int fuse_encode_fuse_fh(struct inode *inode, u32 *fh, int *max_len, + struct inode *parent) +{ + struct fuse_inode *fi =3D get_fuse_inode(inode); + struct fuse_inode *fip =3D NULL; + struct fuse_inode_handle *handle =3D (void *)fh; + int type =3D FILEID_FUSE_WITHOUT_PARENT; + int len, lenp =3D 0; + int buflen =3D *max_len << 2; /* max_len: number of words */ + + len =3D sizeof(struct fuse_inode_handle) + fi->fh->size; + if (parent) { + fip =3D get_fuse_inode(parent); + if (fip->fh && fip->fh->size) { + lenp =3D sizeof(struct fuse_inode_handle) + + fip->fh->size; + type =3D FILEID_FUSE_WITH_PARENT; + } + } + + if (buflen < (len + lenp)) { + *max_len =3D (len + lenp) >> 2; + return FILEID_INVALID; + } + + handle[0].nodeid =3D fi->nodeid; + handle[0].generation =3D inode->i_generation; + memcpy(&handle[0].fh, fi->fh, len); + if (lenp) { + handle[1].nodeid =3D fip->nodeid; + handle[1].generation =3D parent->i_generation; + memcpy(&handle[1].fh, fip->fh, lenp); + } + + *max_len =3D (len + lenp) >> 2; + + return type; +} + +static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len, + struct inode *parent) +{ + struct fuse_conn *fc =3D get_fuse_conn(inode); + struct fuse_inode *fi =3D get_fuse_inode(inode); + + if (fc->lookup_handle && fi->fh && fi->fh->size) + return fuse_encode_fuse_fh(inode, fh, max_len, parent); + + return fuse_encode_gen_fh(inode, fh, max_len, parent); +} + +static struct dentry *fuse_fh_gen_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len) { struct fuse_inode_handle handle; =20 - if ((fh_type !=3D FILEID_INO64_GEN && - fh_type !=3D FILEID_INO64_GEN_PARENT) || fh_len < 3) + if (fh_len < 3) return NULL; =20 handle.nodeid =3D (u64) fid->raw[0] << 32; handle.nodeid |=3D (u64) fid->raw[1]; handle.generation =3D fid->raw[2]; + return fuse_get_dentry(sb, &handle); } =20 -static struct dentry *fuse_fh_to_parent(struct super_block *sb, +static struct dentry *fuse_fh_fuse_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len) +{ + struct fuse_inode_handle *handle; + struct dentry *dentry; + int len =3D sizeof(struct fuse_file_handle); + + handle =3D (void *)fid; + len +=3D handle->fh.size; + + if ((fh_len << 2) < len) + return NULL; + + handle =3D kzalloc(len, GFP_KERNEL); + if (!handle) + return NULL; + + memcpy(handle, fid, len); + + dentry =3D fuse_get_dentry(sb, handle); + kfree(handle); + + return dentry; +} + +static struct dentry *fuse_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) +{ + switch (fh_type) { + case FILEID_INO64_GEN: + case FILEID_INO64_GEN_PARENT: + return fuse_fh_gen_to_dentry(sb, fid, fh_len); + case FILEID_FUSE_WITHOUT_PARENT: + case FILEID_FUSE_WITH_PARENT: + return fuse_fh_fuse_to_dentry(sb, fid, fh_len); + } + + return NULL; + +} + +static struct dentry *fuse_fh_gen_to_parent(struct super_block *sb, + struct fid *fid, int fh_len) { struct fuse_inode_handle parent; =20 - if (fh_type !=3D FILEID_INO64_GEN_PARENT || fh_len < 6) + if (fh_len < 6) return NULL; =20 parent.nodeid =3D (u64) fid->raw[3] << 32; parent.nodeid |=3D (u64) fid->raw[4]; parent.generation =3D fid->raw[5]; + return fuse_get_dentry(sb, &parent); } =20 +static struct dentry *fuse_fh_fuse_to_parent(struct super_block *sb, + struct fid *fid, int fh_len) +{ + struct fuse_inode_handle *handle; + struct dentry *dentry; + int total_len; + int len; + + handle =3D (void *)fid; + total_len =3D len =3D sizeof(struct fuse_inode_handle) + handle->fh.size; + + if ((fh_len << 2) < total_len) + return NULL; + + handle =3D (void *)(fid + len); + len =3D sizeof(struct fuse_file_handle) + handle->fh.size; + total_len +=3D len; + + if ((fh_len << 2) < total_len) + return NULL; +=09 + handle =3D kzalloc(len, GFP_KERNEL); + if (!handle) + return NULL; + + memcpy(handle, fid, len); + + dentry =3D fuse_get_dentry(sb, handle); + kfree(handle); + + return dentry; +} + +static struct dentry *fuse_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + switch (fh_type) { + case FILEID_INO64_GEN: + case FILEID_INO64_GEN_PARENT: + return fuse_fh_gen_to_parent(sb, fid, fh_len); + case FILEID_FUSE_WITHOUT_PARENT: + case FILEID_FUSE_WITH_PARENT: + return fuse_fh_fuse_to_parent(sb, fid, fh_len); + } + + return NULL; +} + static struct dentry *fuse_get_parent(struct dentry *child) { struct inode *child_inode =3D d_inode(child); diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index f0cf2714ec52..db783f6b28bc 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h @@ -110,6 +110,13 @@ enum fid_type { */ FILEID_INO64_GEN_PARENT =3D 0x82, =20 + /* + * 64 bit nodeid number, 32 bit generation number, + * variable length handle. + */ + FILEID_FUSE_WITHOUT_PARENT =3D 0x91, + FILEID_FUSE_WITH_PARENT =3D 0x92, + /* * 128 bit child FID (struct lu_fid) * 128 bit parent FID (struct lu_fid)