From: NeilBrown <neilb@ownmail.net>
To: Chuck Lever <chuck.lever@oracle.com>, Jeff Layton <jlayton@kernel.org>
Cc: Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
linux-nfs@vger.kernel.org
Subject: [PATCH v3 02/17] nfsd: correctly handle CREATE of mounted-on files
Date: Mon, 13 Jul 2026 16:15:25 +1000 [thread overview]
Message-ID: <20260713062219.6399-3-neilb@ownmail.net> (raw)
In-Reply-To: <20260713062219.6399-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
Linux allows a file (non-directory) to be mounted on a file. nfsd
mostly supports this if the crossmnt option is in effect. However if
CREATE is used on an existing mounted-on file, the filehandle for the
underlying file is returns. The client will then continue to use that
filehandle.
So
cat /mnt/file
will show the contents of the mounted file as expected, but if
the dcache is flushed with "drop_caches" or similar, then
>> /mnt/file
cat /mnt/file
will show the mounted-on file.
For exclusive or checked creates this is not a problem as the creation
will fail no matter which file is seen. For unchecked creates we need to
see if the name is in the dcache, and if it is mounted. If so, we
simply provide that filehandle, possibly truncating.
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/nfsd/nfs3proc.c | 28 ++++++++++++++++++++++++++++
fs/nfsd/nfs4proc.c | 30 ++++++++++++++++++++++++++++++
fs/nfsd/nfsproc.c | 24 +++++++++++++++++++++++-
3 files changed, 81 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index bbaef884f893..20eaf56fa9e7 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -303,6 +303,34 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
parent = fhp->fh_dentry;
inode = d_inode(parent);
+ if (argp->createmode == NFS3_CREATE_UNCHECKED) {
+ /*
+ * If name is already in dcache we need to check for mountpoints
+ */
+ child = try_lookup_noperm(&QSTR_LEN(argp->name,
+ argp->len),
+ parent);
+ if (child && !IS_ERR(child) && d_is_reg(child) &&
+ unlikely(nfsd_mountpoint(child, fhp->fh_export))) {
+ struct svc_export *exp = exp_get(fhp->fh_export);
+ if (nfsd_cross_mnt(rqstp, &child, &exp) == 0) {
+ status = check_nfsd_access(exp, rqstp, false);
+ if (status == nfs_ok)
+ status = fh_compose(resfhp, exp,
+ child, fhp);
+ if (status == nfs_ok)
+ status = nfsd_create_setattr(
+ rqstp, fhp, resfhp, &attrs);
+ dput(child);
+ exp_put(exp);
+ return status;
+ }
+ exp_put(exp);
+ }
+ if (!IS_ERR(child))
+ dput(child);
+ }
+
host_err = fh_want_write(fhp);
if (host_err)
return nfserrno(host_err);
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index ca9460e97e2b..9a8c1e37cc0f 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -270,6 +270,36 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
parent = fhp->fh_dentry;
inode = d_inode(parent);
+ if (open->op_createmode == NFS4_CREATE_UNCHECKED) {
+ /*
+ * If name is already in dcache we need to check for mountpoints
+ */
+ child = try_lookup_noperm(&QSTR_LEN(open->op_fname,
+ open->op_fnamelen),
+ parent);
+ if (child && !IS_ERR(child) && d_is_reg(child) &&
+ unlikely(nfsd_mountpoint(child, fhp->fh_export))) {
+ struct svc_export *exp = exp_get(fhp->fh_export);
+ if (nfsd_cross_mnt(rqstp, &child, &exp) == 0) {
+ status = check_nfsd_access(exp, rqstp, false);
+ if (status == nfs_ok)
+ status = fh_compose(resfhp, exp,
+ child, fhp);
+ if (status == nfs_ok)
+ status = fh_fill_both_attrs(fhp);
+ open->op_truncate =
+ (iap->ia_valid & ATTR_SIZE) &&
+ !iap->ia_size;
+ dput(child);
+ exp_put(exp);
+ return status;
+ }
+ exp_put(exp);
+ }
+ if (!IS_ERR(child))
+ dput(child);
+ }
+
host_err = fh_want_write(fhp);
if (host_err)
return nfserrno(host_err);
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index f60043632575..549eed8f2c19 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -302,11 +302,34 @@ nfsd_proc_create(struct svc_rqst *rqstp)
if (resp->status != nfs_ok)
goto done; /* must fh_put dirfhp even on error */
+ fh_init(newfhp, NFS_FHSIZE);
+
/* Check for NFSD_MAY_WRITE in nfsd_create if necessary */
resp->status = nfserr_exist;
if (name_is_dot_dotdot(argp->name, argp->len))
goto done;
+
+ /*
+ * If name is already in dcache we need to check for mountpoints
+ */
+ dchild = try_lookup_noperm(&QSTR_LEN(argp->name, argp->len),
+ dirfhp->fh_export);
+ if (dchild && !IS_ERR(dchild) && d_is_reg(child) &&
+ unlikely(nfsd_mountpoint(dchild, fhp->fh_export))) {
+ struct svc_export *exp = fhp->fh_export;
+ if (nfsd_cross_mnt(rqstp, &dchild, &exp) == 0 &&
+ d_isreg(dchild)) {
+ resp->status = check_nfsd_access(exp, rqstp, false);
+ if (resp->status == nfs_ok)
+ resp->status = fh_compose(newfhp, dirfhp->fh_export,
+ dchild, dirfhp);
+ goto done;
+ }
+ }
+ if (!IS_ERR(dchild))
+ dput(dchild);
+
hosterr = fh_want_write(dirfhp);
if (hosterr) {
resp->status = nfserrno(hosterr);
@@ -319,7 +342,6 @@ nfsd_proc_create(struct svc_rqst *rqstp)
resp->status = nfserrno(PTR_ERR(dchild));
goto out_write;
}
- fh_init(newfhp, NFS_FHSIZE);
resp->status = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp);
if (!resp->status && d_really_is_negative(dchild))
resp->status = nfserr_noent;
--
2.50.0.107.gf914562f5916.dirty
next prev parent reply other threads:[~2026-07-13 6:22 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-13 6:15 [PATCH v3 00/17] nfsd: refactor nfs4_create_file() NeilBrown
2026-07-13 6:15 ` [PATCH v3 01/17] nfsd: honour client-provided attributes for NFS4_CREATE_EXCLUSIVE4_1 NeilBrown
2026-07-13 6:15 ` NeilBrown [this message]
2026-07-13 13:38 ` [PATCH v3 02/17] nfsd: correctly handle CREATE of mounted-on files Chuck Lever
2026-07-13 21:46 ` NeilBrown
2026-07-13 6:15 ` [PATCH v3 03/17] nfsd: replace fh_fill_both_attrs() with fh_fill_post_noop() NeilBrown
2026-07-13 6:15 ` [PATCH v3 04/17] nfsd: move fh_want_write() after preamble in nfsd4_create_file() NeilBrown
2026-07-13 6:15 ` [PATCH v3 05/17] nfsd: move more nfs-specific code into preamble of nfsd4_create_file() NeilBrown
2026-07-13 6:15 ` [PATCH v3 06/17] nfsd: remove subtlety from nfsd4_create_file() NeilBrown
2026-07-13 6:15 ` [PATCH v3 07/17] nfsd: in nfsd4_create_file() let VFS report if file was created NeilBrown
2026-07-13 6:15 ` [PATCH v3 08/17] nfsd: nfsd4_create_file(): Move NFSD_MAY_CREATE check earlier NeilBrown
2026-07-13 6:15 ` [PATCH v3 09/17] nfsd: fh_want_write) failure need not be immediately fatal for nfsd4_create_file() NeilBrown
2026-07-13 6:15 ` [PATCH v3 10/17] nfsd: (almost) always open file in nfsd4_create_file() NeilBrown
2026-07-13 6:15 ` [PATCH v3 11/17] nfsd: reduce range of directory lock " NeilBrown
2026-07-13 6:15 ` [PATCH v3 12/17] nfsd: open-code nfsd4_vfs_create() into nfsd4_create_file() NeilBrown
2026-07-13 6:15 ` [PATCH v3 13/17] nfsd: move some code out of the d_really_is_negative() branch in nfsd4_create_file() NeilBrown
2026-07-13 6:15 ` [PATCH v3 14/17] nfsd: reduce want-write range " NeilBrown
2026-07-16 12:29 ` Jeff Layton
2026-07-13 6:15 ` [PATCH v3 15/17] nfsd: move v0 checking out of nfsd_check_obj_isreg() NeilBrown
2026-07-16 12:31 ` Jeff Layton
2026-07-13 6:15 ` [PATCH v3 16/17] nfsd: separate out VFS-specific code from nfsd4_create_file() NeilBrown
2026-07-13 6:15 ` [PATCH v3 17/17] nfsd: use do_lookup_open() for non-creating open requests too NeilBrown
2026-07-16 13:29 ` [PATCH v3 00/17] nfsd: refactor nfs4_create_file() Jeff Layton
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260713062219.6399-3-neilb@ownmail.net \
--to=neilb@ownmail.net \
--cc=Dai.Ngo@oracle.com \
--cc=chuck.lever@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox