Release 4.10 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/slab.h>
#include <linux/namei.h>
#include <linux/module.h>
#include <linux/exportfs.h>
#include <linux/sunrpc/svc_xprt.h>
#include "nfsd.h"
#include "nfsfh.h"
#include "netns.h"
#include "pnfs.h"
#define NFSDDBG_FACILITY NFSDDBG_EXPORT
/*
* 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 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))
path_put(&key->ek_path);
auth_domain_put(key->ek_client);
kfree(key);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 71 | 94.67% | 4 | 66.67% |
jan blunck | jan blunck | 3 | 4.00% | 1 | 16.67% |
adrian bunk | adrian bunk | 1 | 1.33% | 1 | 16.67% |
| Total | 75 | 100.00% | 6 | 100.00% |
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';
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 112 | 97.39% | 3 | 75.00% |
al viro | al viro | 3 | 2.61% | 1 | 25.00% |
| Total | 115 | 100.00% | 4 | 100.00% |
static struct svc_expkey *svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new,
struct svc_expkey *old);
static struct svc_expkey *svc_expkey_lookup(struct cache_detail *cd, struct svc_expkey *);
static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
{
/* client fsidtype fsid expiry [path] */
char *buf;
int len;
struct auth_domain *dom = NULL;
int err;
int fsidtype;
char *ep;
struct svc_expkey key;
struct svc_expkey *ek = NULL;
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(cd, &key);
err = -ENOMEM;
if (!ek)
goto out;
/* now we want a pathname, or empty meaning NEGATIVE */
err = -EINVAL;
len = qword_get(&mesg, buf, PAGE_SIZE);
if (len < 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(cd, &key, ek);
if (!ek)
err = -ENOMEM;
} else {
err = kern_path(buf, 0, &key.ek_path);
if (err)
goto out;
dprintk("Found the path %s\n", buf);
ek = svc_expkey_update(cd, &key, ek);
if (!ek)
err = -ENOMEM;
path_put(&key.ek_path);
}
cache_flush();
out:
if (ek)
cache_put(&ek->h, cd);
if (dom)
auth_domain_put(dom);
kfree(buf);
return err;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 470 | 91.09% | 6 | 42.86% |
j. bruce fields | j. bruce fields | 21 | 4.07% | 1 | 7.14% |
al viro | al viro | 9 | 1.74% | 2 | 14.29% |
stanislav kinsbursky | stanislav kinsbursky | 7 | 1.36% | 2 | 14.29% |
frank filz | frank filz | 6 | 1.16% | 1 | 7.14% |
jan blunck | jan blunck | 2 | 0.39% | 1 | 7.14% |
kinglong mee | kinglong mee | 1 | 0.19% | 1 | 7.14% |
| Total | 516 | 100.00% | 14 | 100.00% |
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_path, "\\ \t\n");
}
seq_printf(m, "\n");
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 158 | 96.34% | 2 | 40.00% |
al viro | al viro | 4 | 2.44% | 1 | 20.00% |
jan blunck | jan blunck | 2 | 1.22% | 2 | 40.00% |
| Total | 164 | 100.00% | 5 | 100.00% |
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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 76 | 83.52% | 3 | 50.00% |
al viro | al viro | 11 | 12.09% | 2 | 33.33% |
pre-git | pre-git | 4 | 4.40% | 1 | 16.67% |
| Total | 91 | 100.00% | 6 | 100.00% |
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));
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 91 | 100.00% | 5 | 100.00% |
| Total | 91 | 100.00% | 5 | 100.00% |
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_path = item->ek_path;
path_get(&item->ek_path);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 58 | 92.06% | 1 | 50.00% |
jan blunck | jan blunck | 5 | 7.94% | 1 | 50.00% |
| Total | 63 | 100.00% | 2 | 100.00% |
static struct cache_head *expkey_alloc(void)
{
struct svc_expkey *i = kmalloc(sizeof(*i), GFP_KERNEL);
if (i)
return &i->h;
else
return NULL;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 40 | 100.00% | 1 | 100.00% |
| Total | 40 | 100.00% | 1 | 100.00% |
static struct cache_detail svc_expkey_cache_template = {
.owner = THIS_MODULE,
.hash_size = EXPKEY_HASHMAX,
.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 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);
hash &= EXPKEY_HASHMASK;
return hash;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 69 | 100.00% | 3 | 100.00% |
| Total | 69 | 100.00% | 3 | 100.00% |
static struct svc_expkey *
svc_expkey_lookup(struct cache_detail *cd, struct svc_expkey *item)
{
struct cache_head *ch;
int hash = svc_expkey_hash(item);
ch = sunrpc_cache_lookup(cd, &item->h, hash);
if (ch)
return container_of(ch, struct svc_expkey, h);
else
return NULL;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 54 | 84.38% | 3 | 60.00% |
stanislav kinsbursky | stanislav kinsbursky | 6 | 9.38% | 1 | 20.00% |
al viro | al viro | 4 | 6.25% | 1 | 20.00% |
| Total | 64 | 100.00% | 5 | 100.00% |
static struct svc_expkey *
svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new,
struct svc_expkey *old)
{
struct cache_head *ch;
int hash = svc_expkey_hash(new);
ch = sunrpc_cache_update(cd, &new->h, &old->h, hash);
if (ch)
return container_of(ch, struct svc_expkey, h);
else
return NULL;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 68 | 91.89% | 5 | 83.33% |
stanislav kinsbursky | stanislav kinsbursky | 6 | 8.11% | 1 | 16.67% |
| Total | 74 | 100.00% | 6 | 100.00% |
#define EXPORT_HASHBITS 8
#define EXPORT_HASHMAX (1<< EXPORT_HASHBITS)
static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc)
{
struct nfsd4_fs_location *locations = fsloc->locations;
int i;
if (!locations)
return;
for (i = 0; i < fsloc->locations_count; i++) {
kfree(locations[i].path);
kfree(locations[i].hosts);
}
kfree(locations);
fsloc->locations = NULL;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
manoj naik | manoj naik | 57 | 74.03% | 1 | 50.00% |
kinglong mee | kinglong mee | 20 | 25.97% | 1 | 50.00% |
| Total | 77 | 100.00% | 2 | 100.00% |
static void svc_export_put(struct kref *ref)
{
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
path_put(&exp->ex_path);
auth_domain_put(exp->ex_client);
nfsd4_fslocs_free(&exp->ex_fslocs);
kfree(exp->ex_uuid);
kfree(exp);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 42 | 66.67% | 3 | 42.86% |
manoj naik | manoj naik | 8 | 12.70% | 1 | 14.29% |
ma jianpeng | ma jianpeng | 7 | 11.11% | 1 | 14.29% |
jan blunck | jan blunck | 3 | 4.76% | 1 | 14.29% |
j. bruce fields | j. bruce fields | 3 | 4.76% | 1 | 14.29% |
| Total | 63 | 100.00% | 7 | 100.00% |
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_path, *bpp, *blen);
if (IS_ERR(pth)) {
/* is this correct? */
(*bpp)[0] = '\n';
return;
}
qword_add(bpp, blen, pth);
(*bpp)[-1] = '\n';
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 92 | 80.00% | 4 | 57.14% |
andrea arcangeli | andrea arcangeli | 21 | 18.26% | 1 | 14.29% |
jan blunck | jan blunck | 2 | 1.74% | 2 | 28.57% |
| Total | 115 | 100.00% | 7 | 100.00% |
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, regular files, and (for v4
* pseudoroot) symlinks.
*/
if (!S_ISDIR(inode->i_mode) &&
!S_ISLNK(inode->i_mode) &&
!S_ISREG(inode->i_mode))
return -ENOTDIR;
/*
* Mountd should never pass down a writeable V4ROOT export, but,
* just to make sure:
*/
if (*flags & NFSEXP_V4ROOT)
*flags |= NFSEXP_READONLY;
/* 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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
neil brown | neil brown | 101 | 74.81% | 2 | 33.33% |
j. bruce fields | j. bruce fields | 24 | 17.78% | 2 | 33.33% |
christoph hellwig | christoph hellwig | 9 | 6.67% | 1 | 16.67% |
greg banks | greg banks | 1 | 0.74% | 1 | 16.67% |
| Total | 135 | 100.00% | 6 | 100.00% |
#ifdef CONFIG_NFSD_V4
static int
fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc)
{
int len;
int migrated, i, err;
/* more than one fsloc */
if (fsloc->locations)
return -EINVAL;
/* listsize */
err = get_uint(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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
manoj naik | manoj naik | 298 | 96.13% | 1 | 33.33% |
kinglong mee | kinglong mee | 11 | 3.55% | 1 | 33.33% |
j. bruce fields | j. bruce fields | 1 | 0.32% | 1 | 33.33% |
| Total | 310 | 100.00% | 3 | 100.00% |
static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp)
{
struct exp_flavor_info *f;
u32 listsize;
int err;
/* more than one secinfo */
if (exp->ex_nflavors)
return -EINVAL;
err = get_uint(mesg, &listsize);
if (err)
return err;
if (listsize > MAX_SECINFO_LIST)
return -EINVAL;
for (f = exp->ex_flavors; f < exp->ex_flavors + listsize; f++) {
err = get_uint(mesg, &f->pseudoflavor);
if (err)
return err;
/*
* XXX: It would be nice to also check whether this
* pseudoflavor is supported, so we can discover the
* problem at export time instead of when a client fails
* to authenticate.
*/
err = get_uint(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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
andy adamson | andy adamson | 137 | 86.71% | 1 | 20.00% |
kinglong mee | kinglong mee | 18 | 11.39% | 2 | 40.00% |
j. bruce fields | j. bruce fields | 2 | 1.27% | 1 | 20.00% |
roel kluin | roel kluin | 1 | 0.63% | 1 | 20.00% |
| Total | 158 | 100.00% | 5 | 100.00% |
#else /* CONFIG_NFSD_V4 */
static inline int
fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc){return 0;}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
manoj naik | manoj naik | 24 | 100.00% | 1 | 100.00% |
| Total | 24 | 100.00% | 1 | 100.00% |
static inline int
secinfo_parse(char **mesg, char *buf, struct svc_export *exp) { return 0; }
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
andy adamson | andy adamson | 24 | 100.00% | 1 | 100.00% |
| Total | 24 | 100.00% | 1 | 100.00% |
#endif
static inline int
uuid_parse(char **mesg, char *buf, unsigned char **puuid)
{
int len;
/* more than one uuid */
if (*puuid)
return -EINVAL;
/* expect a 16 byte uuid encoded as \xXXXX... */
len = qword_get(mesg, buf, PAGE_SIZE);
if (len != EX_UUID_LEN)
return -EINVAL;
*puuid = kmemdup(buf, EX_UUID_LEN, GFP_KERNEL);
if (*puuid == NULL)
return -ENOMEM;
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
kinglong mee | kinglong mee | 83 | 100.00% | 3 | 100.00% |
| Total | 83 | 100.00% | 3 | 100.00% |
static 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 svc_export exp = {}, *expp;
int an_int;
if (mesg[mlen-1] != '\n')
return -EINVAL;
mesg[mlen-1] = 0;
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* client */
err = -EINVAL;
len = qword_get(&mesg, buf, PAGE_SIZE);
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 out1;
err = kern_path(buf, 0, &exp.ex_path);
if (err)
goto out1;
exp.ex_client = dom;
exp.cd = cd;
exp.ex_devid_map = NULL;
/* expiry */
err = -EINVAL;
exp.h.expiry_time = get_expiry(&mesg);
if (exp.h.expiry_time == 0)
goto out3;
/* flags */
err = get_int(&mesg, &an_int);
if (err == -ENOENT) {
err = 0;
set_bit(CACHE_NEGATIVE, &exp.h.flags);
} else {
if (err || an_int < 0)
goto out3;
exp.ex_flags= an_int;
/* anon uid */
err = get_int(&mesg, &an_int);
if (err)
goto out3;
exp.ex_anon_uid= make_kuid(&init_user_ns, an_int);
/* anon gid */
err = get_int(&mesg, &an_int);
if (err)
goto out3;
exp.ex_anon_gid= make_kgid(&init_user_ns