Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nfsd: Allow AIX client to read dir containing mountpoints

This patch addresses a compatibility issue with a Linux NFS server and
AIX NFS client.

I have exported /export as fsid=0 with sec=krb5:krb5i
I have mount --bind /home onto /export/home
I have exported /export/home with sec=krb5i

The AIX client mounts / -o sec=krb5:krb5i onto /mnt

If I do an ls /mnt, the AIX client gets a permission error. Looking at
the network traceIwe see a READDIR looking for attributes
FATTR4_RDATTR_ERROR and FATTR4_MOUNTED_ON_FILEID. The response gives a
NFS4ERR_WRONGSEC which the AIX client is not expecting.

Since the AIX client is only asking for an attribute that is an
attribute of the parent file system (pseudo root in my example), it
seems reasonable that there should not be an error.

In discussing this issue with Bruce Fields, I initially proposed
ignoring the error in nfsd4_encode_dirent_fattr() if all that was being
asked for was FATTR4_RDATTR_ERROR and FATTR4_MOUNTED_ON_FILEID, however,
Bruce suggested that we avoid calling cross_mnt() if only these
attributes are requested.

The following patch implements bypassing cross_mnt() if only
FATTR4_RDATTR_ERROR and FATTR4_MOUNTED_ON_FILEID are called. Since there
is some complexity in the code in nfsd4_encode_fattr(), I didn't want to
duplicate code (and introduce a maintenance nightmare), so I added a
parameter to nfsd4_encode_fattr() that indicates whether it should
ignore cross mounts and simply fill in the attribute using the passed in
dentry as opposed to it's parent.

Signed-off-by: Frank Filz <ffilzlnx@us.ibm.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>

authored by

Frank Filz and committed by
J. Bruce Fields
406a7ea9 39325bd0

+24 -7
+1 -1
fs/nfsd/nfs4proc.c
··· 750 750 cstate->current_fh.fh_export, 751 751 cstate->current_fh.fh_dentry, buf, 752 752 &count, verify->ve_bmval, 753 - rqstp); 753 + rqstp, 0); 754 754 755 755 /* this means that nfsd4_encode_fattr() ran out of space */ 756 756 if (status == nfserr_resource && count == 0)
+22 -5
fs/nfsd/nfs4xdr.c
··· 1453 1453 __be32 1454 1454 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, 1455 1455 struct dentry *dentry, __be32 *buffer, int *countp, u32 *bmval, 1456 - struct svc_rqst *rqstp) 1456 + struct svc_rqst *rqstp, int ignore_crossmnt) 1457 1457 { 1458 1458 u32 bmval0 = bmval[0]; 1459 1459 u32 bmval1 = bmval[1]; ··· 1833 1833 if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) { 1834 1834 if ((buflen -= 8) < 0) 1835 1835 goto out_resource; 1836 - if (exp->ex_mnt->mnt_root->d_inode == dentry->d_inode) { 1836 + /* 1837 + * Get parent's attributes if not ignoring crossmount 1838 + * and this is the root of a cross-mounted filesystem. 1839 + */ 1840 + if (ignore_crossmnt == 0 && 1841 + exp->ex_mnt->mnt_root->d_inode == dentry->d_inode) { 1837 1842 err = vfs_getattr(exp->ex_mnt->mnt_parent, 1838 1843 exp->ex_mnt->mnt_mountpoint, &stat); 1839 1844 if (err) ··· 1874 1869 struct svc_export *exp = cd->rd_fhp->fh_export; 1875 1870 struct dentry *dentry; 1876 1871 __be32 nfserr; 1872 + int ignore_crossmnt = 0; 1877 1873 1878 1874 dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen); 1879 1875 if (IS_ERR(dentry)) 1880 1876 return nfserrno(PTR_ERR(dentry)); 1881 1877 1882 1878 exp_get(exp); 1883 - if (d_mountpoint(dentry)) { 1879 + /* 1880 + * In the case of a mountpoint, the client may be asking for 1881 + * attributes that are only properties of the underlying filesystem 1882 + * as opposed to the cross-mounted file system. In such a case, 1883 + * we will not follow the cross mount and will fill the attribtutes 1884 + * directly from the mountpoint dentry. 1885 + */ 1886 + if (d_mountpoint(dentry) && 1887 + (cd->rd_bmval[0] & ~FATTR4_WORD0_RDATTR_ERROR) == 0 && 1888 + (cd->rd_bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID) == 0) 1889 + ignore_crossmnt = 1; 1890 + else if (d_mountpoint(dentry)) { 1884 1891 int err; 1885 1892 1886 1893 /* ··· 1911 1894 1912 1895 } 1913 1896 nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval, 1914 - cd->rd_rqstp); 1897 + cd->rd_rqstp, ignore_crossmnt); 1915 1898 out_put: 1916 1899 dput(dentry); 1917 1900 exp_put(exp); ··· 2065 2048 buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2); 2066 2049 nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry, 2067 2050 resp->p, &buflen, getattr->ga_bmval, 2068 - resp->rqstp); 2051 + resp->rqstp, 0); 2069 2052 if (!nfserr) 2070 2053 resp->p += buflen; 2071 2054 return nfserr;
+1 -1
include/linux/nfsd/xdr4.h
··· 441 441 void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op); 442 442 __be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, 443 443 struct dentry *dentry, __be32 *buffer, int *countp, 444 - u32 *bmval, struct svc_rqst *); 444 + u32 *bmval, struct svc_rqst *, int ignore_crossmnt); 445 445 extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp, 446 446 struct nfsd4_compound_state *, 447 447 struct nfsd4_setclientid *setclid);