📄 nfs3xdr.c
字号:
/* * linux/fs/nfsd/nfs3xdr.c * * XDR support for nfsd/protocol version 3. * * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> * * 2003-08-09 Jamie Lokier: Use htonl() for nanoseconds, not htons()! */#include <linux/types.h>#include <linux/time.h>#include <linux/nfs3.h>#include <linux/list.h>#include <linux/spinlock.h>#include <linux/dcache.h>#include <linux/namei.h>#include <linux/mm.h>#include <linux/vfs.h>#include <linux/sunrpc/xdr.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/xdr3.h>#define NFSDDBG_FACILITY NFSDDBG_XDR/* * Mapping of S_IF* types to NFS file types */static u32 nfs3_ftypes[] = { NF3NON, NF3FIFO, NF3CHR, NF3BAD, NF3DIR, NF3BAD, NF3BLK, NF3BAD, NF3REG, NF3BAD, NF3LNK, NF3BAD, NF3SOCK, NF3BAD, NF3LNK, NF3BAD,};/* * XDR functions for basic NFS types */static __be32 *encode_time3(__be32 *p, struct timespec *time){ *p++ = htonl((u32) time->tv_sec); *p++ = htonl(time->tv_nsec); return p;}static __be32 *decode_time3(__be32 *p, struct timespec *time){ time->tv_sec = ntohl(*p++); time->tv_nsec = ntohl(*p++); return p;}static __be32 *decode_fh(__be32 *p, struct svc_fh *fhp){ unsigned int size; fh_init(fhp, NFS3_FHSIZE); size = ntohl(*p++); if (size > NFS3_FHSIZE) return NULL; memcpy(&fhp->fh_handle.fh_base, p, size); fhp->fh_handle.fh_size = size; return p + XDR_QUADLEN(size);}/* Helper function for NFSv3 ACL code */__be32 *nfs3svc_decode_fh(__be32 *p, struct svc_fh *fhp){ return decode_fh(p, fhp);}static __be32 *encode_fh(__be32 *p, struct svc_fh *fhp){ unsigned int size = fhp->fh_handle.fh_size; *p++ = htonl(size); if (size) p[XDR_QUADLEN(size)-1]=0; memcpy(p, &fhp->fh_handle.fh_base, size); return p + XDR_QUADLEN(size);}/* * Decode a file name and make sure that the path contains * no slashes or null bytes. */static __be32 *decode_filename(__be32 *p, char **namp, int *lenp){ char *name; int i; if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) { for (i = 0, name = *namp; i < *lenp; i++, name++) { if (*name == '\0' || *name == '/') return NULL; } } return p;}static __be32 *decode_sattr3(__be32 *p, struct iattr *iap){ u32 tmp; iap->ia_valid = 0; if (*p++) { iap->ia_valid |= ATTR_MODE; iap->ia_mode = ntohl(*p++); } if (*p++) { iap->ia_valid |= ATTR_UID; iap->ia_uid = ntohl(*p++); } if (*p++) { iap->ia_valid |= ATTR_GID; iap->ia_gid = ntohl(*p++); } if (*p++) { u64 newsize; iap->ia_valid |= ATTR_SIZE; p = xdr_decode_hyper(p, &newsize); if (newsize <= NFS_OFFSET_MAX) iap->ia_size = newsize; else iap->ia_size = NFS_OFFSET_MAX; } if ((tmp = ntohl(*p++)) == 1) { /* set to server time */ iap->ia_valid |= ATTR_ATIME; } else if (tmp == 2) { /* set to client time */ iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; iap->ia_atime.tv_sec = ntohl(*p++); iap->ia_atime.tv_nsec = ntohl(*p++); } if ((tmp = ntohl(*p++)) == 1) { /* set to server time */ iap->ia_valid |= ATTR_MTIME; } else if (tmp == 2) { /* set to client time */ iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET; iap->ia_mtime.tv_sec = ntohl(*p++); iap->ia_mtime.tv_nsec = ntohl(*p++); } return p;}static __be32 *encode_fsid(__be32 *p, struct svc_fh *fhp){ u64 f; switch(fsid_source(fhp)) { default: case FSIDSOURCE_DEV: p = xdr_encode_hyper(p, (u64)huge_encode_dev (fhp->fh_dentry->d_inode->i_sb->s_dev)); break; case FSIDSOURCE_FSID: p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid); break; case FSIDSOURCE_UUID: f = ((u64*)fhp->fh_export->ex_uuid)[0]; f ^= ((u64*)fhp->fh_export->ex_uuid)[1]; p = xdr_encode_hyper(p, f); break; } return p;}static __be32 *encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat){ *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]); *p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->nlink); *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid)); *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid)); if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) { p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); } else { p = xdr_encode_hyper(p, (u64) stat->size); } p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9); *p++ = htonl((u32) MAJOR(stat->rdev)); *p++ = htonl((u32) MINOR(stat->rdev)); p = encode_fsid(p, fhp); p = xdr_encode_hyper(p, stat->ino); p = encode_time3(p, &stat->atime); p = encode_time3(p, &stat->mtime); p = encode_time3(p, &stat->ctime); return p;}static __be32 *encode_saved_post_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp){ /* Attributes to follow */ *p++ = xdr_one; return encode_fattr3(rqstp, p, fhp, &fhp->fh_post_attr);}/* * Encode post-operation attributes. * The inode may be NULL if the call failed because of a stale file * handle. In this case, no attributes are returned. */static __be32 *encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp){ struct dentry *dentry = fhp->fh_dentry; if (dentry && dentry->d_inode) { int err; struct kstat stat; err = vfs_getattr(fhp->fh_export->ex_mnt, dentry, &stat); if (!err) { *p++ = xdr_one; /* attributes follow */ lease_get_mtime(dentry->d_inode, &stat.mtime); return encode_fattr3(rqstp, p, fhp, &stat); } } *p++ = xdr_zero; return p;}/* Helper for NFSv3 ACLs */__be32 *nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp){ return encode_post_op_attr(rqstp, p, fhp);}/* * Enocde weak cache consistency data */static __be32 *encode_wcc_data(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp){ struct dentry *dentry = fhp->fh_dentry; if (dentry && dentry->d_inode && fhp->fh_post_saved) { if (fhp->fh_pre_saved) { *p++ = xdr_one; p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size); p = encode_time3(p, &fhp->fh_pre_mtime); p = encode_time3(p, &fhp->fh_pre_ctime); } else { *p++ = xdr_zero; } return encode_saved_post_attr(rqstp, p, fhp); } /* no pre- or post-attrs */ *p++ = xdr_zero; return encode_post_op_attr(rqstp, p, fhp);}/* * Fill in the post_op attr for the wcc data */void fill_post_wcc(struct svc_fh *fhp){ int err; if (fhp->fh_post_saved) printk("nfsd: inode locked twice during operation.\n"); err = vfs_getattr(fhp->fh_export->ex_mnt, fhp->fh_dentry, &fhp->fh_post_attr); if (err) fhp->fh_post_saved = 0; else fhp->fh_post_saved = 1;}/* * XDR decode functions */intnfs3svc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p, struct nfsd_fhandle *args){ if (!(p = decode_fh(p, &args->fh))) return 0; return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_sattrargs *args){ if (!(p = decode_fh(p, &args->fh))) return 0; p = decode_sattr3(p, &args->attrs); if ((args->check_guard = ntohl(*p++)) != 0) { struct timespec time; p = decode_time3(p, &time); args->guardtime = time.tv_sec; } return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_diropargs *args){ if (!(p = decode_fh(p, &args->fh)) || !(p = decode_filename(p, &args->name, &args->len))) return 0; return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_accessargs *args){ if (!(p = decode_fh(p, &args->fh))) return 0; args->access = ntohl(*p++); return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_readargs *args){ unsigned int len; int v,pn; u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh))) return 0; p = xdr_decode_hyper(p, &args->offset); len = args->count = ntohl(*p++); if (len > max_blocksize) len = max_blocksize; /* set up the kvec */ v=0; while (len > 0) { pn = rqstp->rq_resused++; rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]); rqstp->rq_vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE; len -= rqstp->rq_vec[v].iov_len; v++; } args->vlen = v; return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_writeargs *args){ unsigned int len, v, hdr, dlen; u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh))) return 0; p = xdr_decode_hyper(p, &args->offset); args->count = ntohl(*p++); args->stable = ntohl(*p++); len = args->len = ntohl(*p++); /* * The count must equal the amount of data passed. */ if (args->count != args->len) return 0; /* * Check to make sure that we got the right number of * bytes. */ hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len - hdr; /* * Round the length of the data which was specified up to * the next multiple of XDR units and then compare that * against the length which was actually received. * Note that when RPCSEC/GSS (for example) is used, the * data buffer can be padded so dlen might be larger * than required. It must never be smaller. */ if (dlen < XDR_QUADLEN(len)*4) return 0; if (args->count > max_blocksize) { args->count = max_blocksize; len = args->len = max_blocksize; } rqstp->rq_vec[0].iov_base = (void*)p; rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; v = 0; while (len > rqstp->rq_vec[v].iov_len) { len -= rqstp->rq_vec[v].iov_len; v++; rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]); rqstp->rq_vec[v].iov_len = PAGE_SIZE; } rqstp->rq_vec[v].iov_len = len; args->vlen = v + 1; return 1;}intnfs3svc_decode_createargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_createargs *args){ if (!(p = decode_fh(p, &args->fh)) || !(p = decode_filename(p, &args->name, &args->len))) return 0; switch (args->createmode = ntohl(*p++)) { case NFS3_CREATE_UNCHECKED: case NFS3_CREATE_GUARDED: p = decode_sattr3(p, &args->attrs); break; case NFS3_CREATE_EXCLUSIVE: args->verf = p; p += 2; break; default: return 0; } return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_createargs *args){ if (!(p = decode_fh(p, &args->fh)) || !(p = decode_filename(p, &args->name, &args->len))) return 0; p = decode_sattr3(p, &args->attrs); return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_symlinkargs *args){ unsigned int len; int avail; char *old, *new; struct kvec *vec; if (!(p = decode_fh(p, &args->ffh)) || !(p = decode_filename(p, &args->fname, &args->flen)) ) return 0; p = decode_sattr3(p, &args->attrs); /* now decode the pathname, which might be larger than the first page. * As we have to check for nul's anyway, we copy it into a new page * This page appears in the rq_res.pages list, but as pages_len is always * 0, it won't get in the way */ len = ntohl(*p++); if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE) return 0; args->tname = new = page_address(rqstp->rq_respages[rqstp->rq_resused++]); args->tlen = len; /* first copy and check from the first page */ old = (char*)p; vec = &rqstp->rq_arg.head[0]; avail = vec->iov_len - (old - (char*)vec->iov_base); while (len && avail && *old) { *new++ = *old++; len--; avail--; } /* now copy next page if there is one */ if (len && !avail && rqstp->rq_arg.page_len) { avail = rqstp->rq_arg.page_len; if (avail > PAGE_SIZE) avail = PAGE_SIZE; old = page_address(rqstp->rq_arg.pages[0]); } while (len && avail && *old) { *new++ = *old++; len--; avail--; } *new = '\0'; if (len) return 0; return 1;}intnfs3svc_decode_mknodargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_mknodargs *args){ if (!(p = decode_fh(p, &args->fh)) || !(p = decode_filename(p, &args->name, &args->len))) return 0; args->ftype = ntohl(*p++); if (args->ftype == NF3BLK || args->ftype == NF3CHR || args->ftype == NF3SOCK || args->ftype == NF3FIFO) p = decode_sattr3(p, &args->attrs); if (args->ftype == NF3BLK || args->ftype == NF3CHR) { args->major = ntohl(*p++); args->minor = ntohl(*p++); } return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_renameargs *args){ if (!(p = decode_fh(p, &args->ffh)) || !(p = decode_filename(p, &args->fname, &args->flen)) || !(p = decode_fh(p, &args->tfh)) || !(p = decode_filename(p, &args->tname, &args->tlen))) return 0; return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_readlinkargs *args){ if (!(p = decode_fh(p, &args->fh))) return 0; args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]); return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_linkargs *args){ if (!(p = decode_fh(p, &args->ffh)) || !(p = decode_fh(p, &args->tfh)) || !(p = decode_filename(p, &args->tname, &args->tlen))) return 0; return xdr_argsize_check(rqstp, p);}intnfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_readdirargs *args){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -