📄 nfs4xdr.c
字号:
/* * 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/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>#include <linux/sunrpc/gss_api.h>#include <linux/sunrpc/svcauth_gss.h>#define NFSDDBG_FACILITY NFSDDBG_XDR/* * 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 * We use a fixed fsid for a referral */#define NFS4_REFERRAL_FSID_MAJOR 0x8000000ULL#define NFS4_REFERRAL_FSID_MINOR 0x8000000ULLstatic __be32check_filename(char *str, int len, __be32 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 0;}/* * 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 \ __be32 *p; \ __be32 status#define DECODE_TAIL \ status = 0; \out: \ return status; \xdr_error: \ dprintk("NFSD: 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)) { \ dprintk("NFSD: 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))) { \ dprintk("NFSD: xdr error (%s:%d)\n", \ __FILE__, __LINE__); \ goto xdr_error; \ } \} while (0)static __be32 *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; __be32 *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 { 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;}static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes){ if (p == argp->tmp) { p = kmalloc(nbytes, GFP_KERNEL); if (!p) return NULL; memcpy(p, argp->tmp, nbytes); } else { BUG_ON(p != argp->tmpp); argp->tmpp = NULL; } if (defer_free(argp, kfree, p)) { kfree(p); return NULL; } else return (char *)p;}static __be32nfsd4_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 __be32nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr, struct nfs4_acl **acl){ int expected_len, len = 0; u32 dummy32; char *buf; int host_err; DECODE_HEAD; iattr->ia_valid = 0; if ((status = nfsd4_decode_bitmap(argp, bmval))) return status; /* * According to spec, unsupported attributes return ERR_ATTRNOTSUPP; * 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; struct nfs4_ace *ace; READ_BUF(4); len += 4; READ32(nace); if (nace > NFS4_ACL_MAX) return nfserr_resource; *acl = nfs4_acl_new(nace); if (*acl == NULL) { host_err = -ENOMEM; goto out_nfserr; } defer_free(argp, kfree, *acl); (*acl)->naces = nace; for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) { 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); ace->whotype = nfs4_acl_get_whotype(buf, dummy32); host_err = 0; if (ace->whotype != NFS4_ACL_WHO_NAMED) ace->who = 0; else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) host_err = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &ace->who); else host_err = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &ace->who); if (host_err) 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 ((host_err = 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 ((host_err = 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; case NFS4_SET_TO_SERVER_TIME: iattr->ia_valid |= ATTR_MTIME; break; default: goto xdr_error; } } if (len != expected_len) goto xdr_error; DECODE_TAIL;out_nfserr: status = nfserrno(host_err); goto out;}static __be32nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access){ DECODE_HEAD; READ_BUF(4); READ32(access->ac_req_access); DECODE_TAIL;}static __be32nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close){ DECODE_HEAD; close->cl_stateowner = NULL; READ_BUF(4 + sizeof(stateid_t)); READ32(close->cl_seqid); READ32(close->cl_stateid.si_generation); COPYMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t)); DECODE_TAIL;}static __be32nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit){ DECODE_HEAD; READ_BUF(12); READ64(commit->co_offset); READ32(commit->co_count); DECODE_TAIL;}static __be32nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create){ DECODE_HEAD; READ_BUF(4); READ32(create->cr_type); switch (create->cr_type) { case NF4LNK: READ_BUF(4); READ32(create->cr_linklen); READ_BUF(create->cr_linklen); SAVEMEM(create->cr_linkname, create->cr_linklen); break; case NF4BLK: case NF4CHR: READ_BUF(8); READ32(create->cr_specdata1); READ32(create->cr_specdata2); break; case NF4SOCK: case NF4FIFO: case NF4DIR: default: break; } READ_BUF(4); READ32(create->cr_namelen); READ_BUF(create->cr_namelen); SAVEMEM(create->cr_name, create->cr_namelen); if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval))) return status; if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, &create->cr_acl))) goto out; DECODE_TAIL;}static inline __be32nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr){ DECODE_HEAD; READ_BUF(sizeof(stateid_t)); READ32(dr->dr_stateid.si_generation); COPYMEM(&dr->dr_stateid.si_opaque, sizeof(stateid_opaque_t)); DECODE_TAIL;}static inline __be32nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr){ return nfsd4_decode_bitmap(argp, getattr->ga_bmval);}static __be32nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -