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 + -
显示快捷键?