diff options
Diffstat (limited to 'fs/nfsd/nfs4xdr.c')
| -rw-r--r-- | fs/nfsd/nfs4xdr.c | 89 | 
1 files changed, 60 insertions, 29 deletions
| diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index c2d2895a1ec1..8fae53ce21d1 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -33,6 +33,7 @@   *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   */ +#include <linux/fs_struct.h>  #include <linux/file.h>  #include <linux/slab.h>  #include <linux/namei.h> @@ -57,6 +58,20 @@  #define NFSDDBG_FACILITY		NFSDDBG_XDR +u32 nfsd_suppattrs[3][3] = { +	{NFSD4_SUPPORTED_ATTRS_WORD0, +	 NFSD4_SUPPORTED_ATTRS_WORD1, +	 NFSD4_SUPPORTED_ATTRS_WORD2}, + +	{NFSD4_1_SUPPORTED_ATTRS_WORD0, +	 NFSD4_1_SUPPORTED_ATTRS_WORD1, +	 NFSD4_1_SUPPORTED_ATTRS_WORD2}, + +	{NFSD4_1_SUPPORTED_ATTRS_WORD0, +	 NFSD4_1_SUPPORTED_ATTRS_WORD1, +	 NFSD4_2_SUPPORTED_ATTRS_WORD2}, +}; +  /*   * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing   * directory in order to indicate to the client that a filesystem boundary is present @@ -285,7 +300,7 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)  static __be32  nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,  		   struct iattr *iattr, struct nfs4_acl **acl, -		   struct xdr_netobj *label) +		   struct xdr_netobj *label, int *umask)  {  	int expected_len, len = 0;  	u32 dummy32; @@ -296,6 +311,14 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,  	if ((status = nfsd4_decode_bitmap(argp, bmval)))  		return status; +	if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0 +	    || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1 +	    || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2) { +		if (nfsd_attrs_supported(argp->minorversion, bmval)) +			return nfserr_inval; +		return nfserr_attrnotsupp; +	} +  	READ_BUF(4);  	expected_len = be32_to_cpup(p++); @@ -435,12 +458,18 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,  			return nfserr_jukebox;  	}  #endif - -	if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0 -	    || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1 -	    || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2) -		READ_BUF(expected_len - len); -	else if (len != expected_len) +	if (bmval[2] & FATTR4_WORD2_MODE_UMASK) { +		if (!umask) +			goto xdr_error; +		READ_BUF(8); +		len += 8; +		dummy32 = be32_to_cpup(p++); +		iattr->ia_mode = dummy32 & (S_IFMT | S_IALLUGO); +		dummy32 = be32_to_cpup(p++); +		*umask = dummy32 & S_IRWXUGO; +		iattr->ia_valid |= ATTR_MODE; +	} +	if (len != expected_len)  		goto xdr_error;  	DECODE_TAIL; @@ -634,7 +663,8 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create  		return status;  	status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, -				    &create->cr_acl, &create->cr_label); +				    &create->cr_acl, &create->cr_label, +				    ¤t->fs->umask);  	if (status)  		goto out; @@ -879,13 +909,15 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)  	case NFS4_OPEN_NOCREATE:  		break;  	case NFS4_OPEN_CREATE: +		current->fs->umask = 0;  		READ_BUF(4);  		open->op_createmode = be32_to_cpup(p++);  		switch (open->op_createmode) {  		case NFS4_CREATE_UNCHECKED:  		case NFS4_CREATE_GUARDED:  			status = nfsd4_decode_fattr(argp, open->op_bmval, -				&open->op_iattr, &open->op_acl, &open->op_label); +				&open->op_iattr, &open->op_acl, &open->op_label, +				¤t->fs->umask);  			if (status)  				goto out;  			break; @@ -899,7 +931,8 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)  			READ_BUF(NFS4_VERIFIER_SIZE);  			COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);  			status = nfsd4_decode_fattr(argp, open->op_bmval, -				&open->op_iattr, &open->op_acl, &open->op_label); +				&open->op_iattr, &open->op_acl, &open->op_label, +				¤t->fs->umask);  			if (status)  				goto out;  			break; @@ -1136,7 +1169,7 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta  	if (status)  		return status;  	return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, -				  &setattr->sa_acl, &setattr->sa_label); +				  &setattr->sa_acl, &setattr->sa_label, NULL);  }  static __be32 @@ -2340,9 +2373,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,  	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);  	BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); -	BUG_ON(bmval0 & ~nfsd_suppattrs0(minorversion)); -	BUG_ON(bmval1 & ~nfsd_suppattrs1(minorversion)); -	BUG_ON(bmval2 & ~nfsd_suppattrs2(minorversion)); +	BUG_ON(!nfsd_attrs_supported(minorversion, bmval));  	if (exp->ex_fslocs.migrated) {  		status = fattr_handle_absent_fs(&bmval0, &bmval1, &bmval2, &rdattr_err); @@ -2409,29 +2440,29 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,  	p++;                /* to be backfilled later */  	if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { -		u32 word0 = nfsd_suppattrs0(minorversion); -		u32 word1 = nfsd_suppattrs1(minorversion); -		u32 word2 = nfsd_suppattrs2(minorversion); +		u32 supp[3]; + +		memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));  		if (!IS_POSIXACL(dentry->d_inode)) -			word0 &= ~FATTR4_WORD0_ACL; +			supp[0] &= ~FATTR4_WORD0_ACL;  		if (!contextsupport) -			word2 &= ~FATTR4_WORD2_SECURITY_LABEL; -		if (!word2) { +			supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL; +		if (!supp[2]) {  			p = xdr_reserve_space(xdr, 12);  			if (!p)  				goto out_resource;  			*p++ = cpu_to_be32(2); -			*p++ = cpu_to_be32(word0); -			*p++ = cpu_to_be32(word1); +			*p++ = cpu_to_be32(supp[0]); +			*p++ = cpu_to_be32(supp[1]);  		} else {  			p = xdr_reserve_space(xdr, 16);  			if (!p)  				goto out_resource;  			*p++ = cpu_to_be32(3); -			*p++ = cpu_to_be32(word0); -			*p++ = cpu_to_be32(word1); -			*p++ = cpu_to_be32(word2); +			*p++ = cpu_to_be32(supp[0]); +			*p++ = cpu_to_be32(supp[1]); +			*p++ = cpu_to_be32(supp[2]);  		}  	}  	if (bmval0 & FATTR4_WORD0_TYPE) { @@ -3576,10 +3607,10 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd  	if (!p)  		return nfserr_resource;  	/* -	 * XXX: By default, the ->readlink() VFS op will truncate symlinks -	 * if they would overflow the buffer.  Is this kosher in NFSv4?  If -	 * not, one easy fix is: if ->readlink() precisely fills the buffer, -	 * assume that truncation occurred, and return NFS4ERR_RESOURCE. +	 * XXX: By default, vfs_readlink() will truncate symlinks if they +	 * would overflow the buffer.  Is this kosher in NFSv4?  If not, one +	 * easy fix is: if vfs_readlink() precisely fills the buffer, assume +	 * that truncation occurred, and return NFS4ERR_RESOURCE.  	 */  	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,  						(char *)p, &maxcount); |