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 + -
显示快捷键?