📄 nfs4proc.c
字号:
/* * fs/nfsd/nfs4proc.c * * Server-side procedures 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. */#include <linux/param.h>#include <linux/major.h>#include <linux/slab.h>#include <linux/file.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/cache.h>#include <linux/nfs4.h>#include <linux/nfsd/state.h>#include <linux/nfsd/xdr4.h>#include <linux/nfs4_acl.h>#include <linux/sunrpc/gss_api.h>#define NFSDDBG_FACILITY NFSDDBG_PROCstatic inline voidfh_dup2(struct svc_fh *dst, struct svc_fh *src){ fh_put(dst); dget(src->fh_dentry); if (src->fh_export) cache_get(&src->fh_export->h); *dst = *src;}static __be32do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open, int accmode){ __be32 status; if (open->op_truncate && !(open->op_share_access & NFS4_SHARE_ACCESS_WRITE)) return nfserr_inval; if (open->op_share_access & NFS4_SHARE_ACCESS_READ) accmode |= MAY_READ; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) accmode |= (MAY_WRITE | MAY_TRUNC); if (open->op_share_deny & NFS4_SHARE_DENY_WRITE) accmode |= MAY_WRITE; status = fh_verify(rqstp, current_fh, S_IFREG, accmode); return status;}static __be32do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open){ struct svc_fh resfh; __be32 status; int created = 0; fh_init(&resfh, NFS4_FHSIZE); open->op_truncate = 0; if (open->op_create) { /* * Note: create modes (UNCHECKED,GUARDED...) are the same * in NFSv4 as in v3. */ status = nfsd_create_v3(rqstp, current_fh, open->op_fname.data, open->op_fname.len, &open->op_iattr, &resfh, open->op_createmode, (u32 *)open->op_verf.data, &open->op_truncate, &created); /* If we ever decide to use different attrs to store the * verifier in nfsd_create_v3, then we'll need to change this */ if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0) open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_MODIFY); } else { status = nfsd_lookup(rqstp, current_fh, open->op_fname.data, open->op_fname.len, &resfh); fh_unlock(current_fh); } if (status) goto out; set_change_info(&open->op_cinfo, current_fh); /* set reply cache */ fh_dup2(current_fh, &resfh); open->op_stateowner->so_replay.rp_openfh_len = resfh.fh_handle.fh_size; memcpy(open->op_stateowner->so_replay.rp_openfh, &resfh.fh_handle.fh_base, resfh.fh_handle.fh_size); if (!created) status = do_open_permission(rqstp, current_fh, open, MAY_NOP);out: fh_put(&resfh); return status;}static __be32do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open){ __be32 status; /* Only reclaims from previously confirmed clients are valid */ if ((status = nfs4_check_open_reclaim(&open->op_clientid))) return status; /* We don't know the target directory, and therefore can not * set the change info */ memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info)); /* set replay cache */ open->op_stateowner->so_replay.rp_openfh_len = current_fh->fh_handle.fh_size; memcpy(open->op_stateowner->so_replay.rp_openfh, ¤t_fh->fh_handle.fh_base, current_fh->fh_handle.fh_size); open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) && (open->op_iattr.ia_size == 0); status = do_open_permission(rqstp, current_fh, open, MAY_OWNER_OVERRIDE); return status;}static __be32nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_open *open){ __be32 status; dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n", (int)open->op_fname.len, open->op_fname.data, open->op_stateowner); /* This check required by spec. */ if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) return nfserr_inval; nfs4_lock_state(); /* check seqid for replay. set nfs4_owner */ status = nfsd4_process_open1(open); if (status == nfserr_replay_me) { struct nfs4_replay *rp = &open->op_stateowner->so_replay; fh_put(&cstate->current_fh); cstate->current_fh.fh_handle.fh_size = rp->rp_openfh_len; memcpy(&cstate->current_fh.fh_handle.fh_base, rp->rp_openfh, rp->rp_openfh_len); status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); if (status) dprintk("nfsd4_open: replay failed" " restoring previous filehandle\n"); else status = nfserr_replay_me; } if (status) goto out; /* Openowner is now set, so sequence id will get bumped. Now we need * these checks before we do any creates: */ status = nfserr_grace; if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) goto out; status = nfserr_no_grace; if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) goto out; switch (open->op_claim_type) { case NFS4_OPEN_CLAIM_DELEGATE_CUR: status = nfserr_inval; if (open->op_create) goto out; /* fall through */ case NFS4_OPEN_CLAIM_NULL: /* * (1) set CURRENT_FH to the file being opened, * creating it if necessary, (2) set open->op_cinfo, * (3) set open->op_truncate if the file is to be * truncated after opening, (4) do permission checking. */ status = do_open_lookup(rqstp, &cstate->current_fh, open); if (status) goto out; break; case NFS4_OPEN_CLAIM_PREVIOUS: open->op_stateowner->so_confirmed = 1; /* * The CURRENT_FH is already set to the file being * opened. (1) set open->op_cinfo, (2) set * open->op_truncate if the file is to be truncated * after opening, (3) do permission checking. */ status = do_open_fhandle(rqstp, &cstate->current_fh, open); if (status) goto out; break; case NFS4_OPEN_CLAIM_DELEGATE_PREV: open->op_stateowner->so_confirmed = 1; dprintk("NFSD: unsupported OPEN claim type %d\n", open->op_claim_type); status = nfserr_notsupp; goto out; default: dprintk("NFSD: Invalid OPEN claim type %d\n", open->op_claim_type); status = nfserr_inval; goto out; } /* * nfsd4_process_open2() does the actual opening of the file. If * successful, it (1) truncates the file if open->op_truncate was * set, (2) sets open->op_stateid, (3) sets open->op_delegation. */ status = nfsd4_process_open2(rqstp, &cstate->current_fh, open);out: if (open->op_stateowner) { nfs4_get_stateowner(open->op_stateowner); cstate->replay_owner = open->op_stateowner; } nfs4_unlock_state(); return status;}/* * filehandle-manipulating ops. */static __be32nfsd4_getfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct svc_fh **getfh){ if (!cstate->current_fh.fh_dentry) return nfserr_nofilehandle; *getfh = &cstate->current_fh; return nfs_ok;}static __be32nfsd4_putfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_putfh *putfh){ fh_put(&cstate->current_fh); cstate->current_fh.fh_handle.fh_size = putfh->pf_fhlen; memcpy(&cstate->current_fh.fh_handle.fh_base, putfh->pf_fhval, putfh->pf_fhlen); return fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP);}static __be32nfsd4_putrootfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, void *arg){ __be32 status; fh_put(&cstate->current_fh); status = exp_pseudoroot(rqstp, &cstate->current_fh); return status;}static __be32nfsd4_restorefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, void *arg){ if (!cstate->save_fh.fh_dentry) return nfserr_restorefh; fh_dup2(&cstate->current_fh, &cstate->save_fh); return nfs_ok;}static __be32nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, void *arg){ if (!cstate->current_fh.fh_dentry) return nfserr_nofilehandle; fh_dup2(&cstate->save_fh, &cstate->current_fh); return nfs_ok;}/* * misc nfsv4 ops */static __be32nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_access *access){ if (access->ac_req_access & ~NFS3_ACCESS_FULL) return nfserr_inval; access->ac_resp_access = access->ac_req_access; return nfsd_access(rqstp, &cstate->current_fh, &access->ac_resp_access, &access->ac_supported);}static __be32nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_commit *commit){ __be32 status; u32 *p = (u32 *)commit->co_verf.data; *p++ = nfssvc_boot.tv_sec; *p++ = nfssvc_boot.tv_usec; status = nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset, commit->co_count); if (status == nfserr_symlink) status = nfserr_inval; return status;}static __be32nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_create *create){ struct svc_fh resfh; __be32 status; dev_t rdev; fh_init(&resfh, NFS4_FHSIZE); status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, MAY_CREATE); if (status == nfserr_symlink) status = nfserr_notdir; if (status) return status; switch (create->cr_type) { case NF4LNK: /* ugh! we have to null-terminate the linktext, or * vfs_symlink() will choke. it is always safe to * null-terminate by brute force, since at worst we * will overwrite the first byte of the create namelen * in the XDR buffer, which has already been extracted * during XDR decode. */ create->cr_linkname[create->cr_linklen] = 0; status = nfsd_symlink(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, create->cr_linkname, create->cr_linklen, &resfh, &create->cr_iattr); break; case NF4BLK: rdev = MKDEV(create->cr_specdata1, create->cr_specdata2); if (MAJOR(rdev) != create->cr_specdata1 || MINOR(rdev) != create->cr_specdata2) return nfserr_inval; status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, &create->cr_iattr, S_IFBLK, rdev, &resfh); break; case NF4CHR: rdev = MKDEV(create->cr_specdata1, create->cr_specdata2); if (MAJOR(rdev) != create->cr_specdata1 || MINOR(rdev) != create->cr_specdata2) return nfserr_inval; status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, &create->cr_iattr,S_IFCHR, rdev, &resfh); break; case NF4SOCK: status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, &create->cr_iattr, S_IFSOCK, 0, &resfh); break; case NF4FIFO: status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, &create->cr_iattr, S_IFIFO, 0, &resfh); break; case NF4DIR: create->cr_iattr.ia_valid &= ~ATTR_SIZE; status = nfsd_create(rqstp, &cstate->current_fh, create->cr_name, create->cr_namelen, &create->cr_iattr, S_IFDIR, 0, &resfh); break; default: status = nfserr_badtype; } if (!status) { fh_unlock(&cstate->current_fh); set_change_info(&create->cr_cinfo, &cstate->current_fh); fh_dup2(&cstate->current_fh, &resfh); } fh_put(&resfh); return status;}static __be32nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_getattr *getattr){ __be32 status; status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); if (status) return status; if (getattr->ga_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) return nfserr_inval; getattr->ga_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0; getattr->ga_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1; getattr->ga_fhp = &cstate->current_fh; return nfs_ok;}static __be32nfsd4_link(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_link *link){ __be32 status = nfserr_nofilehandle; if (!cstate->save_fh.fh_dentry) return status; status = nfsd_link(rqstp, &cstate->current_fh, link->li_name, link->li_namelen, &cstate->save_fh); if (!status) set_change_info(&link->li_cinfo, &cstate->current_fh); return status;}static __be32nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, void *arg){ struct svc_fh tmp_fh; __be32 ret; fh_init(&tmp_fh, NFS4_FHSIZE); ret = exp_pseudoroot(rqstp, &tmp_fh); if (ret) return ret; if (tmp_fh.fh_dentry == cstate->current_fh.fh_dentry) { fh_put(&tmp_fh); return nfserr_noent; } fh_put(&tmp_fh); return nfsd_lookup(rqstp, &cstate->current_fh, "..", 2, &cstate->current_fh);}static __be32nfsd4_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_lookup *lookup){ return nfsd_lookup(rqstp, &cstate->current_fh, lookup->lo_name, lookup->lo_len, &cstate->current_fh);}static __be32nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_read *read){ __be32 status; /* no need to check permission - this will be done in nfsd_read() */ read->rd_filp = NULL; if (read->rd_offset >= OFFSET_MAX) return nfserr_inval; nfs4_lock_state(); /* check stateid */ if ((status = nfs4_preprocess_stateid_op(&cstate->current_fh, &read->rd_stateid, CHECK_FH | RD_STATE, &read->rd_filp))) { dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); goto out; } if (read->rd_filp) get_file(read->rd_filp); status = nfs_ok;out: nfs4_unlock_state(); read->rd_rqstp = rqstp; read->rd_fhp = &cstate->current_fh; return status;}static __be32nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_readdir *readdir){ u64 cookie = readdir->rd_cookie; static const nfs4_verifier zeroverf; /* no need to check permission - this will be done in nfsd_readdir() */ if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) return nfserr_inval; readdir->rd_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0; readdir->rd_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1; if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) || (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE))) return nfserr_bad_cookie; readdir->rd_rqstp = rqstp; readdir->rd_fhp = &cstate->current_fh; return nfs_ok;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -