[PATCH v3 02/17] nfsd: correctly handle CREATE of mounted-on files
NeilBrown <[email protected]>
| 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. Signed-off-by: NeilBrown <neil-+NVA1uvv1dVBDLzU/[email protected]> --- 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