nfs4state.c
字号:
/** linux/fs/nfsd/nfs4state.c** Copyright (c) 2001 The Regents of the University of Michigan.* All rights reserved.** Kendrick Smith <kmsmith@umich.edu>* Andy Adamson <kandros@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/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/cache.h>#include <linux/mount.h>#include <linux/workqueue.h>#include <linux/smp_lock.h>#include <linux/nfs4.h>#include <linux/nfsd/state.h>#include <linux/nfsd/xdr4.h>#define NFSDDBG_FACILITY NFSDDBG_PROC/* Globals */static time_t lease_time = 90; /* default lease time */static time_t old_lease_time = 90; /* past incarnation lease time */static u32 nfs4_reclaim_init = 0;time_t boot_time;static time_t grace_end = 0;static u32 current_clientid = 1;static u32 current_ownerid;static u32 current_fileid;static u32 nfs4_init;stateid_t zerostateid; /* bits all 0 */stateid_t onestateid; /* bits all 1 *//* debug counters */u32 list_add_perfile = 0; u32 list_del_perfile = 0;u32 add_perclient = 0;u32 del_perclient = 0;u32 alloc_file = 0;u32 free_file = 0;u32 alloc_sowner = 0;u32 free_sowner = 0;u32 vfsopen = 0;u32 vfsclose = 0;u32 alloc_lsowner= 0;/* forward declarations */struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);/* Locking: * * client_sema: * protects clientid_hashtbl[], clientstr_hashtbl[], * unconfstr_hashtbl[], uncofid_hashtbl[]. */static DECLARE_MUTEX(client_sema);voidnfs4_lock_state(void){ down(&client_sema);}/* * nfs4_unlock_state(); called in encode */voidnfs4_unlock_state(void){ up(&client_sema);}static inline u32opaque_hashval(const void *ptr, int nbytes){ unsigned char *cptr = (unsigned char *) ptr; u32 x = 0; while (nbytes--) { x *= 37; x += *cptr++; } return x;}/* forward declarations */static void release_stateowner(struct nfs4_stateowner *sop);static void release_stateid(struct nfs4_stateid *stp, int flags);static void release_file(struct nfs4_file *fp);/* * SETCLIENTID state *//* Hash tables for nfs4_clientid state */#define CLIENT_HASH_BITS 4#define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS)#define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1)#define clientid_hashval(id) \ ((id) & CLIENT_HASH_MASK)#define clientstr_hashval(name, namelen) \ (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK)/* * reclaim_str_hashtbl[] holds known client info from previous reset/reboot * used in reboot/reset lease grace period processing * * conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed * setclientid_confirmed info. * * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed * setclientid info. * * client_lru holds client queue ordered by nfs4_client.cl_time * for lease renewal. * * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time * for last close replay. */static struct list_head reclaim_str_hashtbl[CLIENT_HASH_SIZE];static int reclaim_str_hashtbl_size;static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE];static struct list_head conf_str_hashtbl[CLIENT_HASH_SIZE];static struct list_head unconf_str_hashtbl[CLIENT_HASH_SIZE];static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE];static struct list_head client_lru;static struct list_head close_lru;static inline voidrenew_client(struct nfs4_client *clp){ /* * Move client to the end to the LRU list. */ dprintk("renewing client (clientid %08x/%08x)\n", clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); list_move_tail(&clp->cl_lru, &client_lru); clp->cl_time = get_seconds();}/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */static intSTALE_CLIENTID(clientid_t *clid){ if (clid->cl_boot == boot_time) return 0; dprintk("NFSD stale clientid (%08x/%08x)\n", clid->cl_boot, clid->cl_id); return 1;}/* * XXX Should we use a slab cache ? * This type of memory management is somewhat inefficient, but we use it * anyway since SETCLIENTID is not a common operation. */static inline struct nfs4_client *alloc_client(struct xdr_netobj name){ struct nfs4_client *clp; if ((clp = kmalloc(sizeof(struct nfs4_client), GFP_KERNEL))!= NULL) { memset(clp, 0, sizeof(*clp)); if ((clp->cl_name.data = kmalloc(name.len, GFP_KERNEL)) != NULL) { memcpy(clp->cl_name.data, name.data, name.len); clp->cl_name.len = name.len; } else { kfree(clp); clp = NULL; } } return clp;}static inline voidfree_client(struct nfs4_client *clp){ if (clp->cl_cred.cr_group_info) put_group_info(clp->cl_cred.cr_group_info); kfree(clp->cl_name.data); kfree(clp);}static voidexpire_client(struct nfs4_client *clp){ struct nfs4_stateowner *sop; dprintk("NFSD: expire_client\n"); list_del(&clp->cl_idhash); list_del(&clp->cl_strhash); list_del(&clp->cl_lru); while (!list_empty(&clp->cl_perclient)) { sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient); release_stateowner(sop); } free_client(clp);}static struct nfs4_client *create_client(struct xdr_netobj name) { struct nfs4_client *clp; if (!(clp = alloc_client(name))) goto out; INIT_LIST_HEAD(&clp->cl_idhash); INIT_LIST_HEAD(&clp->cl_strhash); INIT_LIST_HEAD(&clp->cl_perclient); INIT_LIST_HEAD(&clp->cl_lru);out: return clp;}static voidcopy_verf(struct nfs4_client *target, nfs4_verifier *source) { memcpy(target->cl_verifier.data, source->data, sizeof(target->cl_verifier.data));}static voidcopy_clid(struct nfs4_client *target, struct nfs4_client *source) { target->cl_clientid.cl_boot = source->cl_clientid.cl_boot; target->cl_clientid.cl_id = source->cl_clientid.cl_id; }static voidcopy_cred(struct svc_cred *target, struct svc_cred *source) { target->cr_uid = source->cr_uid; target->cr_gid = source->cr_gid; target->cr_group_info = source->cr_group_info; get_group_info(target->cr_group_info);}static intcmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) { if (!n1 || !n2) return 0; return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len));}static intcmp_verf(nfs4_verifier *v1, nfs4_verifier *v2) { return(!memcmp(v1->data,v2->data,sizeof(v1->data)));}static intcmp_clid(clientid_t * cl1, clientid_t * cl2) { return((cl1->cl_boot == cl2->cl_boot) && (cl1->cl_id == cl2->cl_id));}/* XXX what about NGROUP */static intcmp_creds(struct svc_cred *cr1, struct svc_cred *cr2){ return(cr1->cr_uid == cr2->cr_uid);}static voidgen_clid(struct nfs4_client *clp) { clp->cl_clientid.cl_boot = boot_time; clp->cl_clientid.cl_id = current_clientid++; }static voidgen_confirm(struct nfs4_client *clp) { struct timespec tv; u32 * p; tv = CURRENT_TIME; p = (u32 *)clp->cl_confirm.data; *p++ = tv.tv_sec; *p++ = tv.tv_nsec;}static intcheck_name(struct xdr_netobj name) { if (name.len == 0) return 0; if (name.len > NFS4_OPAQUE_LIMIT) { printk("NFSD: check_name: name too long(%d)!\n", name.len); return 0; } return 1;}voidadd_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval){ unsigned int idhashval; list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); idhashval = clientid_hashval(clp->cl_clientid.cl_id); list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); list_add_tail(&clp->cl_lru, &client_lru); clp->cl_time = get_seconds();}voidmove_to_confirmed(struct nfs4_client *clp, unsigned int idhashval){ unsigned int strhashval; dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); list_del_init(&clp->cl_strhash); list_del_init(&clp->cl_idhash); list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len); list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); renew_client(clp);}/* a helper function for parse_callback */static intparse_octet(unsigned int *lenp, char **addrp){ unsigned int len = *lenp; char *p = *addrp; int n = -1; char c; for (;;) { if (!len) break; len--; c = *p++; if (c == '.') break; if ((c < '0') || (c > '9')) { n = -1; break; } if (n < 0) n = 0; n = (n * 10) + (c - '0'); if (n > 255) { n = -1; break; } } *lenp = len; *addrp = p; return n;}/* parse and set the setclientid ipv4 callback address */intparse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp){ int temp = 0; u32 cbaddr = 0; u16 cbport = 0; u32 addrlen = addr_len; char *addr = addr_val; int i, shift; /* ipaddress */ shift = 24; for(i = 4; i > 0 ; i--) { if ((temp = parse_octet(&addrlen, &addr)) < 0) { return 0; } cbaddr |= (temp << shift); if (shift > 0) shift -= 8; } *cbaddrp = cbaddr; /* port */ shift = 8; for(i = 2; i > 0 ; i--) { if ((temp = parse_octet(&addrlen, &addr)) < 0) { return 0; } cbport |= (temp << shift); if (shift > 0) shift -= 8; } *cbportp = cbport; return 1;}voidgen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se){ struct nfs4_callback *cb = &clp->cl_callback; if ( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val, &cb->cb_addr, &cb->cb_port))) { printk(KERN_INFO "NFSD: BAD callback address. client will not receive delegations\n"); cb->cb_parsed = 0; return; } cb->cb_netid.len = se->se_callback_netid_len; cb->cb_netid.data = se->se_callback_netid_val; cb->cb_prog = se->se_callback_prog; cb->cb_ident = se->se_callback_ident; cb->cb_parsed = 1;}/* * RFC 3010 has a complex implmentation description of processing a * SETCLIENTID request consisting of 5 bullets, labeled as * CASE0 - CASE4 below. * * NOTES: * callback information will be processed in a future patch * * an unconfirmed record is added when: * NORMAL (part of CASE 4): there is no confirmed nor unconfirmed record. * CASE 1: confirmed record found with matching name, principal, * verifier, and clientid. * CASE 2: confirmed record found with matching name, principal, * and there is no unconfirmed record with matching * name and principal * * an unconfirmed record is replaced when: * CASE 3: confirmed record found with matching name, principal, * and an unconfirmed record is found with matching * name, principal, and with clientid and * confirm that does not match the confirmed record. * CASE 4: there is no confirmed record with matching name and * principal. there is an unconfirmed record with * matching name, principal. * * an unconfirmed record is deleted when: * CASE 1: an unconfirmed record that matches input name, verifier, * and confirmed clientid. * CASE 4: any unconfirmed records with matching name and principal * that exist after an unconfirmed record has been replaced * as described above. * */intnfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid){ u32 ip_addr = rqstp->rq_addr.sin_addr.s_addr; struct xdr_netobj clname = { .len = setclid->se_namelen, .data = setclid->se_name, }; nfs4_verifier clverifier = setclid->se_verf; unsigned int strhashval; struct nfs4_client * conf, * unconf, * new, * clp; int status; status = nfserr_inval;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -