export.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,206 行 · 第 1/2 页

C
1,206
字号
#define MSNFS	/* HACK HACK *//* * linux/fs/nfsd/export.c * * NFS exporting and validation. * * We maintain a list of clients, each of which has a list of * exports. To export an fs to a given client, you first have * to create the client entry with NFSCTL_ADDCLIENT, which * creates a client control block and adds it to the hash * table. Then, you call NFSCTL_EXPORT for each fs. * * * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de> */#include <linux/unistd.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/stat.h>#include <linux/in.h>#include <linux/seq_file.h>#include <linux/syscalls.h>#include <linux/rwsem.h>#include <linux/dcache.h>#include <linux/namei.h>#include <linux/mount.h>#include <linux/hash.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/nfsfh.h>#include <linux/nfsd/syscall.h>#include <linux/lockd/bind.h>#define NFSDDBG_FACILITY	NFSDDBG_EXPORT#define NFSD_PARANOIA 1typedef struct auth_domain	svc_client;typedef struct svc_export	svc_export;static void		exp_do_unexport(svc_export *unexp);static int		exp_verify_string(char *cp, int max);/* * We have two caches. * One maps client+vfsmnt+dentry to export options - the export map * The other maps client+filehandle-fragment to export options. - the expkey map * * The export options are actually stored in the first map, and the * second map contains a reference to the entry in the first map. */#define	EXPKEY_HASHBITS		8#define	EXPKEY_HASHMAX		(1 << EXPKEY_HASHBITS)#define	EXPKEY_HASHMASK		(EXPKEY_HASHMAX -1)static struct cache_head *expkey_table[EXPKEY_HASHMAX];static inline int svc_expkey_hash(struct svc_expkey *item){	int hash = item->ek_fsidtype;	char * cp = (char*)item->ek_fsid;	int len = key_len(item->ek_fsidtype);	hash ^= hash_mem(cp, len, EXPKEY_HASHBITS);	hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS);	return hash & EXPKEY_HASHMASK;}void expkey_put(struct cache_head *item, struct cache_detail *cd){	if (cache_put(item, cd)) {		struct svc_expkey *key = container_of(item, struct svc_expkey, h);		if (test_bit(CACHE_VALID, &item->flags) &&		    !test_bit(CACHE_NEGATIVE, &item->flags))			exp_put(key->ek_export);		auth_domain_put(key->ek_client);		kfree(key);	}}void expkey_request(struct cache_detail *cd,		    struct cache_head *h,		    char **bpp, int *blen){	/* client fsidtype \xfsid */	struct svc_expkey *ek = container_of(h, struct svc_expkey, h);	char type[5];	qword_add(bpp, blen, ek->ek_client->name);	snprintf(type, 5, "%d", ek->ek_fsidtype);	qword_add(bpp, blen, type);	qword_addhex(bpp, blen, (char*)ek->ek_fsid, key_len(ek->ek_fsidtype));	(*bpp)[-1] = '\n';}static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *, int);int expkey_parse(struct cache_detail *cd, char *mesg, int mlen){	/* client fsidtype fsid [path] */	char *buf;	int len;	struct auth_domain *dom = NULL;	int err;	int fsidtype;	char *ep;	struct svc_expkey key;	if (mesg[mlen-1] != '\n')		return -EINVAL;	mesg[mlen-1] = 0;	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);	err = -ENOMEM;	if (!buf) goto out;	err = -EINVAL;	if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0)		goto out;	err = -ENOENT;	dom = auth_domain_find(buf);	if (!dom)		goto out;	dprintk("found domain %s\n", buf);	err = -EINVAL;	if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0)		goto out;	fsidtype = simple_strtoul(buf, &ep, 10);	if (*ep)		goto out;	dprintk("found fsidtype %d\n", fsidtype);	if (fsidtype > 2)		goto out;	if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0)		goto out;	dprintk("found fsid length %d\n", len);	if (len != key_len(fsidtype))		goto out;	/* OK, we seem to have a valid key */	key.h.flags = 0;	key.h.expiry_time = get_expiry(&mesg);	if (key.h.expiry_time == 0)		goto out;	key.ek_client = dom;		key.ek_fsidtype = fsidtype;	memcpy(key.ek_fsid, buf, len);	/* now we want a pathname, or empty meaning NEGATIVE  */	if ((len=qword_get(&mesg, buf, PAGE_SIZE)) < 0)		goto out;	dprintk("Path seems to be <%s>\n", buf);	err = 0;	if (len == 0) {		struct svc_expkey *ek;		set_bit(CACHE_NEGATIVE, &key.h.flags);		ek = svc_expkey_lookup(&key, 1);		if (ek)			expkey_put(&ek->h, &svc_expkey_cache);	} else {		struct nameidata nd;		struct svc_expkey *ek;		struct svc_export *exp;		err = path_lookup(buf, 0, &nd);		if (err)			goto out;		dprintk("Found the path %s\n", buf);		exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL);		err = -ENOENT;		if (!exp)			goto out_nd;		key.ek_export = exp;		dprintk("And found export\n");				ek = svc_expkey_lookup(&key, 1);		if (ek)			expkey_put(&ek->h, &svc_expkey_cache);		exp_put(exp);		err = 0;	out_nd:		path_release(&nd);	}	cache_flush(); out:	if (dom)		auth_domain_put(dom);	if (buf)		kfree(buf);	return err;}static int expkey_show(struct seq_file *m,		       struct cache_detail *cd,		       struct cache_head *h){	struct svc_expkey *ek ;	if (h ==NULL) {		seq_puts(m, "#domain fsidtype fsid [path]\n");		return 0;	}	ek = container_of(h, struct svc_expkey, h);	seq_printf(m, "%s %d 0x%08x", ek->ek_client->name,		   ek->ek_fsidtype, ek->ek_fsid[0]);	if (ek->ek_fsidtype != 1)		seq_printf(m, "%08x", ek->ek_fsid[1]);	if (ek->ek_fsidtype == 2)		seq_printf(m, "%08x", ek->ek_fsid[2]);	if (test_bit(CACHE_VALID, &h->flags) && 	    !test_bit(CACHE_NEGATIVE, &h->flags)) {		seq_printf(m, " ");		seq_path(m, ek->ek_export->ex_mnt, ek->ek_export->ex_dentry, "\\ \t\n");	}	seq_printf(m, "\n");	return 0;}	struct cache_detail svc_expkey_cache = {	.hash_size	= EXPKEY_HASHMAX,	.hash_table	= expkey_table,	.name		= "nfsd.fh",	.cache_put	= expkey_put,	.cache_request	= expkey_request,	.cache_parse	= expkey_parse,	.cache_show	= expkey_show,};static inline int svc_expkey_match (struct svc_expkey *a, struct svc_expkey *b){	if (a->ek_fsidtype != b->ek_fsidtype ||	    a->ek_client != b->ek_client ||	    memcmp(a->ek_fsid, b->ek_fsid, key_len(a->ek_fsidtype)) != 0)		return 0;	return 1;}static inline void svc_expkey_init(struct svc_expkey *new, struct svc_expkey *item){	cache_get(&item->ek_client->h);	new->ek_client = item->ek_client;	new->ek_fsidtype = item->ek_fsidtype;	new->ek_fsid[0] = item->ek_fsid[0];	new->ek_fsid[1] = item->ek_fsid[1];	new->ek_fsid[2] = item->ek_fsid[2];}static inline void svc_expkey_update(struct svc_expkey *new, struct svc_expkey *item){	cache_get(&item->ek_export->h);	new->ek_export = item->ek_export;}static DefineSimpleCacheLookup(svc_expkey,0) /* no inplace updates */#define	EXPORT_HASHBITS		8#define	EXPORT_HASHMAX		(1<< EXPORT_HASHBITS)#define	EXPORT_HASHMASK		(EXPORT_HASHMAX -1)static struct cache_head *export_table[EXPORT_HASHMAX];static inline int svc_export_hash(struct svc_export *item){	int rv;	rv = hash_ptr(item->ex_client, EXPORT_HASHBITS);	rv ^= hash_ptr(item->ex_dentry, EXPORT_HASHBITS);	rv ^= hash_ptr(item->ex_mnt, EXPORT_HASHBITS);	return rv;}void svc_export_put(struct cache_head *item, struct cache_detail *cd){	if (cache_put(item, cd)) {		struct svc_export *exp = container_of(item, struct svc_export, h);		dput(exp->ex_dentry);		mntput(exp->ex_mnt);		auth_domain_put(exp->ex_client);		kfree(exp);	}}void svc_export_request(struct cache_detail *cd,			struct cache_head *h,			char **bpp, int *blen){	/*  client path */	struct svc_export *exp = container_of(h, struct svc_export, h);	char *pth;	qword_add(bpp, blen, exp->ex_client->name);	pth = d_path(exp->ex_dentry, exp->ex_mnt, *bpp, *blen);	if (IS_ERR(pth)) {		/* is this correct? */		(*bpp)[0] = '\n';		return;	}	qword_add(bpp, blen, pth);	(*bpp)[-1] = '\n';}static struct svc_export *svc_export_lookup(struct svc_export *, int);extern struct dentry *find_exported_dentry(struct super_block *sb, void *obj, void *parent,		     int (*acceptable)(void *context, struct dentry *de),		     void *context);static int check_export(struct inode *inode, int flags){	/* We currently export only dirs and regular files.	 * This is what umountd does.	 */	if (!S_ISDIR(inode->i_mode) &&	    !S_ISREG(inode->i_mode))		return -ENOTDIR;	/* There are two requirements on a filesystem to be exportable.	 * 1:  We must be able to identify the filesystem from a number.	 *       either a device number (so FS_REQUIRES_DEV needed)	 *       or an FSID number (so NFSEXP_FSID needed).	 * 2:  We must be able to find an inode from a filehandle.	 *       This means that s_export_op must be set.	 */	if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) &&	    !(flags & NFSEXP_FSID)) {		dprintk("exp_export: export of non-dev fs without fsid");		return -EINVAL;	}	if (!inode->i_sb->s_export_op) {		dprintk("exp_export: export of invalid fs type.\n");		return -EINVAL;	}	/* Ok, we can export it */;	if (!inode->i_sb->s_export_op->find_exported_dentry)		inode->i_sb->s_export_op->find_exported_dentry =			find_exported_dentry;	return 0;}int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen){	/* client path expiry [flags anonuid anongid fsid] */	char *buf;	int len;	int err;	struct auth_domain *dom = NULL;	struct nameidata nd;	struct svc_export exp, *expp;	int an_int;	nd.dentry = NULL;	if (mesg[mlen-1] != '\n')		return -EINVAL;	mesg[mlen-1] = 0;	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);	err = -ENOMEM;	if (!buf) goto out;	/* client */	len = qword_get(&mesg, buf, PAGE_SIZE);	err = -EINVAL;	if (len <= 0) goto out;	err = -ENOENT;	dom = auth_domain_find(buf);	if (!dom)		goto out;	/* path */	err = -EINVAL;	if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0)		goto out;	err = path_lookup(buf, 0, &nd);	if (err) goto out;	exp.h.flags = 0;	exp.ex_client = dom;	exp.ex_mnt = nd.mnt;	exp.ex_dentry = nd.dentry;	/* expiry */	err = -EINVAL;	exp.h.expiry_time = get_expiry(&mesg);	if (exp.h.expiry_time == 0)		goto out;	/* flags */	err = get_int(&mesg, &an_int);	if (err == -ENOENT)		set_bit(CACHE_NEGATIVE, &exp.h.flags);	else {		if (err || an_int < 0) goto out;			exp.ex_flags= an_int;			/* anon uid */		err = get_int(&mesg, &an_int);		if (err) goto out;		exp.ex_anon_uid= an_int;		/* anon gid */		err = get_int(&mesg, &an_int);		if (err) goto out;		exp.ex_anon_gid= an_int;		/* fsid */		err = get_int(&mesg, &an_int);		if (err) goto out;		exp.ex_fsid = an_int;		err = check_export(nd.dentry->d_inode, exp.ex_flags);		if (err) goto out;	}	expp = svc_export_lookup(&exp, 1);	if (expp)		exp_put(expp);	err = 0;	cache_flush(); out:	if (nd.dentry)		path_release(&nd);	if (dom)		auth_domain_put(dom);	if (buf)		kfree(buf);	return err;}static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong);static int svc_export_show(struct seq_file *m,			   struct cache_detail *cd,			   struct cache_head *h){	struct svc_export *exp ;	if (h ==NULL) {		seq_puts(m, "#path domain(flags)\n");		return 0;	}	exp = container_of(h, struct svc_export, h);	seq_path(m, exp->ex_mnt, exp->ex_dentry, " \t\n\\");	seq_putc(m, '\t');	seq_escape(m, exp->ex_client->name, " \t\n\\");	seq_putc(m, '(');	if (test_bit(CACHE_VALID, &h->flags) && 	    !test_bit(CACHE_NEGATIVE, &h->flags))		exp_flags(m, exp->ex_flags, exp->ex_fsid, 			  exp->ex_anon_uid, exp->ex_anon_gid);	seq_puts(m, ")\n");	return 0;}struct cache_detail svc_export_cache = {	.hash_size	= EXPORT_HASHMAX,	.hash_table	= export_table,	.name		= "nfsd.export",	.cache_put	= svc_export_put,	.cache_request	= svc_export_request,	.cache_parse	= svc_export_parse,	.cache_show	= svc_export_show,};static inline int svc_export_match(struct svc_export *a, struct svc_export *b){	return a->ex_client == b->ex_client &&		a->ex_dentry == b->ex_dentry &&		a->ex_mnt == b->ex_mnt;}static inline void svc_export_init(struct svc_export *new, struct svc_export *item){	cache_get(&item->ex_client->h);	new->ex_client = item->ex_client;	new->ex_dentry = dget(item->ex_dentry);	new->ex_mnt = mntget(item->ex_mnt);}static inline void svc_export_update(struct svc_export *new, struct svc_export *item){	new->ex_flags = item->ex_flags;	new->ex_anon_uid = item->ex_anon_uid;	new->ex_anon_gid = item->ex_anon_gid;	new->ex_fsid = item->ex_fsid;}static DefineSimpleCacheLookup(svc_export,1) /* allow inplace updates */struct svc_expkey *exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp){	struct svc_expkey key, *ek;	int err;		if (!clp)		return NULL;	key.ek_client = clp;	key.ek_fsidtype = fsid_type;	memcpy(key.ek_fsid, fsidv, key_len(fsid_type));	ek = svc_expkey_lookup(&key, 0);	if (ek != NULL)		if ((err = cache_check(&svc_expkey_cache, &ek->h, reqp)))			ek = ERR_PTR(err);	return ek;}int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv, 		struct svc_export *exp){	struct svc_expkey key, *ek;	key.ek_client = clp;	key.ek_fsidtype = fsid_type;	memcpy(key.ek_fsid, fsidv, key_len(fsid_type));	key.ek_export = exp;	key.h.expiry_time = NEVER;	key.h.flags = 0;	ek = svc_expkey_lookup(&key, 1);	if (ek) {		expkey_put(&ek->h, &svc_expkey_cache);		return 0;	}	return -ENOMEM;}/* * Find the client's export entry matching xdev/xino. */static inline struct svc_expkey *exp_get_key(svc_client *clp, dev_t dev, ino_t ino){	u32 fsidv[3];		if (old_valid_dev(dev)) {		mk_fsid_v0(fsidv, dev, ino);		return exp_find_key(clp, 0, fsidv, NULL);	}	mk_fsid_v3(fsidv, dev, ino);	return exp_find_key(clp, 3, fsidv, NULL);}/* * Find the client's export entry matching fsid */static inline struct svc_expkey *exp_get_fsid_key(svc_client *clp, int fsid){	u32 fsidv[2];	mk_fsid_v1(fsidv, fsid);	return exp_find_key(clp, 1, fsidv, NULL);}svc_export *exp_get_by_name(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry,		struct cache_req *reqp){	struct svc_export *exp, key;		if (!clp)		return NULL;	key.ex_client = clp;	key.ex_mnt = mnt;	key.ex_dentry = dentry;	exp = svc_export_lookup(&key, 0);	if (exp != NULL) 		switch (cache_check(&svc_export_cache, &exp->h, reqp)) {		case 0: break;		case -EAGAIN:			exp = ERR_PTR(-EAGAIN);			break;		default:			exp = NULL;		}	return exp;}/* * Find the export entry for a given dentry. */struct svc_export *exp_parent(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry,	   struct cache_req *reqp){	svc_export *exp;	dget(dentry);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?