nfs4xdr.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,595 行 · 第 1/5 页

C
2,595
字号
/* *  fs/nfs/nfs4xdr.c * *  Server-side XDR for NFSv4 * *  Copyright (c) 2002 The Regents of the University of Michigan. *  All rights reserved. * *  Kendrick Smith <kmsmith@umich.edu> *  Andy Adamson   <andros@umich.edu> * *  Redistribution and use in source and binary forms, with or without *  modification, are permitted provided that the following conditions *  are met: * *  1. Redistributions of source code must retain the above copyright *     notice, this list of conditions and the following disclaimer. *  2. Redistributions in binary form must reproduce the above copyright *     notice, this list of conditions and the following disclaimer in the *     documentation and/or other materials provided with the distribution. *  3. Neither the name of the University nor the names of its *     contributors may be used to endorse or promote products derived *     from this software without specific prior written permission. * *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * TODO: Neil Brown made the following observation:  We currently * initially reserve NFSD_BUFSIZE space on the transmit queue and * never release any of that until the request is complete. * It would be good to calculate a new maximum response size while * decoding the COMPOUND, and call svc_reserve with this number * at the end of nfs4svc_decode_compoundargs. */#include <linux/param.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/fs.h>#include <linux/namei.h>#include <linux/vfs.h>#include <linux/sunrpc/xdr.h>#include <linux/sunrpc/svc.h>#include <linux/sunrpc/clnt.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/state.h>#include <linux/nfsd/xdr4.h>#include <linux/nfsd_idmap.h>#include <linux/nfs4.h>#include <linux/nfs4_acl.h>#define NFSDDBG_FACILITY		NFSDDBG_XDRstatic const char utf8_byte_len[256] = {	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0};static inline intis_legal_utf8_sequence(unsigned char *source, int length){	unsigned char *ptr;	unsigned char c;	if (length==1) return 1;	/* Check for overlong sequence, and check second byte */	c = *(source + 1);	switch (*source) {	case 0xE0: /* 3 bytes */		if ( c < 0xA0 ) return 0;		break;	case 0xF0: /* 4 bytes */		if ( c < 0x90 ) return 0;		break;	case 0xF8: /* 5 bytes */		if ( c < 0xC8 ) return 0;		break;	case 0xFC: /* 6 bytes */		if ( c < 0x84 ) return 0;		break;	default:		if ( (c & 0xC0) != 0x80) return 0;	}	/* Check that trailing bytes look like 10xxxxxx */	for (ptr = source++ + length - 1; ptr>source; ptr--)		if ( ((*ptr) & 0xC0) != 0x80 ) return 0;	return 1;}/* This does some screening on disallowed unicode characters.  It is NOT * comprehensive. */static intis_allowed_utf8_char(unsigned char *source, int length){	/* We assume length and source point to a valid utf8 sequence */	unsigned char c;	/* Disallow F0000 and up (in utf8, F3B08080) */	if (*source > 0xF3 ) return 0;	c = *(source + 1);	switch (*source) {	case 0xF3:		if (c >= 0xB0) return 0;		break;	/* Disallow D800-F8FF (in utf8, EDA080-EFA3BF */	case 0xED:		if (c >= 0xA0) return 0;		break;	case 0xEE:		return 0;		break;	case 0xEF:		if (c <= 0xA3) return 0;	/* Disallow FFF9-FFFF (EFBFB9-EFBFBF) */		if (c==0xBF)			/* Don't need to check <=0xBF, since valid utf8 */			if ( *(source+2) >= 0xB9) return 0;		break;	}	return 1;}/* This routine should really check to see that the proper stringprep * mappings have been applied.  Instead, we do a simple screen of some * of the more obvious illegal values by calling is_allowed_utf8_char. * This will allow many illegal strings through, but if a client behaves, * it will get full functionality.  The other option (apart from full * stringprep checking) is to limit everything to an easily handled subset, * such as 7-bit ascii. * * Note - currently calling routines ignore return value except as boolean. */static intcheck_utf8(char *str, int len){	unsigned char *chunk, *sourceend;	int chunklen;	chunk = str;	sourceend = str + len;	while (chunk < sourceend) {		chunklen = utf8_byte_len[*chunk];		if (!chunklen)			return nfserr_inval;		if (chunk + chunklen > sourceend)			return nfserr_inval;		if (!is_legal_utf8_sequence(chunk, chunklen))			return nfserr_inval;		if (!is_allowed_utf8_char(chunk, chunklen))			return nfserr_inval;		if ( (chunklen==1) && (!*chunk) )			return nfserr_inval; /* Disallow embedded nulls */		chunk += chunklen;	}	return 0;}static intcheck_filename(char *str, int len, int err){	int i;	if (len == 0)		return nfserr_inval;	if (isdotent(str, len))		return err;	for (i = 0; i < len; i++)		if (str[i] == '/')			return err;	return check_utf8(str, len);}/* * START OF "GENERIC" DECODE ROUTINES. *   These may look a little ugly since they are imported from a "generic" * set of XDR encode/decode routines which are intended to be shared by * all of our NFSv4 implementations (OpenBSD, MacOS X...). * * If the pain of reading these is too great, it should be a straightforward * task to translate them into Linux-specific versions which are more * consistent with the style used in NFSv2/v3... */#define DECODE_HEAD				\	u32 *p;					\	int status#define DECODE_TAIL				\	status = 0;				\out:						\	return status;				\xdr_error:					\	printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__);	\	status = nfserr_bad_xdr;		\	goto out#define READ32(x)         (x) = ntohl(*p++)#define READ64(x)         do {			\	(x) = (u64)ntohl(*p++) << 32;		\	(x) |= ntohl(*p++);			\} while (0)#define READTIME(x)       do {			\	p++;					\	(x) = ntohl(*p++);			\	p++;					\} while (0)#define READMEM(x,nbytes) do {			\	x = (char *)p;				\	p += XDR_QUADLEN(nbytes);		\} while (0)#define SAVEMEM(x,nbytes) do {			\	if (!(x = (p==argp->tmp || p == argp->tmpp) ? \ 		savemem(argp, p, nbytes) :	\ 		(char *)p)) {			\		printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \		goto xdr_error;			\		}				\	p += XDR_QUADLEN(nbytes);		\} while (0)#define COPYMEM(x,nbytes) do {			\	memcpy((x), p, nbytes);			\	p += XDR_QUADLEN(nbytes);		\} while (0)/* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */#define READ_BUF(nbytes)  do {			\	if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) {	\		p = argp->p;			\		argp->p += XDR_QUADLEN(nbytes);	\	} else if (!(p = read_buf(argp, nbytes))) { \		printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \		goto xdr_error;			\	}					\} while (0)u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes){	/* We want more bytes than seem to be available.	 * Maybe we need a new page, maybe we have just run out	 */	int avail = (char*)argp->end - (char*)argp->p;	u32 *p;	if (avail + argp->pagelen < nbytes)		return NULL;	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */		return NULL;	/* ok, we can do it with the current plus the next page */	if (nbytes <= sizeof(argp->tmp))		p = argp->tmp;	else {		if (argp->tmpp)			kfree(argp->tmpp);		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);		if (!p)			return NULL;			}	memcpy(p, argp->p, avail);	/* step to next page */	argp->p = page_address(argp->pagelist[0]);	argp->pagelist++;	if (argp->pagelen < PAGE_SIZE) {		argp->end = p + (argp->pagelen>>2);		argp->pagelen = 0;	} else {		argp->end = p + (PAGE_SIZE>>2);		argp->pagelen -= PAGE_SIZE;	}	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));	argp->p += XDR_QUADLEN(nbytes - avail);	return p;}static intdefer_free(struct nfsd4_compoundargs *argp,		void (*release)(const void *), void *p){	struct tmpbuf *tb;	tb = kmalloc(sizeof(*tb), GFP_KERNEL);	if (!tb)		return -ENOMEM;	tb->buf = p;	tb->release = release;	tb->next = argp->to_free;	argp->to_free = tb;	return 0;}char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes){	void *new = NULL;	if (p == argp->tmp) {		new = kmalloc(nbytes, GFP_KERNEL);		if (!new) return NULL;		p = new;		memcpy(p, argp->tmp, nbytes);	} else {		if (p != argp->tmpp)			BUG();		argp->tmpp = NULL;	}	if (defer_free(argp, kfree, p)) {		kfree(new);		return NULL;	} else		return (char *)p;}static intnfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval){	u32 bmlen;	DECODE_HEAD;	bmval[0] = 0;	bmval[1] = 0;	READ_BUF(4);	READ32(bmlen);	if (bmlen > 1000)		goto xdr_error;	READ_BUF(bmlen << 2);	if (bmlen > 0)		READ32(bmval[0]);	if (bmlen > 1)		READ32(bmval[1]);	DECODE_TAIL;}static intnfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr,    struct nfs4_acl **acl){	int expected_len, len = 0;	u32 dummy32;	char *buf;	DECODE_HEAD;	iattr->ia_valid = 0;	if ((status = nfsd4_decode_bitmap(argp, bmval)))		return status;	/*	 * According to spec, unsupported attributes return ERR_NOTSUPP;	 * read-only attributes return ERR_INVAL.	 */	if ((bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0) || (bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1))		return nfserr_attrnotsupp;	if ((bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0) || (bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1))		return nfserr_inval;	READ_BUF(4);	READ32(expected_len);	if (bmval[0] & FATTR4_WORD0_SIZE) {		READ_BUF(8);		len += 8;		READ64(iattr->ia_size);		iattr->ia_valid |= ATTR_SIZE;	}	if (bmval[0] & FATTR4_WORD0_ACL) {		int nace, i;		struct nfs4_ace ace;		READ_BUF(4); len += 4;		READ32(nace);		*acl = nfs4_acl_new();		if (*acl == NULL) {			status = -ENOMEM;			goto out_nfserr;		}		defer_free(argp, (void (*)(const void *))nfs4_acl_free, *acl);		for (i = 0; i < nace; i++) {			READ_BUF(16); len += 16;			READ32(ace.type);			READ32(ace.flag);			READ32(ace.access_mask);			READ32(dummy32);			READ_BUF(dummy32);			len += XDR_QUADLEN(dummy32) << 2;			READMEM(buf, dummy32);			if (check_utf8(buf, dummy32))				return nfserr_inval;			ace.whotype = nfs4_acl_get_whotype(buf, dummy32);			status = 0;			if (ace.whotype != NFS4_ACL_WHO_NAMED)				ace.who = 0;			else if (ace.flag & NFS4_ACE_IDENTIFIER_GROUP)				status = nfsd_map_name_to_gid(argp->rqstp,						buf, dummy32, &ace.who);			else				status = nfsd_map_name_to_uid(argp->rqstp,						buf, dummy32, &ace.who);			if (status)				goto out_nfserr;			if (nfs4_acl_add_ace(*acl, ace.type, ace.flag,				 ace.access_mask, ace.whotype, ace.who) != 0) {				status = -ENOMEM;				goto out_nfserr;			}		}	} else		*acl = NULL;	if (bmval[1] & FATTR4_WORD1_MODE) {		READ_BUF(4);		len += 4;		READ32(iattr->ia_mode);		iattr->ia_mode &= (S_IFMT | S_IALLUGO);		iattr->ia_valid |= ATTR_MODE;	}	if (bmval[1] & FATTR4_WORD1_OWNER) {		READ_BUF(4);		len += 4;		READ32(dummy32);		READ_BUF(dummy32);		len += (XDR_QUADLEN(dummy32) << 2);		READMEM(buf, dummy32);		if (check_utf8(buf, dummy32))			return nfserr_inval;		if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))			goto out_nfserr;		iattr->ia_valid |= ATTR_UID;	}	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {		READ_BUF(4);		len += 4;		READ32(dummy32);		READ_BUF(dummy32);		len += (XDR_QUADLEN(dummy32) << 2);		READMEM(buf, dummy32);		if (check_utf8(buf, dummy32))			return nfserr_inval;		if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))			goto out_nfserr;		iattr->ia_valid |= ATTR_GID;	}	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {		READ_BUF(4);		len += 4;		READ32(dummy32);		switch (dummy32) {		case NFS4_SET_TO_CLIENT_TIME:			/* We require the high 32 bits of 'seconds' to be 0, and we ignore			   all 32 bits of 'nseconds'. */			READ_BUF(12);			len += 12;			READ32(dummy32);			if (dummy32)				return nfserr_inval;			READ32(iattr->ia_atime.tv_sec);			READ32(iattr->ia_atime.tv_nsec);			if (iattr->ia_atime.tv_nsec >= (u32)1000000000)				return nfserr_inval;			iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);			break;		case NFS4_SET_TO_SERVER_TIME:			iattr->ia_valid |= ATTR_ATIME;			break;		default:			goto xdr_error;		}	}	if (bmval[1] & FATTR4_WORD1_TIME_METADATA) {		/* We require the high 32 bits of 'seconds' to be 0, and we ignore		   all 32 bits of 'nseconds'. */		READ_BUF(12);		len += 12;		READ32(dummy32);		if (dummy32)			return nfserr_inval;		READ32(iattr->ia_ctime.tv_sec);		READ32(iattr->ia_ctime.tv_nsec);		if (iattr->ia_ctime.tv_nsec >= (u32)1000000000)			return nfserr_inval;		iattr->ia_valid |= ATTR_CTIME;	}	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {		READ_BUF(4);		len += 4;		READ32(dummy32);		switch (dummy32) {		case NFS4_SET_TO_CLIENT_TIME:			/* We require the high 32 bits of 'seconds' to be 0, and we ignore			   all 32 bits of 'nseconds'. */			READ_BUF(12);			len += 12;			READ32(dummy32);			if (dummy32)				return nfserr_inval;			READ32(iattr->ia_mtime.tv_sec);			READ32(iattr->ia_mtime.tv_nsec);			if (iattr->ia_mtime.tv_nsec >= (u32)1000000000)				return nfserr_inval;			iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);			break;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?