From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 DAA8B32C924; Wed, 22 Apr 2026 23:30:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776900643; cv=none; b=FggZuE3JIZMmBM7Avj+SWKu5kQqMND2Fr3EgQ13QGwDY+TQGxGYvTRKzygIVHGIaN3LDgO/jd8+ORz/FHT5Ic4eLEIR1cfNnjYGSHUFbMfPFYg+oOtFVIf1XSDnkVSiIZ83KfpfYyoZ0NuEU08TiX7BIQfeVTlwns5qg9SW7MlQ= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776900643; c=relaxed/simple; bh=GR9z0DkqTplRpepB/Qln3uG86o4mVdQ9KP8Tv3A17ss=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bEx0cByR5s05nNJ6lBaMXP+Gc6u01RZ4tnUPhYBPjCZe/0K2A2bDL/f1+Wh5nGodYDTakLSDSvzaNLpPW8qR51rH1iigtGSxVOAQmwTyJv3FRbg7h+R4JN453y8mzAhw4keM2SXkW5DKB7NxYGZ109hF9+PHHqA2QkKPjuzQ7vY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=anGo91ms; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="anGo91ms" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 554FBC19425; Wed, 22 Apr 2026 23:30:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776900643; bh=GR9z0DkqTplRpepB/Qln3uG86o4mVdQ9KP8Tv3A17ss=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=anGo91mst4wzlfqq26E5sln7GLGnW8o5qG/gwOZxP2J7zshyoudLxL++2AQiCuNiM j7IUdWAudmDR77erwBlZVYeVDcUkm0kxx3wo10DD9PPk+wf7axffRykmQFg0eCbafY KH7xVwP+hk7TBc9vqe5oPOVsIcoE9UjTpvDm1s7YIEQxy/7EyDUhj7HUReuRCX0XSM 70V9XBgznnLpcZRVYlHcReEREWlW4HVka8w7sq39l0rlueBcIaL8+CKccuIIbKfw5S FCr7kHe6EKkIMCRnyFyo27fTHQ5WzxZffCA4Xel44xYLgsWd1xcVvMCl7rV4999kPl 1RLcuU1VmAw9g== From: Chuck Lever Date: Wed, 22 Apr 2026 19:30:05 -0400 Subject: [PATCH v9 11/17] nfs: Implement fileattr_get for case sensitivity Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260422-case-sensitivity-v9-11-be023cc070e2@oracle.com> References: <20260422-case-sensitivity-v9-0-be023cc070e2@oracle.com> In-Reply-To: <20260422-case-sensitivity-v9-0-be023cc070e2@oracle.com> To: Al Viro , Christian Brauner , Jan Kara Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-xfs@vger.kernel.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-api@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, hirofumi@mail.parknet.co.jp, linkinjeon@kernel.org, sj1557.seo@samsung.com, yuezhang.mo@sony.com, almaz.alexandrovich@paragon-software.com, slava@dubeyko.com, glaubitz@physik.fu-berlin.de, frank.li@vivo.com, tytso@mit.edu, adilger.kernel@dilger.ca, cem@kernel.org, sfrench@samba.org, pc@manguebit.org, ronniesahlberg@gmail.com, sprasad@microsoft.com, trondmy@kernel.org, anna@kernel.org, jaegeuk@kernel.org, chao@kernel.org, hansg@kernel.org, senozhatsky@chromium.org, Chuck Lever X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7886; i=chuck.lever@oracle.com; h=from:subject:message-id; bh=M73da8cGzQrZTcM0rxcg8uMx/wOdiZ+atXvp65Lr5YA=; b=owEBbQKS/ZANAwAKATNqszNvZn+XAcsmYgBp6VoGlFy7S8BEEemUX4nRLOCN16C35+KOE1vIl Bdr3+9snSCJAjMEAAEKAB0WIQQosuWwEobfJDzyPv4zarMzb2Z/lwUCaelaBgAKCRAzarMzb2Z/ l14BEACMAEXpq+VoHoopeiTvvyH4TDrocynd7u+rW+p8mn7VsCojLWcGrOXT1YIr92EmvQy51Q3 ympKacZ5H6+wOzaij4v3+ySrfF/bQ0WmHiJXRZctf9s5BfmnV/Gq9f3KR62XDfa3x3THCsHZtHf kBxfq+sJsSvP5+dhOGwvkH1+n1xRNyM62yLSuWZxwpPNj8oSFswAWbi5UegPBYvFvSKlltTs4Pp zD3aiPZrFg8E/lHj5hzaOsTzY4K4DvSRRIDadZ4eFt3DsjKJ3gzfzUP9Vh0kerQe30DOcsP37kl GRfpuu3vcbyYxF9nGUe2W8/kGsKkIIN8m1hwbGeCgmtPMokv96M7aG2ydsotFsmffcramoKVDbH CNsqf57f9caukeGG1QTpMUQXPkaplD8tPGJMrREld1P14ZUAKPFdIM+aaDTMiafs6una4Npqhtj lIOhXYxxtblZWoSEjefp1TBiNCnEHhDdknTLQ6hxqUX/LamkkCmMtdw36ik8sIiHM5rosNrMSi3 APpI1G6rlE6Lv8cBSAiadtNOiWhcHIQ8lty3Y4zJuxjEi9pzrIIbufoPkuk8WtDzW3tSsjtQ+FH dB7QA9GSGIQ49SZKdNSKj4jz1qGSMuNSa+x0WsgHl9ZRB6Js1lldji2PQYjOrl841Zn5+LCC/1Q ozPplU+x7f0LOEw== X-Developer-Key: i=chuck.lever@oracle.com; a=openpgp; fpr=28B2E5B01286DF243CF23EFE336AB3336F667F97 From: Chuck Lever An NFS server re-exporting an NFS mount point needs to report the case sensitivity behavior of the underlying filesystem to its clients. Without this, re-export servers cannot accurately convey case handling semantics, potentially causing client applications to make incorrect assumptions about filename collisions and lookups. The NFS client already retrieves case sensitivity information from servers during mount via PATHCONF (NFSv3) or the FATTR4_CASE_INSENSITIVE/FATTR4_CASE_PRESERVING attributes (NFSv4). Expose this information through fileattr_get by reporting the FS_XFLAG_CASEFOLD and FS_XFLAG_CASENONPRESERVING flags. NFSv2 lacks PATHCONF support, so mounts using that protocol version default to standard POSIX behavior: case-sensitive and case-preserving. Signed-off-by: Chuck Lever --- fs/nfs/client.c | 9 +++++++-- fs/nfs/inode.c | 21 +++++++++++++++++++++ fs/nfs/internal.h | 3 +++ fs/nfs/nfs3proc.c | 2 ++ fs/nfs/nfs3xdr.c | 7 +++++-- fs/nfs/nfs4proc.c | 2 ++ fs/nfs/proc.c | 3 +++ fs/nfs/symlink.c | 3 +++ include/linux/nfs_xdr.h | 2 ++ 9 files changed, 48 insertions(+), 4 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index be02bb227741..1b588d944598 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -935,13 +935,18 @@ static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, str /* Get some general file system info */ if (server->namelen == 0) { - struct nfs_pathconf pathinfo; + struct nfs_pathconf pathinfo = { }; pathinfo.fattr = fattr; nfs_fattr_init(fattr); - if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) + if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) { server->namelen = pathinfo.max_namelen; + if (pathinfo.case_insensitive) + server->caps |= NFS_CAP_CASE_INSENSITIVE; + if (pathinfo.case_preserving) + server->caps |= NFS_CAP_CASE_PRESERVING; + } } if (clp->rpc_ops->discover_trunking != NULL && diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 98a8f0de1199..e148e459f689 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "nfs4_fs.h" #include "callback.h" @@ -1101,6 +1102,26 @@ int nfs_getattr(struct mnt_idmap *idmap, const struct path *path, } EXPORT_SYMBOL_GPL(nfs_getattr); +int nfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa) +{ + struct inode *inode = d_inode(dentry); + + /* + * Case handling is a property of the exported filesystem on the + * NFS server, reported to the client at mount via PATHCONF + * (NFSv3) or FATTR4_CASE_INSENSITIVE / FATTR4_CASE_PRESERVING + * (NFSv4). Unlike filesystems that always preserve case, an NFS + * mount may front a backend that does not, so both flags can + * appear. + */ + if (nfs_server_capable(inode, NFS_CAP_CASE_INSENSITIVE)) + fa->fsx_xflags |= FS_XFLAG_CASEFOLD; + if (!nfs_server_capable(inode, NFS_CAP_CASE_PRESERVING)) + fa->fsx_xflags |= FS_XFLAG_CASENONPRESERVING; + return 0; +} +EXPORT_SYMBOL_GPL(nfs_fileattr_get); + static void nfs_init_lock_context(struct nfs_lock_context *l_ctx) { refcount_set(&l_ctx->count, 1); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index fc5456377160..309d3f679bb3 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -449,6 +449,9 @@ extern void nfs_set_cache_invalid(struct inode *inode, unsigned long flags); extern bool nfs_check_cache_invalid(struct inode *, unsigned long); extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode); +struct file_kattr; +int nfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa); + #if IS_ENABLED(CONFIG_NFS_LOCALIO) /* localio.c */ struct nfs_local_dio { diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 95d7cd564b74..b80d0c5efc27 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -1053,6 +1053,7 @@ static const struct inode_operations nfs3_dir_inode_operations = { .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, + .fileattr_get = nfs_fileattr_get, #ifdef CONFIG_NFS_V3_ACL .listxattr = nfs3_listxattr, .get_inode_acl = nfs3_get_acl, @@ -1064,6 +1065,7 @@ static const struct inode_operations nfs3_file_inode_operations = { .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, + .fileattr_get = nfs_fileattr_get, #ifdef CONFIG_NFS_V3_ACL .listxattr = nfs3_listxattr, .get_inode_acl = nfs3_get_acl, diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index e17d72908412..e745e78faab0 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -2276,8 +2276,11 @@ static int decode_pathconf3resok(struct xdr_stream *xdr, if (unlikely(!p)) return -EIO; result->max_link = be32_to_cpup(p++); - result->max_namelen = be32_to_cpup(p); - /* ignore remaining fields */ + result->max_namelen = be32_to_cpup(p++); + p++; /* ignore no_trunc */ + p++; /* ignore chown_restricted */ + result->case_insensitive = be32_to_cpup(p++) != 0; + result->case_preserving = be32_to_cpup(p) != 0; return 0; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d839a97df822..507b74c406f2 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -10598,6 +10598,7 @@ static const struct inode_operations nfs4_dir_inode_operations = { .getattr = nfs_getattr, .setattr = nfs_setattr, .listxattr = nfs4_listxattr, + .fileattr_get = nfs_fileattr_get, }; static const struct inode_operations nfs4_file_inode_operations = { @@ -10605,6 +10606,7 @@ static const struct inode_operations nfs4_file_inode_operations = { .getattr = nfs_getattr, .setattr = nfs_setattr, .listxattr = nfs4_listxattr, + .fileattr_get = nfs_fileattr_get, }; static struct nfs_server *nfs4_clone_server(struct nfs_server *source, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 70795684b8e8..03c2c1f31be9 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -598,6 +598,7 @@ nfs_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, { info->max_link = 0; info->max_namelen = NFS2_MAXNAMLEN; + info->case_preserving = true; return 0; } @@ -718,12 +719,14 @@ static const struct inode_operations nfs_dir_inode_operations = { .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, + .fileattr_get = nfs_fileattr_get, }; static const struct inode_operations nfs_file_inode_operations = { .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, + .fileattr_get = nfs_fileattr_get, }; const struct nfs_rpc_ops nfs_v2_clientops = { diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 58146e935402..74a072896f8d 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -22,6 +22,8 @@ #include #include +#include "internal.h" + /* Symlink caching in the page cache is even more simplistic * and straight-forward than readdir caching. */ @@ -74,4 +76,5 @@ const struct inode_operations nfs_symlink_inode_operations = { .get_link = nfs_get_link, .getattr = nfs_getattr, .setattr = nfs_setattr, + .fileattr_get = nfs_fileattr_get, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index ff1f12aa73d2..7c2057e40f99 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -182,6 +182,8 @@ struct nfs_pathconf { struct nfs_fattr *fattr; /* Post-op attributes */ __u32 max_link; /* max # of hard links */ __u32 max_namelen; /* max name length */ + bool case_insensitive; + bool case_preserving; }; struct nfs4_change_info { -- 2.53.0