[PATCH v5 03/18] nfsd: correctly handle CREATE of mounted-on files
NeilBrown <[email protected]> Fri, 17 Jul 2026 19:27:51 +1000
| Newsgroups | gmane.linux.nfs |
|---|---|
| Message-ID | <[email protected]> |
From: NeilBrown <neil-+NVA1uvv1dVBDLzU/[email protected]> 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. This probably has always existed since before the git history. Signed-off-by: NeilBrown <neil-+NVA1uvv1dVBDLzU/[email protected]> --- fs/nfsd/nfs3proc.c | 19 ++++++++++++++++++- fs/nfsd/nfs4proc.c | 27 +++++++++++++++++++++++++++ fs/nfsd/nfsproc.c | 17 ++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index bbaef884f893..61272a6d2211 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -282,6 +282,7 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_attrs attrs = { .na_iattr = iap, }; + struct svc_export *exp; __u32 v_mtime, v_atime; struct inode *inode; __be32 status; @@ -320,7 +321,23 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; } - status = fh_compose(resfhp, fhp->fh_export, child, fhp); + exp = exp_get(fhp->fh_export); + if (argp->createmode == NFS3_CREATE_UNCHECKED) { + /* + * If name is already in dcache we need to check for mountpoints + */ + if (d_is_reg(child) && + unlikely(nfsd_mountpoint(child, exp))) { + status = nfsd_cross_mnt(rqstp, &child, &exp); + if (status != nfs_ok) { + exp_put(exp); + goto out; + } + } + } + + status = fh_compose(resfhp, exp, child, fhp); + exp_put(exp); if (status != nfs_ok) goto out; diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 0317ff3f1c39..a13dc1756b1b 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -271,6 +271,33 @@ 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); + status = nfsd_cross_mnt(rqstp, &child, &exp); + 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; + } + 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..20fe4411f666 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -291,6 +291,7 @@ nfsd_proc_create(struct svc_rqst *rqstp) struct nfsd_attrs attrs = { .na_iattr = attr, }; + struct svc_export *exp; struct inode *inode; struct dentry *dchild; int type, mode; @@ -319,8 +320,22 @@ nfsd_proc_create(struct svc_rqst *rqstp) resp->status = nfserrno(PTR_ERR(dchild)); goto out_write; } + /* + * If name exists we need to check for mountpoints + */ + exp = exp_get(dirfhp->fh_export); + if (d_is_reg(dchild) && + unlikely(nfsd_mountpoint(dchild, exp))) { + resp->status = nfsd_cross_mnt(rqstp, &dchild, &exp); + if (resp->status != nfs_ok) { + exp_put(exp); + goto out_unlock; + } + } + fh_init(newfhp, NFS_FHSIZE); - resp->status = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp); + resp->status = fh_compose(newfhp, exp, dchild, dirfhp); + exp_put(exp); if (!resp->status && d_really_is_negative(dchild)) resp->status = nfserr_noent; if (resp->status) { -- 2.50.0.107.gf914562f5916.dirty