162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  Mapping of UID/GIDs to name and vice versa.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (c) 2002, 2003 The Regents of the University of
562306a36Sopenharmony_ci *  Michigan.  All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Marius Aamodt Eriksen <marius@umich.edu>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Redistribution and use in source and binary forms, with or without
1062306a36Sopenharmony_ci *  modification, are permitted provided that the following conditions
1162306a36Sopenharmony_ci *  are met:
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *  1. Redistributions of source code must retain the above copyright
1462306a36Sopenharmony_ci *     notice, this list of conditions and the following disclaimer.
1562306a36Sopenharmony_ci *  2. Redistributions in binary form must reproduce the above copyright
1662306a36Sopenharmony_ci *     notice, this list of conditions and the following disclaimer in the
1762306a36Sopenharmony_ci *     documentation and/or other materials provided with the distribution.
1862306a36Sopenharmony_ci *  3. Neither the name of the University nor the names of its
1962306a36Sopenharmony_ci *     contributors may be used to endorse or promote products derived
2062306a36Sopenharmony_ci *     from this software without specific prior written permission.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
2362306a36Sopenharmony_ci *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2462306a36Sopenharmony_ci *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2562306a36Sopenharmony_ci *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2662306a36Sopenharmony_ci *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2762306a36Sopenharmony_ci *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2862306a36Sopenharmony_ci *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2962306a36Sopenharmony_ci *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3062306a36Sopenharmony_ci *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3162306a36Sopenharmony_ci *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3262306a36Sopenharmony_ci *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/module.h>
3662306a36Sopenharmony_ci#include <linux/seq_file.h>
3762306a36Sopenharmony_ci#include <linux/sched.h>
3862306a36Sopenharmony_ci#include <linux/slab.h>
3962306a36Sopenharmony_ci#include <linux/sunrpc/svc_xprt.h>
4062306a36Sopenharmony_ci#include <net/net_namespace.h>
4162306a36Sopenharmony_ci#include "idmap.h"
4262306a36Sopenharmony_ci#include "nfsd.h"
4362306a36Sopenharmony_ci#include "netns.h"
4462306a36Sopenharmony_ci#include "vfs.h"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * Turn off idmapping when using AUTH_SYS.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic bool nfs4_disable_idmapping = true;
5062306a36Sopenharmony_cimodule_param(nfs4_disable_idmapping, bool, 0644);
5162306a36Sopenharmony_ciMODULE_PARM_DESC(nfs4_disable_idmapping,
5262306a36Sopenharmony_ci		"Turn off server's NFSv4 idmapping when using 'sec=sys'");
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/*
5562306a36Sopenharmony_ci * Cache entry
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * XXX we know that IDMAP_NAMESZ < PAGE_SIZE, but it's ugly to rely on
6062306a36Sopenharmony_ci * that.
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct ent {
6462306a36Sopenharmony_ci	struct cache_head h;
6562306a36Sopenharmony_ci	int               type;		       /* User / Group */
6662306a36Sopenharmony_ci	u32               id;
6762306a36Sopenharmony_ci	char              name[IDMAP_NAMESZ];
6862306a36Sopenharmony_ci	char              authname[IDMAP_NAMESZ];
6962306a36Sopenharmony_ci	struct rcu_head	  rcu_head;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Common entry handling */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define ENT_HASHBITS          8
7562306a36Sopenharmony_ci#define ENT_HASHMAX           (1 << ENT_HASHBITS)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void
7862306a36Sopenharmony_cient_init(struct cache_head *cnew, struct cache_head *citm)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct ent *new = container_of(cnew, struct ent, h);
8162306a36Sopenharmony_ci	struct ent *itm = container_of(citm, struct ent, h);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	new->id = itm->id;
8462306a36Sopenharmony_ci	new->type = itm->type;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	strscpy(new->name, itm->name, sizeof(new->name));
8762306a36Sopenharmony_ci	strscpy(new->authname, itm->authname, sizeof(new->authname));
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void
9162306a36Sopenharmony_cient_put(struct kref *ref)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct ent *map = container_of(ref, struct ent, h.ref);
9462306a36Sopenharmony_ci	kfree_rcu(map, rcu_head);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic struct cache_head *
9862306a36Sopenharmony_cient_alloc(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct ent *e = kmalloc(sizeof(*e), GFP_KERNEL);
10162306a36Sopenharmony_ci	if (e)
10262306a36Sopenharmony_ci		return &e->h;
10362306a36Sopenharmony_ci	else
10462306a36Sopenharmony_ci		return NULL;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * ID -> Name cache
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic uint32_t
11262306a36Sopenharmony_ciidtoname_hash(struct ent *ent)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	uint32_t hash;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	hash = hash_str(ent->authname, ENT_HASHBITS);
11762306a36Sopenharmony_ci	hash = hash_long(hash ^ ent->id, ENT_HASHBITS);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* Flip LSB for user/group */
12062306a36Sopenharmony_ci	if (ent->type == IDMAP_TYPE_GROUP)
12162306a36Sopenharmony_ci		hash ^= 1;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return hash;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int
12762306a36Sopenharmony_ciidtoname_upcall(struct cache_detail *cd, struct cache_head *h)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	return sunrpc_cache_pipe_upcall_timeout(cd, h);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void
13362306a36Sopenharmony_ciidtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
13462306a36Sopenharmony_ci    int *blen)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci 	struct ent *ent = container_of(ch, struct ent, h);
13762306a36Sopenharmony_ci	char idstr[11];
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	qword_add(bpp, blen, ent->authname);
14062306a36Sopenharmony_ci	snprintf(idstr, sizeof(idstr), "%u", ent->id);
14162306a36Sopenharmony_ci	qword_add(bpp, blen, ent->type == IDMAP_TYPE_GROUP ? "group" : "user");
14262306a36Sopenharmony_ci	qword_add(bpp, blen, idstr);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	(*bpp)[-1] = '\n';
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int
14862306a36Sopenharmony_ciidtoname_match(struct cache_head *ca, struct cache_head *cb)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct ent *a = container_of(ca, struct ent, h);
15162306a36Sopenharmony_ci	struct ent *b = container_of(cb, struct ent, h);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return (a->id == b->id && a->type == b->type &&
15462306a36Sopenharmony_ci	    strcmp(a->authname, b->authname) == 0);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int
15862306a36Sopenharmony_ciidtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct ent *ent;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (h == NULL) {
16362306a36Sopenharmony_ci		seq_puts(m, "#domain type id [name]\n");
16462306a36Sopenharmony_ci		return 0;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	ent = container_of(h, struct ent, h);
16762306a36Sopenharmony_ci	seq_printf(m, "%s %s %u", ent->authname,
16862306a36Sopenharmony_ci			ent->type == IDMAP_TYPE_GROUP ? "group" : "user",
16962306a36Sopenharmony_ci			ent->id);
17062306a36Sopenharmony_ci	if (test_bit(CACHE_VALID, &h->flags))
17162306a36Sopenharmony_ci		seq_printf(m, " %s", ent->name);
17262306a36Sopenharmony_ci	seq_putc(m, '\n');
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic void
17762306a36Sopenharmony_ciwarn_no_idmapd(struct cache_detail *detail, int has_died)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	printk("nfsd: nfsv4 idmapping failing: has idmapd %s?\n",
18062306a36Sopenharmony_ci			has_died ? "died" : "not been started");
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int         idtoname_parse(struct cache_detail *, char *, int);
18562306a36Sopenharmony_cistatic struct ent *idtoname_lookup(struct cache_detail *, struct ent *);
18662306a36Sopenharmony_cistatic struct ent *idtoname_update(struct cache_detail *, struct ent *,
18762306a36Sopenharmony_ci				   struct ent *);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic const struct cache_detail idtoname_cache_template = {
19062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
19162306a36Sopenharmony_ci	.hash_size	= ENT_HASHMAX,
19262306a36Sopenharmony_ci	.name		= "nfs4.idtoname",
19362306a36Sopenharmony_ci	.cache_put	= ent_put,
19462306a36Sopenharmony_ci	.cache_upcall	= idtoname_upcall,
19562306a36Sopenharmony_ci	.cache_request	= idtoname_request,
19662306a36Sopenharmony_ci	.cache_parse	= idtoname_parse,
19762306a36Sopenharmony_ci	.cache_show	= idtoname_show,
19862306a36Sopenharmony_ci	.warn_no_listener = warn_no_idmapd,
19962306a36Sopenharmony_ci	.match		= idtoname_match,
20062306a36Sopenharmony_ci	.init		= ent_init,
20162306a36Sopenharmony_ci	.update		= ent_init,
20262306a36Sopenharmony_ci	.alloc		= ent_alloc,
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int
20662306a36Sopenharmony_ciidtoname_parse(struct cache_detail *cd, char *buf, int buflen)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct ent ent, *res;
20962306a36Sopenharmony_ci	char *buf1, *bp;
21062306a36Sopenharmony_ci	int len;
21162306a36Sopenharmony_ci	int error = -EINVAL;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (buf[buflen - 1] != '\n')
21462306a36Sopenharmony_ci		return (-EINVAL);
21562306a36Sopenharmony_ci	buf[buflen - 1]= '\0';
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL);
21862306a36Sopenharmony_ci	if (buf1 == NULL)
21962306a36Sopenharmony_ci		return (-ENOMEM);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	memset(&ent, 0, sizeof(ent));
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Authentication name */
22462306a36Sopenharmony_ci	len = qword_get(&buf, buf1, PAGE_SIZE);
22562306a36Sopenharmony_ci	if (len <= 0 || len >= IDMAP_NAMESZ)
22662306a36Sopenharmony_ci		goto out;
22762306a36Sopenharmony_ci	memcpy(ent.authname, buf1, sizeof(ent.authname));
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* Type */
23062306a36Sopenharmony_ci	if (qword_get(&buf, buf1, PAGE_SIZE) <= 0)
23162306a36Sopenharmony_ci		goto out;
23262306a36Sopenharmony_ci	ent.type = strcmp(buf1, "user") == 0 ?
23362306a36Sopenharmony_ci		IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* ID */
23662306a36Sopenharmony_ci	if (qword_get(&buf, buf1, PAGE_SIZE) <= 0)
23762306a36Sopenharmony_ci		goto out;
23862306a36Sopenharmony_ci	ent.id = simple_strtoul(buf1, &bp, 10);
23962306a36Sopenharmony_ci	if (bp == buf1)
24062306a36Sopenharmony_ci		goto out;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* expiry */
24362306a36Sopenharmony_ci	error = get_expiry(&buf, &ent.h.expiry_time);
24462306a36Sopenharmony_ci	if (error)
24562306a36Sopenharmony_ci		goto out;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	error = -ENOMEM;
24862306a36Sopenharmony_ci	res = idtoname_lookup(cd, &ent);
24962306a36Sopenharmony_ci	if (!res)
25062306a36Sopenharmony_ci		goto out;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Name */
25362306a36Sopenharmony_ci	error = -EINVAL;
25462306a36Sopenharmony_ci	len = qword_get(&buf, buf1, PAGE_SIZE);
25562306a36Sopenharmony_ci	if (len < 0 || len >= IDMAP_NAMESZ)
25662306a36Sopenharmony_ci		goto out;
25762306a36Sopenharmony_ci	if (len == 0)
25862306a36Sopenharmony_ci		set_bit(CACHE_NEGATIVE, &ent.h.flags);
25962306a36Sopenharmony_ci	else
26062306a36Sopenharmony_ci		memcpy(ent.name, buf1, sizeof(ent.name));
26162306a36Sopenharmony_ci	error = -ENOMEM;
26262306a36Sopenharmony_ci	res = idtoname_update(cd, &ent, res);
26362306a36Sopenharmony_ci	if (res == NULL)
26462306a36Sopenharmony_ci		goto out;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	cache_put(&res->h, cd);
26762306a36Sopenharmony_ci	error = 0;
26862306a36Sopenharmony_ciout:
26962306a36Sopenharmony_ci	kfree(buf1);
27062306a36Sopenharmony_ci	return error;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic struct ent *
27462306a36Sopenharmony_ciidtoname_lookup(struct cache_detail *cd, struct ent *item)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct cache_head *ch = sunrpc_cache_lookup_rcu(cd, &item->h,
27762306a36Sopenharmony_ci							idtoname_hash(item));
27862306a36Sopenharmony_ci	if (ch)
27962306a36Sopenharmony_ci		return container_of(ch, struct ent, h);
28062306a36Sopenharmony_ci	else
28162306a36Sopenharmony_ci		return NULL;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic struct ent *
28562306a36Sopenharmony_ciidtoname_update(struct cache_detail *cd, struct ent *new, struct ent *old)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h,
28862306a36Sopenharmony_ci						    idtoname_hash(new));
28962306a36Sopenharmony_ci	if (ch)
29062306a36Sopenharmony_ci		return container_of(ch, struct ent, h);
29162306a36Sopenharmony_ci	else
29262306a36Sopenharmony_ci		return NULL;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/*
29762306a36Sopenharmony_ci * Name -> ID cache
29862306a36Sopenharmony_ci */
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic inline int
30162306a36Sopenharmony_cinametoid_hash(struct ent *ent)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	return hash_str(ent->name, ENT_HASHBITS);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic int
30762306a36Sopenharmony_cinametoid_upcall(struct cache_detail *cd, struct cache_head *h)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	return sunrpc_cache_pipe_upcall_timeout(cd, h);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void
31362306a36Sopenharmony_cinametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
31462306a36Sopenharmony_ci    int *blen)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci 	struct ent *ent = container_of(ch, struct ent, h);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	qword_add(bpp, blen, ent->authname);
31962306a36Sopenharmony_ci	qword_add(bpp, blen, ent->type == IDMAP_TYPE_GROUP ? "group" : "user");
32062306a36Sopenharmony_ci	qword_add(bpp, blen, ent->name);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	(*bpp)[-1] = '\n';
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int
32662306a36Sopenharmony_cinametoid_match(struct cache_head *ca, struct cache_head *cb)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct ent *a = container_of(ca, struct ent, h);
32962306a36Sopenharmony_ci	struct ent *b = container_of(cb, struct ent, h);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return (a->type == b->type && strcmp(a->name, b->name) == 0 &&
33262306a36Sopenharmony_ci	    strcmp(a->authname, b->authname) == 0);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int
33662306a36Sopenharmony_cinametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct ent *ent;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (h == NULL) {
34162306a36Sopenharmony_ci		seq_puts(m, "#domain type name [id]\n");
34262306a36Sopenharmony_ci		return 0;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	ent = container_of(h, struct ent, h);
34562306a36Sopenharmony_ci	seq_printf(m, "%s %s %s", ent->authname,
34662306a36Sopenharmony_ci			ent->type == IDMAP_TYPE_GROUP ? "group" : "user",
34762306a36Sopenharmony_ci			ent->name);
34862306a36Sopenharmony_ci	if (test_bit(CACHE_VALID, &h->flags))
34962306a36Sopenharmony_ci		seq_printf(m, " %u", ent->id);
35062306a36Sopenharmony_ci	seq_putc(m, '\n');
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic struct ent *nametoid_lookup(struct cache_detail *, struct ent *);
35562306a36Sopenharmony_cistatic struct ent *nametoid_update(struct cache_detail *, struct ent *,
35662306a36Sopenharmony_ci				   struct ent *);
35762306a36Sopenharmony_cistatic int         nametoid_parse(struct cache_detail *, char *, int);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic const struct cache_detail nametoid_cache_template = {
36062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
36162306a36Sopenharmony_ci	.hash_size	= ENT_HASHMAX,
36262306a36Sopenharmony_ci	.name		= "nfs4.nametoid",
36362306a36Sopenharmony_ci	.cache_put	= ent_put,
36462306a36Sopenharmony_ci	.cache_upcall	= nametoid_upcall,
36562306a36Sopenharmony_ci	.cache_request	= nametoid_request,
36662306a36Sopenharmony_ci	.cache_parse	= nametoid_parse,
36762306a36Sopenharmony_ci	.cache_show	= nametoid_show,
36862306a36Sopenharmony_ci	.warn_no_listener = warn_no_idmapd,
36962306a36Sopenharmony_ci	.match		= nametoid_match,
37062306a36Sopenharmony_ci	.init		= ent_init,
37162306a36Sopenharmony_ci	.update		= ent_init,
37262306a36Sopenharmony_ci	.alloc		= ent_alloc,
37362306a36Sopenharmony_ci};
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic int
37662306a36Sopenharmony_cinametoid_parse(struct cache_detail *cd, char *buf, int buflen)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct ent ent, *res;
37962306a36Sopenharmony_ci	char *buf1;
38062306a36Sopenharmony_ci	int len, error = -EINVAL;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (buf[buflen - 1] != '\n')
38362306a36Sopenharmony_ci		return (-EINVAL);
38462306a36Sopenharmony_ci	buf[buflen - 1]= '\0';
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL);
38762306a36Sopenharmony_ci	if (buf1 == NULL)
38862306a36Sopenharmony_ci		return (-ENOMEM);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	memset(&ent, 0, sizeof(ent));
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* Authentication name */
39362306a36Sopenharmony_ci	len = qword_get(&buf, buf1, PAGE_SIZE);
39462306a36Sopenharmony_ci	if (len <= 0 || len >= IDMAP_NAMESZ)
39562306a36Sopenharmony_ci		goto out;
39662306a36Sopenharmony_ci	memcpy(ent.authname, buf1, sizeof(ent.authname));
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* Type */
39962306a36Sopenharmony_ci	if (qword_get(&buf, buf1, PAGE_SIZE) <= 0)
40062306a36Sopenharmony_ci		goto out;
40162306a36Sopenharmony_ci	ent.type = strcmp(buf1, "user") == 0 ?
40262306a36Sopenharmony_ci		IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* Name */
40562306a36Sopenharmony_ci	len = qword_get(&buf, buf1, PAGE_SIZE);
40662306a36Sopenharmony_ci	if (len <= 0 || len >= IDMAP_NAMESZ)
40762306a36Sopenharmony_ci		goto out;
40862306a36Sopenharmony_ci	memcpy(ent.name, buf1, sizeof(ent.name));
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* expiry */
41162306a36Sopenharmony_ci	error = get_expiry(&buf, &ent.h.expiry_time);
41262306a36Sopenharmony_ci	if (error)
41362306a36Sopenharmony_ci		goto out;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* ID */
41662306a36Sopenharmony_ci	error = get_int(&buf, &ent.id);
41762306a36Sopenharmony_ci	if (error == -EINVAL)
41862306a36Sopenharmony_ci		goto out;
41962306a36Sopenharmony_ci	if (error == -ENOENT)
42062306a36Sopenharmony_ci		set_bit(CACHE_NEGATIVE, &ent.h.flags);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	error = -ENOMEM;
42362306a36Sopenharmony_ci	res = nametoid_lookup(cd, &ent);
42462306a36Sopenharmony_ci	if (res == NULL)
42562306a36Sopenharmony_ci		goto out;
42662306a36Sopenharmony_ci	res = nametoid_update(cd, &ent, res);
42762306a36Sopenharmony_ci	if (res == NULL)
42862306a36Sopenharmony_ci		goto out;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	cache_put(&res->h, cd);
43162306a36Sopenharmony_ci	error = 0;
43262306a36Sopenharmony_ciout:
43362306a36Sopenharmony_ci	kfree(buf1);
43462306a36Sopenharmony_ci	return (error);
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic struct ent *
43962306a36Sopenharmony_cinametoid_lookup(struct cache_detail *cd, struct ent *item)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct cache_head *ch = sunrpc_cache_lookup_rcu(cd, &item->h,
44262306a36Sopenharmony_ci							nametoid_hash(item));
44362306a36Sopenharmony_ci	if (ch)
44462306a36Sopenharmony_ci		return container_of(ch, struct ent, h);
44562306a36Sopenharmony_ci	else
44662306a36Sopenharmony_ci		return NULL;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic struct ent *
45062306a36Sopenharmony_cinametoid_update(struct cache_detail *cd, struct ent *new, struct ent *old)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h,
45362306a36Sopenharmony_ci						    nametoid_hash(new));
45462306a36Sopenharmony_ci	if (ch)
45562306a36Sopenharmony_ci		return container_of(ch, struct ent, h);
45662306a36Sopenharmony_ci	else
45762306a36Sopenharmony_ci		return NULL;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/*
46162306a36Sopenharmony_ci * Exported API
46262306a36Sopenharmony_ci */
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ciint
46562306a36Sopenharmony_cinfsd_idmap_init(struct net *net)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	int rv;
46862306a36Sopenharmony_ci	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	nn->idtoname_cache = cache_create_net(&idtoname_cache_template, net);
47162306a36Sopenharmony_ci	if (IS_ERR(nn->idtoname_cache))
47262306a36Sopenharmony_ci		return PTR_ERR(nn->idtoname_cache);
47362306a36Sopenharmony_ci	rv = cache_register_net(nn->idtoname_cache, net);
47462306a36Sopenharmony_ci	if (rv)
47562306a36Sopenharmony_ci		goto destroy_idtoname_cache;
47662306a36Sopenharmony_ci	nn->nametoid_cache = cache_create_net(&nametoid_cache_template, net);
47762306a36Sopenharmony_ci	if (IS_ERR(nn->nametoid_cache)) {
47862306a36Sopenharmony_ci		rv = PTR_ERR(nn->nametoid_cache);
47962306a36Sopenharmony_ci		goto unregister_idtoname_cache;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci	rv = cache_register_net(nn->nametoid_cache, net);
48262306a36Sopenharmony_ci	if (rv)
48362306a36Sopenharmony_ci		goto destroy_nametoid_cache;
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cidestroy_nametoid_cache:
48762306a36Sopenharmony_ci	cache_destroy_net(nn->nametoid_cache, net);
48862306a36Sopenharmony_ciunregister_idtoname_cache:
48962306a36Sopenharmony_ci	cache_unregister_net(nn->idtoname_cache, net);
49062306a36Sopenharmony_cidestroy_idtoname_cache:
49162306a36Sopenharmony_ci	cache_destroy_net(nn->idtoname_cache, net);
49262306a36Sopenharmony_ci	return rv;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_civoid
49662306a36Sopenharmony_cinfsd_idmap_shutdown(struct net *net)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	cache_unregister_net(nn->idtoname_cache, net);
50162306a36Sopenharmony_ci	cache_unregister_net(nn->nametoid_cache, net);
50262306a36Sopenharmony_ci	cache_destroy_net(nn->idtoname_cache, net);
50362306a36Sopenharmony_ci	cache_destroy_net(nn->nametoid_cache, net);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic int
50762306a36Sopenharmony_ciidmap_lookup(struct svc_rqst *rqstp,
50862306a36Sopenharmony_ci		struct ent *(*lookup_fn)(struct cache_detail *, struct ent *),
50962306a36Sopenharmony_ci		struct ent *key, struct cache_detail *detail, struct ent **item)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	int ret;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	*item = lookup_fn(detail, key);
51462306a36Sopenharmony_ci	if (!*item)
51562306a36Sopenharmony_ci		return -ENOMEM;
51662306a36Sopenharmony_ci retry:
51762306a36Sopenharmony_ci	ret = cache_check(detail, &(*item)->h, &rqstp->rq_chandle);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (ret == -ETIMEDOUT) {
52062306a36Sopenharmony_ci		struct ent *prev_item = *item;
52162306a36Sopenharmony_ci		*item = lookup_fn(detail, key);
52262306a36Sopenharmony_ci		if (*item != prev_item)
52362306a36Sopenharmony_ci			goto retry;
52462306a36Sopenharmony_ci		cache_put(&(*item)->h, detail);
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	return ret;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic char *
53062306a36Sopenharmony_cirqst_authname(struct svc_rqst *rqstp)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct auth_domain *clp;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	clp = rqstp->rq_gssclient ? rqstp->rq_gssclient : rqstp->rq_client;
53562306a36Sopenharmony_ci	return clp->name;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic __be32
53962306a36Sopenharmony_ciidmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen,
54062306a36Sopenharmony_ci		u32 *id)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct ent *item, key = {
54362306a36Sopenharmony_ci		.type = type,
54462306a36Sopenharmony_ci	};
54562306a36Sopenharmony_ci	int ret;
54662306a36Sopenharmony_ci	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (namelen + 1 > sizeof(key.name))
54962306a36Sopenharmony_ci		return nfserr_badowner;
55062306a36Sopenharmony_ci	memcpy(key.name, name, namelen);
55162306a36Sopenharmony_ci	key.name[namelen] = '\0';
55262306a36Sopenharmony_ci	strscpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
55362306a36Sopenharmony_ci	ret = idmap_lookup(rqstp, nametoid_lookup, &key, nn->nametoid_cache, &item);
55462306a36Sopenharmony_ci	if (ret == -ENOENT)
55562306a36Sopenharmony_ci		return nfserr_badowner;
55662306a36Sopenharmony_ci	if (ret)
55762306a36Sopenharmony_ci		return nfserrno(ret);
55862306a36Sopenharmony_ci	*id = item->id;
55962306a36Sopenharmony_ci	cache_put(&item->h, nn->nametoid_cache);
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic __be32 encode_ascii_id(struct xdr_stream *xdr, u32 id)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	char buf[11];
56662306a36Sopenharmony_ci	int len;
56762306a36Sopenharmony_ci	__be32 *p;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	len = sprintf(buf, "%u", id);
57062306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, len + 4);
57162306a36Sopenharmony_ci	if (!p)
57262306a36Sopenharmony_ci		return nfserr_resource;
57362306a36Sopenharmony_ci	p = xdr_encode_opaque(p, buf, len);
57462306a36Sopenharmony_ci	return 0;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic __be32 idmap_id_to_name(struct xdr_stream *xdr,
57862306a36Sopenharmony_ci			       struct svc_rqst *rqstp, int type, u32 id)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct ent *item, key = {
58162306a36Sopenharmony_ci		.id = id,
58262306a36Sopenharmony_ci		.type = type,
58362306a36Sopenharmony_ci	};
58462306a36Sopenharmony_ci	__be32 *p;
58562306a36Sopenharmony_ci	int ret;
58662306a36Sopenharmony_ci	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	strscpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
58962306a36Sopenharmony_ci	ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item);
59062306a36Sopenharmony_ci	if (ret == -ENOENT)
59162306a36Sopenharmony_ci		return encode_ascii_id(xdr, id);
59262306a36Sopenharmony_ci	if (ret)
59362306a36Sopenharmony_ci		return nfserrno(ret);
59462306a36Sopenharmony_ci	ret = strlen(item->name);
59562306a36Sopenharmony_ci	WARN_ON_ONCE(ret > IDMAP_NAMESZ);
59662306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, ret + 4);
59762306a36Sopenharmony_ci	if (!p)
59862306a36Sopenharmony_ci		return nfserr_resource;
59962306a36Sopenharmony_ci	p = xdr_encode_opaque(p, item->name, ret);
60062306a36Sopenharmony_ci	cache_put(&item->h, nn->idtoname_cache);
60162306a36Sopenharmony_ci	return 0;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic bool
60562306a36Sopenharmony_cinumeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	int ret;
60862306a36Sopenharmony_ci	char buf[11];
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (namelen + 1 > sizeof(buf))
61162306a36Sopenharmony_ci		/* too long to represent a 32-bit id: */
61262306a36Sopenharmony_ci		return false;
61362306a36Sopenharmony_ci	/* Just to make sure it's null-terminated: */
61462306a36Sopenharmony_ci	memcpy(buf, name, namelen);
61562306a36Sopenharmony_ci	buf[namelen] = '\0';
61662306a36Sopenharmony_ci	ret = kstrtouint(buf, 10, id);
61762306a36Sopenharmony_ci	return ret == 0;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic __be32
62162306a36Sopenharmony_cido_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS)
62462306a36Sopenharmony_ci		if (numeric_name_to_id(rqstp, type, name, namelen, id))
62562306a36Sopenharmony_ci			return 0;
62662306a36Sopenharmony_ci		/*
62762306a36Sopenharmony_ci		 * otherwise, fall through and try idmapping, for
62862306a36Sopenharmony_ci		 * backwards compatibility with clients sending names:
62962306a36Sopenharmony_ci		 */
63062306a36Sopenharmony_ci	return idmap_name_to_id(rqstp, type, name, namelen, id);
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistatic __be32 encode_name_from_id(struct xdr_stream *xdr,
63462306a36Sopenharmony_ci				  struct svc_rqst *rqstp, int type, u32 id)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS)
63762306a36Sopenharmony_ci		return encode_ascii_id(xdr, id);
63862306a36Sopenharmony_ci	return idmap_id_to_name(xdr, rqstp, type, id);
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci__be32
64262306a36Sopenharmony_cinfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen,
64362306a36Sopenharmony_ci		kuid_t *uid)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	__be32 status;
64662306a36Sopenharmony_ci	u32 id = -1;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (name == NULL || namelen == 0)
64962306a36Sopenharmony_ci		return nfserr_inval;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id);
65262306a36Sopenharmony_ci	*uid = make_kuid(nfsd_user_namespace(rqstp), id);
65362306a36Sopenharmony_ci	if (!uid_valid(*uid))
65462306a36Sopenharmony_ci		status = nfserr_badowner;
65562306a36Sopenharmony_ci	return status;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci__be32
65962306a36Sopenharmony_cinfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen,
66062306a36Sopenharmony_ci		kgid_t *gid)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	__be32 status;
66362306a36Sopenharmony_ci	u32 id = -1;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (name == NULL || namelen == 0)
66662306a36Sopenharmony_ci		return nfserr_inval;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id);
66962306a36Sopenharmony_ci	*gid = make_kgid(nfsd_user_namespace(rqstp), id);
67062306a36Sopenharmony_ci	if (!gid_valid(*gid))
67162306a36Sopenharmony_ci		status = nfserr_badowner;
67262306a36Sopenharmony_ci	return status;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci__be32 nfsd4_encode_user(struct xdr_stream *xdr, struct svc_rqst *rqstp,
67662306a36Sopenharmony_ci			 kuid_t uid)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	u32 id = from_kuid_munged(nfsd_user_namespace(rqstp), uid);
67962306a36Sopenharmony_ci	return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_USER, id);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci__be32 nfsd4_encode_group(struct xdr_stream *xdr, struct svc_rqst *rqstp,
68362306a36Sopenharmony_ci			  kgid_t gid)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	u32 id = from_kgid_munged(nfsd_user_namespace(rqstp), gid);
68662306a36Sopenharmony_ci	return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_GROUP, id);
68762306a36Sopenharmony_ci}
688