📄 export.c
字号:
#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/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/module.h>#include <linux/exportfs.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>#include <linux/sunrpc/msg_prot.h>#include <linux/sunrpc/gss_api.h>#define NFSDDBG_FACILITY NFSDDBG_EXPORTtypedef 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 void expkey_put(struct kref *ref){ struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); if (test_bit(CACHE_VALID, &key->h.flags) && !test_bit(CACHE_NEGATIVE, &key->h.flags)) { dput(key->ek_dentry); mntput(key->ek_mnt); } auth_domain_put(key->ek_client); kfree(key);}static 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_update(struct svc_expkey *new, struct svc_expkey *old);static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *);static struct cache_detail svc_expkey_cache;static 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; struct svc_expkey *ek; 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 (key_len(fsidtype)==0) /* invalid type */ 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); ek = svc_expkey_lookup(&key); err = -ENOMEM; if (!ek) goto out; /* now we want a pathname, or empty meaning NEGATIVE */ err = -EINVAL; if ((len=qword_get(&mesg, buf, PAGE_SIZE)) < 0) goto out; dprintk("Path seems to be <%s>\n", buf); err = 0; if (len == 0) { set_bit(CACHE_NEGATIVE, &key.h.flags); ek = svc_expkey_update(&key, ek); if (ek) cache_put(&ek->h, &svc_expkey_cache); else err = -ENOMEM; } else { struct nameidata nd; err = path_lookup(buf, 0, &nd); if (err) goto out; dprintk("Found the path %s\n", buf); key.ek_mnt = nd.mnt; key.ek_dentry = nd.dentry; ek = svc_expkey_update(&key, ek); if (ek) cache_put(&ek->h, &svc_expkey_cache); else err = -ENOMEM; path_release(&nd); } cache_flush(); out: if (dom) auth_domain_put(dom); kfree(buf); return err;}static int expkey_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h){ struct svc_expkey *ek ; int i; 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", ek->ek_client->name, ek->ek_fsidtype); for (i=0; i < key_len(ek->ek_fsidtype)/4; i++) seq_printf(m, "%08x", ek->ek_fsid[i]); if (test_bit(CACHE_VALID, &h->flags) && !test_bit(CACHE_NEGATIVE, &h->flags)) { seq_printf(m, " "); seq_path(m, ek->ek_mnt, ek->ek_dentry, "\\ \t\n"); } seq_printf(m, "\n"); return 0;}static inline int expkey_match (struct cache_head *a, struct cache_head *b){ struct svc_expkey *orig = container_of(a, struct svc_expkey, h); struct svc_expkey *new = container_of(b, struct svc_expkey, h); if (orig->ek_fsidtype != new->ek_fsidtype || orig->ek_client != new->ek_client || memcmp(orig->ek_fsid, new->ek_fsid, key_len(orig->ek_fsidtype)) != 0) return 0; return 1;}static inline void expkey_init(struct cache_head *cnew, struct cache_head *citem){ struct svc_expkey *new = container_of(cnew, struct svc_expkey, h); struct svc_expkey *item = container_of(citem, struct svc_expkey, h); kref_get(&item->ek_client->ref); new->ek_client = item->ek_client; new->ek_fsidtype = item->ek_fsidtype; memcpy(new->ek_fsid, item->ek_fsid, sizeof(new->ek_fsid));}static inline void expkey_update(struct cache_head *cnew, struct cache_head *citem){ struct svc_expkey *new = container_of(cnew, struct svc_expkey, h); struct svc_expkey *item = container_of(citem, struct svc_expkey, h); new->ek_mnt = mntget(item->ek_mnt); new->ek_dentry = dget(item->ek_dentry);}static struct cache_head *expkey_alloc(void){ struct svc_expkey *i = kmalloc(sizeof(*i), GFP_KERNEL); if (i) return &i->h; else return NULL;}static struct cache_detail svc_expkey_cache = { .owner = THIS_MODULE, .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, .match = expkey_match, .init = expkey_init, .update = expkey_update, .alloc = expkey_alloc,};static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *item){ struct cache_head *ch; 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); hash &= EXPKEY_HASHMASK; ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h, hash); if (ch) return container_of(ch, struct svc_expkey, h); else return NULL;}static struct svc_expkey *svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old){ struct cache_head *ch; int hash = new->ek_fsidtype; char * cp = (char*)new->ek_fsid; int len = key_len(new->ek_fsidtype); hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); hash ^= hash_ptr(new->ek_client, EXPKEY_HASHBITS); hash &= EXPKEY_HASHMASK; ch = sunrpc_cache_update(&svc_expkey_cache, &new->h, &old->h, hash); if (ch) return container_of(ch, struct svc_expkey, h); else return NULL;}#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 void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc){ int i; for (i = 0; i < fsloc->locations_count; i++) { kfree(fsloc->locations[i].path); kfree(fsloc->locations[i].hosts); } kfree(fsloc->locations);}static void svc_export_put(struct kref *ref){ struct svc_export *exp = container_of(ref, struct svc_export, h.ref); dput(exp->ex_dentry); mntput(exp->ex_mnt); auth_domain_put(exp->ex_client); kfree(exp->ex_path); nfsd4_fslocs_free(&exp->ex_fslocs); kfree(exp);}static 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_update(struct svc_export *new, struct svc_export *old);static struct svc_export *svc_export_lookup(struct svc_export *);static int check_export(struct inode *inode, int flags, unsigned char *uuid){ /* 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 or ->uuid is 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) && uuid == NULL) { dprintk("exp_export: export of non-dev fs without fsid\n"); return -EINVAL; } if (!inode->i_sb->s_export_op || !inode->i_sb->s_export_op->fh_to_dentry) { dprintk("exp_export: export of invalid fs type.\n"); return -EINVAL; } return 0;}#ifdef CONFIG_NFSD_V4static intfsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc){ int len; int migrated, i, err; /* listsize */ err = get_int(mesg, &fsloc->locations_count); if (err) return err; if (fsloc->locations_count > MAX_FS_LOCATIONS) return -EINVAL; if (fsloc->locations_count == 0) return 0; fsloc->locations = kzalloc(fsloc->locations_count * sizeof(struct nfsd4_fs_location), GFP_KERNEL); if (!fsloc->locations) return -ENOMEM; for (i=0; i < fsloc->locations_count; i++) { /* colon separated host list */ err = -EINVAL; len = qword_get(mesg, buf, PAGE_SIZE); if (len <= 0) goto out_free_all; err = -ENOMEM; fsloc->locations[i].hosts = kstrdup(buf, GFP_KERNEL); if (!fsloc->locations[i].hosts) goto out_free_all; err = -EINVAL; /* slash separated path component list */ len = qword_get(mesg, buf, PAGE_SIZE); if (len <= 0) goto out_free_all; err = -ENOMEM; fsloc->locations[i].path = kstrdup(buf, GFP_KERNEL); if (!fsloc->locations[i].path) goto out_free_all; } /* migrated */ err = get_int(mesg, &migrated); if (err) goto out_free_all; err = -EINVAL; if (migrated < 0 || migrated > 1) goto out_free_all; fsloc->migrated = migrated; return 0;out_free_all: nfsd4_fslocs_free(fsloc); return err;}static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp){ int listsize, err; struct exp_flavor_info *f; err = get_int(mesg, &listsize); if (err) return err; if (listsize < 0 || listsize > MAX_SECINFO_LIST) return -EINVAL; for (f = exp->ex_flavors; f < exp->ex_flavors + listsize; f++) { err = get_int(mesg, &f->pseudoflavor); if (err) return err; /* * Just a quick sanity check; we could also try to check * whether this pseudoflavor is supported, but at worst * an unsupported pseudoflavor on the export would just * be a pseudoflavor that won't match the flavor of any * authenticated request. The administrator will * probably discover the problem when someone fails to * authenticate. */ if (f->pseudoflavor < 0) return -EINVAL; err = get_int(mesg, &f->flags); if (err) return err; /* Only some flags are allowed to differ between flavors: */ if (~NFSEXP_SECINFO_FLAGS & (f->flags ^ exp->ex_flags)) return -EINVAL; } exp->ex_nflavors = listsize; return 0;}#else /* CONFIG_NFSD_V4 */static inline intfsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc){return 0;}static inline intsecinfo_parse(char **mesg, char *buf, struct svc_export *exp) { return 0; }#endifstatic 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; exp.ex_path = NULL; /* fs locations */ exp.ex_fslocs.locations = NULL; exp.ex_fslocs.locations_count = 0; exp.ex_fslocs.migrated = 0; exp.ex_uuid = NULL; /* secinfo */ exp.ex_nflavors = 0; 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_no_path; exp.h.flags = 0; exp.ex_client = dom; exp.ex_mnt = nd.mnt; exp.ex_dentry = nd.dentry; exp.ex_path = kstrdup(buf, GFP_KERNEL); err = -ENOMEM; if (!exp.ex_path) goto out; /* expiry */ err = -EINVAL; exp.h.expiry_time = get_expiry(&mesg); if (exp.h.expiry_time == 0) goto out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -