xref: /openbmc/linux/fs/nfsd/export.c (revision ed4543328f7108e1047b83b96ca7f7208747d930)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * NFS exporting and validation.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * We maintain a list of clients, each of which has a list of
61da177e4SLinus Torvalds  * exports. To export an fs to a given client, you first have
71da177e4SLinus Torvalds  * to create the client entry with NFSCTL_ADDCLIENT, which
81da177e4SLinus Torvalds  * creates a client control block and adds it to the hash
91da177e4SLinus Torvalds  * table. Then, you call NFSCTL_EXPORT for each fs.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
131da177e4SLinus Torvalds  */
141da177e4SLinus Torvalds 
155a0e3ad6STejun Heo #include <linux/slab.h>
161da177e4SLinus Torvalds #include <linux/namei.h>
17f35279d3SBruce Allan #include <linux/module.h>
18a5694255SChristoph Hellwig #include <linux/exportfs.h>
19b3853e0eSStanislav Kinsbursky #include <linux/sunrpc/svc_xprt.h>
201da177e4SLinus Torvalds 
219a74af21SBoaz Harrosh #include "nfsd.h"
221557aca7SJ. Bruce Fields #include "nfsfh.h"
23b3853e0eSStanislav Kinsbursky #include "netns.h"
249cf514ccSChristoph Hellwig #include "pnfs.h"
2565294c1fSJeff Layton #include "filecache.h"
26cf749f3cSTrond Myklebust #include "trace.h"
279a74af21SBoaz Harrosh 
281da177e4SLinus Torvalds #define NFSDDBG_FACILITY	NFSDDBG_EXPORT
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds /*
311da177e4SLinus Torvalds  * We have two caches.
321da177e4SLinus Torvalds  * One maps client+vfsmnt+dentry to export options - the export map
331da177e4SLinus Torvalds  * The other maps client+filehandle-fragment to export options. - the expkey map
341da177e4SLinus Torvalds  *
351da177e4SLinus Torvalds  * The export options are actually stored in the first map, and the
361da177e4SLinus Torvalds  * second map contains a reference to the entry in the first map.
371da177e4SLinus Torvalds  */
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds #define	EXPKEY_HASHBITS		8
401da177e4SLinus Torvalds #define	EXPKEY_HASHMAX		(1 << EXPKEY_HASHBITS)
411da177e4SLinus Torvalds #define	EXPKEY_HASHMASK		(EXPKEY_HASHMAX -1)
421da177e4SLinus Torvalds 
expkey_put(struct kref * ref)43*48830406SYang Erkun static void expkey_put(struct kref *ref)
441da177e4SLinus Torvalds {
45*48830406SYang Erkun 	struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);
46baab935fSNeilBrown 
47baab935fSNeilBrown 	if (test_bit(CACHE_VALID, &key->h.flags) &&
48e83aece3SJan Blunck 	    !test_bit(CACHE_NEGATIVE, &key->h.flags))
49e83aece3SJan Blunck 		path_put(&key->ek_path);
501da177e4SLinus Torvalds 	auth_domain_put(key->ek_client);
51*48830406SYang Erkun 	kfree_rcu(key, ek_rcu);
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds 
expkey_upcall(struct cache_detail * cd,struct cache_head * h)5465286b88STrond Myklebust static int expkey_upcall(struct cache_detail *cd, struct cache_head *h)
5565286b88STrond Myklebust {
5665286b88STrond Myklebust 	return sunrpc_cache_pipe_upcall(cd, h);
5765286b88STrond Myklebust }
5865286b88STrond Myklebust 
expkey_request(struct cache_detail * cd,struct cache_head * h,char ** bpp,int * blen)591da177e4SLinus Torvalds static void expkey_request(struct cache_detail *cd,
601da177e4SLinus Torvalds 			   struct cache_head *h,
611da177e4SLinus Torvalds 			   char **bpp, int *blen)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds 	/* client fsidtype \xfsid */
641da177e4SLinus Torvalds 	struct svc_expkey *ek = container_of(h, struct svc_expkey, h);
651da177e4SLinus Torvalds 	char type[5];
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds 	qword_add(bpp, blen, ek->ek_client->name);
681da177e4SLinus Torvalds 	snprintf(type, 5, "%d", ek->ek_fsidtype);
691da177e4SLinus Torvalds 	qword_add(bpp, blen, type);
701da177e4SLinus Torvalds 	qword_addhex(bpp, blen, (char*)ek->ek_fsid, key_len(ek->ek_fsidtype));
711da177e4SLinus Torvalds 	(*bpp)[-1] = '\n';
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
74c89172e3SStanislav Kinsbursky static struct svc_expkey *svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new,
75c89172e3SStanislav Kinsbursky 					    struct svc_expkey *old);
76c89172e3SStanislav Kinsbursky static struct svc_expkey *svc_expkey_lookup(struct cache_detail *cd, struct svc_expkey *);
7774cae61aSAdrian Bunk 
expkey_parse(struct cache_detail * cd,char * mesg,int mlen)781da177e4SLinus Torvalds static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
791da177e4SLinus Torvalds {
80f0db79d5SKinglong Mee 	/* client fsidtype fsid expiry [path] */
811da177e4SLinus Torvalds 	char *buf;
821da177e4SLinus Torvalds 	int len;
831da177e4SLinus Torvalds 	struct auth_domain *dom = NULL;
841da177e4SLinus Torvalds 	int err;
851da177e4SLinus Torvalds 	int fsidtype;
861da177e4SLinus Torvalds 	char *ep;
871da177e4SLinus Torvalds 	struct svc_expkey key;
8830bc4dfdSJ. Bruce Fields 	struct svc_expkey *ek = NULL;
891da177e4SLinus Torvalds 
903476964dSDan Carpenter 	if (mesg[mlen - 1] != '\n')
911da177e4SLinus Torvalds 		return -EINVAL;
921da177e4SLinus Torvalds 	mesg[mlen-1] = 0;
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
951da177e4SLinus Torvalds 	err = -ENOMEM;
9630bc4dfdSJ. Bruce Fields 	if (!buf)
9730bc4dfdSJ. Bruce Fields 		goto out;
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 	err = -EINVAL;
10075bfb704SColin Ian King 	if (qword_get(&mesg, buf, PAGE_SIZE) <= 0)
1011da177e4SLinus Torvalds 		goto out;
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	err = -ENOENT;
1041da177e4SLinus Torvalds 	dom = auth_domain_find(buf);
1051da177e4SLinus Torvalds 	if (!dom)
1061da177e4SLinus Torvalds 		goto out;
1071da177e4SLinus Torvalds 	dprintk("found domain %s\n", buf);
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	err = -EINVAL;
11075bfb704SColin Ian King 	if (qword_get(&mesg, buf, PAGE_SIZE) <= 0)
1111da177e4SLinus Torvalds 		goto out;
1121da177e4SLinus Torvalds 	fsidtype = simple_strtoul(buf, &ep, 10);
1131da177e4SLinus Torvalds 	if (*ep)
1141da177e4SLinus Torvalds 		goto out;
1151da177e4SLinus Torvalds 	dprintk("found fsidtype %d\n", fsidtype);
1164bdff8c0SFrank Filz 	if (key_len(fsidtype)==0) /* invalid type */
1171da177e4SLinus Torvalds 		goto out;
1181da177e4SLinus Torvalds 	if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0)
1191da177e4SLinus Torvalds 		goto out;
1201da177e4SLinus Torvalds 	dprintk("found fsid length %d\n", len);
1211da177e4SLinus Torvalds 	if (len != key_len(fsidtype))
1221da177e4SLinus Torvalds 		goto out;
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	/* OK, we seem to have a valid key */
1251da177e4SLinus Torvalds 	key.h.flags = 0;
126cf64b9bcSNeilBrown 	err = get_expiry(&mesg, &key.h.expiry_time);
127cf64b9bcSNeilBrown 	if (err)
1281da177e4SLinus Torvalds 		goto out;
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	key.ek_client = dom;
1311da177e4SLinus Torvalds 	key.ek_fsidtype = fsidtype;
1321da177e4SLinus Torvalds 	memcpy(key.ek_fsid, buf, len);
1331da177e4SLinus Torvalds 
134c89172e3SStanislav Kinsbursky 	ek = svc_expkey_lookup(cd, &key);
1358d270f7fSNeilBrown 	err = -ENOMEM;
1368d270f7fSNeilBrown 	if (!ek)
1378d270f7fSNeilBrown 		goto out;
1388d270f7fSNeilBrown 
1391da177e4SLinus Torvalds 	/* now we want a pathname, or empty meaning NEGATIVE  */
1408d270f7fSNeilBrown 	err = -EINVAL;
14130bc4dfdSJ. Bruce Fields 	len = qword_get(&mesg, buf, PAGE_SIZE);
14230bc4dfdSJ. Bruce Fields 	if (len < 0)
1431da177e4SLinus Torvalds 		goto out;
1441da177e4SLinus Torvalds 	dprintk("Path seems to be <%s>\n", buf);
1451da177e4SLinus Torvalds 	err = 0;
1461da177e4SLinus Torvalds 	if (len == 0) {
1471da177e4SLinus Torvalds 		set_bit(CACHE_NEGATIVE, &key.h.flags);
148c89172e3SStanislav Kinsbursky 		ek = svc_expkey_update(cd, &key, ek);
1496a30e47fSTrond Myklebust 		if (ek)
1506a30e47fSTrond Myklebust 			trace_nfsd_expkey_update(ek, NULL);
1516a30e47fSTrond Myklebust 		else
15230bc4dfdSJ. Bruce Fields 			err = -ENOMEM;
1531da177e4SLinus Torvalds 	} else {
154a63bb996SAl Viro 		err = kern_path(buf, 0, &key.ek_path);
15530bc4dfdSJ. Bruce Fields 		if (err)
1561da177e4SLinus Torvalds 			goto out;
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 		dprintk("Found the path %s\n", buf);
1591da177e4SLinus Torvalds 
160c89172e3SStanislav Kinsbursky 		ek = svc_expkey_update(cd, &key, ek);
1616a30e47fSTrond Myklebust 		if (ek)
1626a30e47fSTrond Myklebust 			trace_nfsd_expkey_update(ek, buf);
1636a30e47fSTrond Myklebust 		else
1648d270f7fSNeilBrown 			err = -ENOMEM;
165a63bb996SAl Viro 		path_put(&key.ek_path);
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 	cache_flush();
1681da177e4SLinus Torvalds  out:
16930bc4dfdSJ. Bruce Fields 	if (ek)
170d4bb527eSStanislav Kinsbursky 		cache_put(&ek->h, cd);
1711da177e4SLinus Torvalds 	if (dom)
1721da177e4SLinus Torvalds 		auth_domain_put(dom);
1731da177e4SLinus Torvalds 	kfree(buf);
1741da177e4SLinus Torvalds 	return err;
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
expkey_show(struct seq_file * m,struct cache_detail * cd,struct cache_head * h)1771da177e4SLinus Torvalds static int expkey_show(struct seq_file *m,
1781da177e4SLinus Torvalds 		       struct cache_detail *cd,
1791da177e4SLinus Torvalds 		       struct cache_head *h)
1801da177e4SLinus Torvalds {
1811da177e4SLinus Torvalds 	struct svc_expkey *ek ;
182af6a4e28SNeilBrown 	int i;
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 	if (h ==NULL) {
1851da177e4SLinus Torvalds 		seq_puts(m, "#domain fsidtype fsid [path]\n");
1861da177e4SLinus Torvalds 		return 0;
1871da177e4SLinus Torvalds 	}
1881da177e4SLinus Torvalds 	ek = container_of(h, struct svc_expkey, h);
189af6a4e28SNeilBrown 	seq_printf(m, "%s %d 0x", ek->ek_client->name,
190af6a4e28SNeilBrown 		   ek->ek_fsidtype);
191af6a4e28SNeilBrown 	for (i=0; i < key_len(ek->ek_fsidtype)/4; i++)
192af6a4e28SNeilBrown 		seq_printf(m, "%08x", ek->ek_fsid[i]);
1931da177e4SLinus Torvalds 	if (test_bit(CACHE_VALID, &h->flags) &&
1941da177e4SLinus Torvalds 	    !test_bit(CACHE_NEGATIVE, &h->flags)) {
1951da177e4SLinus Torvalds 		seq_printf(m, " ");
196c32c2f63SJan Blunck 		seq_path(m, &ek->ek_path, "\\ \t\n");
1971da177e4SLinus Torvalds 	}
1981da177e4SLinus Torvalds 	seq_printf(m, "\n");
1991da177e4SLinus Torvalds 	return 0;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds 
expkey_match(struct cache_head * a,struct cache_head * b)2028d270f7fSNeilBrown static inline int expkey_match (struct cache_head *a, struct cache_head *b)
2038d270f7fSNeilBrown {
2048d270f7fSNeilBrown 	struct svc_expkey *orig = container_of(a, struct svc_expkey, h);
2058d270f7fSNeilBrown 	struct svc_expkey *new = container_of(b, struct svc_expkey, h);
2068d270f7fSNeilBrown 
2078d270f7fSNeilBrown 	if (orig->ek_fsidtype != new->ek_fsidtype ||
2088d270f7fSNeilBrown 	    orig->ek_client != new->ek_client ||
2098d270f7fSNeilBrown 	    memcmp(orig->ek_fsid, new->ek_fsid, key_len(orig->ek_fsidtype)) != 0)
2108d270f7fSNeilBrown 		return 0;
2118d270f7fSNeilBrown 	return 1;
2128d270f7fSNeilBrown }
2138d270f7fSNeilBrown 
expkey_init(struct cache_head * cnew,struct cache_head * citem)2148d270f7fSNeilBrown static inline void expkey_init(struct cache_head *cnew,
2158d270f7fSNeilBrown 				   struct cache_head *citem)
2168d270f7fSNeilBrown {
2178d270f7fSNeilBrown 	struct svc_expkey *new = container_of(cnew, struct svc_expkey, h);
2188d270f7fSNeilBrown 	struct svc_expkey *item = container_of(citem, struct svc_expkey, h);
2198d270f7fSNeilBrown 
2208d270f7fSNeilBrown 	kref_get(&item->ek_client->ref);
2218d270f7fSNeilBrown 	new->ek_client = item->ek_client;
2228d270f7fSNeilBrown 	new->ek_fsidtype = item->ek_fsidtype;
223af6a4e28SNeilBrown 
224af6a4e28SNeilBrown 	memcpy(new->ek_fsid, item->ek_fsid, sizeof(new->ek_fsid));
2258d270f7fSNeilBrown }
2268d270f7fSNeilBrown 
expkey_update(struct cache_head * cnew,struct cache_head * citem)2278d270f7fSNeilBrown static inline void expkey_update(struct cache_head *cnew,
2288d270f7fSNeilBrown 				   struct cache_head *citem)
2298d270f7fSNeilBrown {
2308d270f7fSNeilBrown 	struct svc_expkey *new = container_of(cnew, struct svc_expkey, h);
2318d270f7fSNeilBrown 	struct svc_expkey *item = container_of(citem, struct svc_expkey, h);
2328d270f7fSNeilBrown 
233e83aece3SJan Blunck 	new->ek_path = item->ek_path;
234e83aece3SJan Blunck 	path_get(&item->ek_path);
2358d270f7fSNeilBrown }
2368d270f7fSNeilBrown 
expkey_alloc(void)2378d270f7fSNeilBrown static struct cache_head *expkey_alloc(void)
2388d270f7fSNeilBrown {
2398d270f7fSNeilBrown 	struct svc_expkey *i = kmalloc(sizeof(*i), GFP_KERNEL);
2408d270f7fSNeilBrown 	if (i)
2418d270f7fSNeilBrown 		return &i->h;
2428d270f7fSNeilBrown 	else
2438d270f7fSNeilBrown 		return NULL;
2448d270f7fSNeilBrown }
2458d270f7fSNeilBrown 
expkey_flush(void)24665294c1fSJeff Layton static void expkey_flush(void)
24765294c1fSJeff Layton {
24865294c1fSJeff Layton 	/*
24965294c1fSJeff Layton 	 * Take the nfsd_mutex here to ensure that the file cache is not
25065294c1fSJeff Layton 	 * destroyed while we're in the middle of flushing.
25165294c1fSJeff Layton 	 */
25265294c1fSJeff Layton 	mutex_lock(&nfsd_mutex);
2535e113224STrond Myklebust 	nfsd_file_cache_purge(current->nsproxy->net_ns);
25465294c1fSJeff Layton 	mutex_unlock(&nfsd_mutex);
25565294c1fSJeff Layton }
25665294c1fSJeff Layton 
257ae2e408eSBhumika Goyal static const struct cache_detail svc_expkey_cache_template = {
258f35279d3SBruce Allan 	.owner		= THIS_MODULE,
2591da177e4SLinus Torvalds 	.hash_size	= EXPKEY_HASHMAX,
2601da177e4SLinus Torvalds 	.name		= "nfsd.fh",
2611da177e4SLinus Torvalds 	.cache_put	= expkey_put,
26265286b88STrond Myklebust 	.cache_upcall	= expkey_upcall,
26373fb847aSStanislav Kinsbursky 	.cache_request	= expkey_request,
2641da177e4SLinus Torvalds 	.cache_parse	= expkey_parse,
2651da177e4SLinus Torvalds 	.cache_show	= expkey_show,
2668d270f7fSNeilBrown 	.match		= expkey_match,
2678d270f7fSNeilBrown 	.init		= expkey_init,
2688d270f7fSNeilBrown 	.update       	= expkey_update,
2698d270f7fSNeilBrown 	.alloc		= expkey_alloc,
27065294c1fSJeff Layton 	.flush		= expkey_flush,
2711da177e4SLinus Torvalds };
2721da177e4SLinus Torvalds 
27361f8603dSNeilBrown static int
svc_expkey_hash(struct svc_expkey * item)27461f8603dSNeilBrown svc_expkey_hash(struct svc_expkey *item)
2751da177e4SLinus Torvalds {
2768d270f7fSNeilBrown 	int hash = item->ek_fsidtype;
2778d270f7fSNeilBrown 	char * cp = (char*)item->ek_fsid;
2788d270f7fSNeilBrown 	int len = key_len(item->ek_fsidtype);
2798d270f7fSNeilBrown 
2808d270f7fSNeilBrown 	hash ^= hash_mem(cp, len, EXPKEY_HASHBITS);
2818d270f7fSNeilBrown 	hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS);
2828d270f7fSNeilBrown 	hash &= EXPKEY_HASHMASK;
28361f8603dSNeilBrown 	return hash;
28461f8603dSNeilBrown }
28561f8603dSNeilBrown 
28661f8603dSNeilBrown static struct svc_expkey *
svc_expkey_lookup(struct cache_detail * cd,struct svc_expkey * item)287c89172e3SStanislav Kinsbursky svc_expkey_lookup(struct cache_detail *cd, struct svc_expkey *item)
28861f8603dSNeilBrown {
28961f8603dSNeilBrown 	struct cache_head *ch;
29061f8603dSNeilBrown 	int hash = svc_expkey_hash(item);
2918d270f7fSNeilBrown 
2929ceddd9dSTrond Myklebust 	ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash);
2938d270f7fSNeilBrown 	if (ch)
2948d270f7fSNeilBrown 		return container_of(ch, struct svc_expkey, h);
2958d270f7fSNeilBrown 	else
2968d270f7fSNeilBrown 		return NULL;
2971da177e4SLinus Torvalds }
2981da177e4SLinus Torvalds 
2998d270f7fSNeilBrown static struct svc_expkey *
svc_expkey_update(struct cache_detail * cd,struct svc_expkey * new,struct svc_expkey * old)300c89172e3SStanislav Kinsbursky svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new,
301c89172e3SStanislav Kinsbursky 		  struct svc_expkey *old)
3021da177e4SLinus Torvalds {
3038d270f7fSNeilBrown 	struct cache_head *ch;
30461f8603dSNeilBrown 	int hash = svc_expkey_hash(new);
3058d270f7fSNeilBrown 
306c89172e3SStanislav Kinsbursky 	ch = sunrpc_cache_update(cd, &new->h, &old->h, hash);
3078d270f7fSNeilBrown 	if (ch)
3088d270f7fSNeilBrown 		return container_of(ch, struct svc_expkey, h);
3098d270f7fSNeilBrown 	else
3108d270f7fSNeilBrown 		return NULL;
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds #define	EXPORT_HASHBITS		8
3151da177e4SLinus Torvalds #define	EXPORT_HASHMAX		(1<< EXPORT_HASHBITS)
3161da177e4SLinus Torvalds 
nfsd4_fslocs_free(struct nfsd4_fs_locations * fsloc)31793346919SManoj Naik static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc)
31893346919SManoj Naik {
319a1f05514SKinglong Mee 	struct nfsd4_fs_location *locations = fsloc->locations;
32093346919SManoj Naik 	int i;
32193346919SManoj Naik 
322a1f05514SKinglong Mee 	if (!locations)
323a1f05514SKinglong Mee 		return;
324a1f05514SKinglong Mee 
32593346919SManoj Naik 	for (i = 0; i < fsloc->locations_count; i++) {
326a1f05514SKinglong Mee 		kfree(locations[i].path);
327a1f05514SKinglong Mee 		kfree(locations[i].hosts);
32893346919SManoj Naik 	}
329a1f05514SKinglong Mee 
330a1f05514SKinglong Mee 	kfree(locations);
331a1f05514SKinglong Mee 	fsloc->locations = NULL;
33293346919SManoj Naik }
33393346919SManoj Naik 
export_stats_init(struct export_stats * stats)33420ad856eSAmir Goldstein static int export_stats_init(struct export_stats *stats)
33520ad856eSAmir Goldstein {
33620ad856eSAmir Goldstein 	stats->start_time = ktime_get_seconds();
33720ad856eSAmir Goldstein 	return nfsd_percpu_counters_init(stats->counter, EXP_STATS_COUNTERS_NUM);
33820ad856eSAmir Goldstein }
33920ad856eSAmir Goldstein 
export_stats_reset(struct export_stats * stats)34020ad856eSAmir Goldstein static void export_stats_reset(struct export_stats *stats)
34120ad856eSAmir Goldstein {
3429b31d561SChuck Lever 	if (stats)
3439b31d561SChuck Lever 		nfsd_percpu_counters_reset(stats->counter,
3449b31d561SChuck Lever 					   EXP_STATS_COUNTERS_NUM);
34520ad856eSAmir Goldstein }
34620ad856eSAmir Goldstein 
export_stats_destroy(struct export_stats * stats)34720ad856eSAmir Goldstein static void export_stats_destroy(struct export_stats *stats)
34820ad856eSAmir Goldstein {
3499b31d561SChuck Lever 	if (stats)
3509b31d561SChuck Lever 		nfsd_percpu_counters_destroy(stats->counter,
3519b31d561SChuck Lever 					     EXP_STATS_COUNTERS_NUM);
35220ad856eSAmir Goldstein }
35320ad856eSAmir Goldstein 
svc_export_put(struct kref * ref)354*48830406SYang Erkun static void svc_export_put(struct kref *ref)
3551da177e4SLinus Torvalds {
356*48830406SYang Erkun 	struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
35754775491SJan Blunck 	path_put(&exp->ex_path);
3581da177e4SLinus Torvalds 	auth_domain_put(exp->ex_client);
35993346919SManoj Naik 	nfsd4_fslocs_free(&exp->ex_fslocs);
3609b31d561SChuck Lever 	export_stats_destroy(exp->ex_stats);
3619b31d561SChuck Lever 	kfree(exp->ex_stats);
362885c91f7Smajianpeng 	kfree(exp->ex_uuid);
363*48830406SYang Erkun 	kfree_rcu(exp, ex_rcu);
3641da177e4SLinus Torvalds }
3651da177e4SLinus Torvalds 
svc_export_upcall(struct cache_detail * cd,struct cache_head * h)36665286b88STrond Myklebust static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h)
36765286b88STrond Myklebust {
36865286b88STrond Myklebust 	return sunrpc_cache_pipe_upcall(cd, h);
36965286b88STrond Myklebust }
37065286b88STrond Myklebust 
svc_export_request(struct cache_detail * cd,struct cache_head * h,char ** bpp,int * blen)3711da177e4SLinus Torvalds static void svc_export_request(struct cache_detail *cd,
3721da177e4SLinus Torvalds 			       struct cache_head *h,
3731da177e4SLinus Torvalds 			       char **bpp, int *blen)
3741da177e4SLinus Torvalds {
3751da177e4SLinus Torvalds 	/*  client path */
3761da177e4SLinus Torvalds 	struct svc_export *exp = container_of(h, struct svc_export, h);
3771da177e4SLinus Torvalds 	char *pth;
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds 	qword_add(bpp, blen, exp->ex_client->name);
380cf28b486SJan Blunck 	pth = d_path(&exp->ex_path, *bpp, *blen);
3811da177e4SLinus Torvalds 	if (IS_ERR(pth)) {
3821da177e4SLinus Torvalds 		/* is this correct? */
3831da177e4SLinus Torvalds 		(*bpp)[0] = '\n';
3841da177e4SLinus Torvalds 		return;
3851da177e4SLinus Torvalds 	}
3861da177e4SLinus Torvalds 	qword_add(bpp, blen, pth);
3871da177e4SLinus Torvalds 	(*bpp)[-1] = '\n';
3881da177e4SLinus Torvalds }
3891da177e4SLinus Torvalds 
39074cae61aSAdrian Bunk static struct svc_export *svc_export_update(struct svc_export *new,
39174cae61aSAdrian Bunk 					    struct svc_export *old);
3924f7774c3SNeilBrown static struct svc_export *svc_export_lookup(struct svc_export *);
3931da177e4SLinus Torvalds 
check_export(struct path * path,int * flags,unsigned char * uuid)394899bf2ceSChristian Brauner static int check_export(struct path *path, int *flags, unsigned char *uuid)
3951da177e4SLinus Torvalds {
396899bf2ceSChristian Brauner 	struct inode *inode = d_inode(path->dentry);
3971da177e4SLinus Torvalds 
398f2ca7153SJ. Bruce Fields 	/*
399f2ca7153SJ. Bruce Fields 	 * We currently export only dirs, regular files, and (for v4
400f2ca7153SJ. Bruce Fields 	 * pseudoroot) symlinks.
4011da177e4SLinus Torvalds 	 */
4021da177e4SLinus Torvalds 	if (!S_ISDIR(inode->i_mode) &&
403f2ca7153SJ. Bruce Fields 	    !S_ISLNK(inode->i_mode) &&
4041da177e4SLinus Torvalds 	    !S_ISREG(inode->i_mode))
4051da177e4SLinus Torvalds 		return -ENOTDIR;
4061da177e4SLinus Torvalds 
407774b1478SJ. Bruce Fields 	/*
408774b1478SJ. Bruce Fields 	 * Mountd should never pass down a writeable V4ROOT export, but,
409774b1478SJ. Bruce Fields 	 * just to make sure:
410774b1478SJ. Bruce Fields 	 */
411774b1478SJ. Bruce Fields 	if (*flags & NFSEXP_V4ROOT)
412774b1478SJ. Bruce Fields 		*flags |= NFSEXP_READONLY;
413774b1478SJ. Bruce Fields 
4141da177e4SLinus Torvalds 	/* There are two requirements on a filesystem to be exportable.
4151da177e4SLinus Torvalds 	 * 1:  We must be able to identify the filesystem from a number.
4161da177e4SLinus Torvalds 	 *       either a device number (so FS_REQUIRES_DEV needed)
417af6a4e28SNeilBrown 	 *       or an FSID number (so NFSEXP_FSID or ->uuid is needed).
4181da177e4SLinus Torvalds 	 * 2:  We must be able to find an inode from a filehandle.
4191da177e4SLinus Torvalds 	 *       This means that s_export_op must be set.
420899bf2ceSChristian Brauner 	 * 3: We must not currently be on an idmapped mount.
4211da177e4SLinus Torvalds 	 */
4221da177e4SLinus Torvalds 	if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) &&
423774b1478SJ. Bruce Fields 	    !(*flags & NFSEXP_FSID) &&
424af6a4e28SNeilBrown 	    uuid == NULL) {
4253e3b4800SGreg Banks 		dprintk("exp_export: export of non-dev fs without fsid\n");
4261da177e4SLinus Torvalds 		return -EINVAL;
4271da177e4SLinus Torvalds 	}
428cfaea787SChristoph Hellwig 
429cfaea787SChristoph Hellwig 	if (!inode->i_sb->s_export_op ||
430cfaea787SChristoph Hellwig 	    !inode->i_sb->s_export_op->fh_to_dentry) {
4311da177e4SLinus Torvalds 		dprintk("exp_export: export of invalid fs type.\n");
4321da177e4SLinus Torvalds 		return -EINVAL;
4331da177e4SLinus Torvalds 	}
4341da177e4SLinus Torvalds 
435bb49e9e7SChristian Brauner 	if (is_idmapped_mnt(path->mnt)) {
436899bf2ceSChristian Brauner 		dprintk("exp_export: export of idmapped mounts not yet supported.\n");
437899bf2ceSChristian Brauner 		return -EINVAL;
438899bf2ceSChristian Brauner 	}
439899bf2ceSChristian Brauner 
440ba5e8187SJeff Layton 	if (inode->i_sb->s_export_op->flags & EXPORT_OP_NOSUBTREECHK &&
441ba5e8187SJeff Layton 	    !(*flags & NFSEXP_NOSUBTREECHECK)) {
442ba5e8187SJeff Layton 		dprintk("%s: %s does not support subtree checking!\n",
443ba5e8187SJeff Layton 			__func__, inode->i_sb->s_type->name);
444ba5e8187SJeff Layton 		return -EINVAL;
445ba5e8187SJeff Layton 	}
4461da177e4SLinus Torvalds 	return 0;
4471da177e4SLinus Torvalds }
4481da177e4SLinus Torvalds 
44993346919SManoj Naik #ifdef CONFIG_NFSD_V4
45093346919SManoj Naik 
45193346919SManoj Naik static int
fsloc_parse(char ** mesg,char * buf,struct nfsd4_fs_locations * fsloc)45293346919SManoj Naik fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc)
45393346919SManoj Naik {
45493346919SManoj Naik 	int len;
45593346919SManoj Naik 	int migrated, i, err;
45693346919SManoj Naik 
457be69da80SKinglong Mee 	/* more than one fsloc */
458be69da80SKinglong Mee 	if (fsloc->locations)
459be69da80SKinglong Mee 		return -EINVAL;
460be69da80SKinglong Mee 
46193346919SManoj Naik 	/* listsize */
462a007c4c3SJ. Bruce Fields 	err = get_uint(mesg, &fsloc->locations_count);
46393346919SManoj Naik 	if (err)
46493346919SManoj Naik 		return err;
46593346919SManoj Naik 	if (fsloc->locations_count > MAX_FS_LOCATIONS)
46693346919SManoj Naik 		return -EINVAL;
46793346919SManoj Naik 	if (fsloc->locations_count == 0)
46893346919SManoj Naik 		return 0;
46993346919SManoj Naik 
4706396bb22SKees Cook 	fsloc->locations = kcalloc(fsloc->locations_count,
4716396bb22SKees Cook 				   sizeof(struct nfsd4_fs_location),
4726396bb22SKees Cook 				   GFP_KERNEL);
47393346919SManoj Naik 	if (!fsloc->locations)
47493346919SManoj Naik 		return -ENOMEM;
47593346919SManoj Naik 	for (i=0; i < fsloc->locations_count; i++) {
47693346919SManoj Naik 		/* colon separated host list */
47793346919SManoj Naik 		err = -EINVAL;
47893346919SManoj Naik 		len = qword_get(mesg, buf, PAGE_SIZE);
47993346919SManoj Naik 		if (len <= 0)
48093346919SManoj Naik 			goto out_free_all;
48193346919SManoj Naik 		err = -ENOMEM;
48293346919SManoj Naik 		fsloc->locations[i].hosts = kstrdup(buf, GFP_KERNEL);
48393346919SManoj Naik 		if (!fsloc->locations[i].hosts)
48493346919SManoj Naik 			goto out_free_all;
48593346919SManoj Naik 		err = -EINVAL;
48693346919SManoj Naik 		/* slash separated path component list */
48793346919SManoj Naik 		len = qword_get(mesg, buf, PAGE_SIZE);
48893346919SManoj Naik 		if (len <= 0)
48993346919SManoj Naik 			goto out_free_all;
49093346919SManoj Naik 		err = -ENOMEM;
49193346919SManoj Naik 		fsloc->locations[i].path = kstrdup(buf, GFP_KERNEL);
49293346919SManoj Naik 		if (!fsloc->locations[i].path)
49393346919SManoj Naik 			goto out_free_all;
49493346919SManoj Naik 	}
49593346919SManoj Naik 	/* migrated */
49693346919SManoj Naik 	err = get_int(mesg, &migrated);
49793346919SManoj Naik 	if (err)
49893346919SManoj Naik 		goto out_free_all;
49993346919SManoj Naik 	err = -EINVAL;
50093346919SManoj Naik 	if (migrated < 0 || migrated > 1)
50193346919SManoj Naik 		goto out_free_all;
50293346919SManoj Naik 	fsloc->migrated = migrated;
50393346919SManoj Naik 	return 0;
50493346919SManoj Naik out_free_all:
50593346919SManoj Naik 	nfsd4_fslocs_free(fsloc);
50693346919SManoj Naik 	return err;
50793346919SManoj Naik }
50893346919SManoj Naik 
secinfo_parse(char ** mesg,char * buf,struct svc_export * exp)509e677bfe4SAndy Adamson static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp)
510e677bfe4SAndy Adamson {
511e677bfe4SAndy Adamson 	struct exp_flavor_info *f;
5121f53146dSKinglong Mee 	u32 listsize;
5131f53146dSKinglong Mee 	int err;
514e677bfe4SAndy Adamson 
515be69da80SKinglong Mee 	/* more than one secinfo */
516be69da80SKinglong Mee 	if (exp->ex_nflavors)
517be69da80SKinglong Mee 		return -EINVAL;
518be69da80SKinglong Mee 
5191f53146dSKinglong Mee 	err = get_uint(mesg, &listsize);
520e677bfe4SAndy Adamson 	if (err)
521e677bfe4SAndy Adamson 		return err;
5221f53146dSKinglong Mee 	if (listsize > MAX_SECINFO_LIST)
523e677bfe4SAndy Adamson 		return -EINVAL;
524e677bfe4SAndy Adamson 
525e677bfe4SAndy Adamson 	for (f = exp->ex_flavors; f < exp->ex_flavors + listsize; f++) {
526a007c4c3SJ. Bruce Fields 		err = get_uint(mesg, &f->pseudoflavor);
527e677bfe4SAndy Adamson 		if (err)
528e677bfe4SAndy Adamson 			return err;
529e677bfe4SAndy Adamson 		/*
53080492e7dSRoel Kluin 		 * XXX: It would be nice to also check whether this
53180492e7dSRoel Kluin 		 * pseudoflavor is supported, so we can discover the
53280492e7dSRoel Kluin 		 * problem at export time instead of when a client fails
53380492e7dSRoel Kluin 		 * to authenticate.
534e677bfe4SAndy Adamson 		 */
535a007c4c3SJ. Bruce Fields 		err = get_uint(mesg, &f->flags);
536e677bfe4SAndy Adamson 		if (err)
537e677bfe4SAndy Adamson 			return err;
538e677bfe4SAndy Adamson 		/* Only some flags are allowed to differ between flavors: */
539e677bfe4SAndy Adamson 		if (~NFSEXP_SECINFO_FLAGS & (f->flags ^ exp->ex_flags))
540e677bfe4SAndy Adamson 			return -EINVAL;
541e677bfe4SAndy Adamson 	}
542e677bfe4SAndy Adamson 	exp->ex_nflavors = listsize;
543e677bfe4SAndy Adamson 	return 0;
544e677bfe4SAndy Adamson }
545e677bfe4SAndy Adamson 
54693346919SManoj Naik #else /* CONFIG_NFSD_V4 */
547e677bfe4SAndy Adamson static inline int
fsloc_parse(char ** mesg,char * buf,struct nfsd4_fs_locations * fsloc)548e677bfe4SAndy Adamson fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc){return 0;}
549e677bfe4SAndy Adamson static inline int
secinfo_parse(char ** mesg,char * buf,struct svc_export * exp)550e677bfe4SAndy Adamson secinfo_parse(char **mesg, char *buf, struct svc_export *exp) { return 0; }
55193346919SManoj Naik #endif
55293346919SManoj Naik 
xprtsec_parse(char ** mesg,char * buf,struct svc_export * exp)5539280c577SChuck Lever static int xprtsec_parse(char **mesg, char *buf, struct svc_export *exp)
5549280c577SChuck Lever {
5559280c577SChuck Lever 	unsigned int i, mode, listsize;
5569280c577SChuck Lever 	int err;
5579280c577SChuck Lever 
5589280c577SChuck Lever 	err = get_uint(mesg, &listsize);
5599280c577SChuck Lever 	if (err)
5609280c577SChuck Lever 		return err;
5619280c577SChuck Lever 	if (listsize > NFSEXP_XPRTSEC_NUM)
5629280c577SChuck Lever 		return -EINVAL;
5639280c577SChuck Lever 
5649280c577SChuck Lever 	exp->ex_xprtsec_modes = 0;
5659280c577SChuck Lever 	for (i = 0; i < listsize; i++) {
5669280c577SChuck Lever 		err = get_uint(mesg, &mode);
5679280c577SChuck Lever 		if (err)
5689280c577SChuck Lever 			return err;
5699280c577SChuck Lever 		if (mode > NFSEXP_XPRTSEC_MTLS)
5709280c577SChuck Lever 			return -EINVAL;
5719280c577SChuck Lever 		exp->ex_xprtsec_modes |= mode;
5729280c577SChuck Lever 	}
5739280c577SChuck Lever 	return 0;
5749280c577SChuck Lever }
5759280c577SChuck Lever 
5760d63790cSKinglong Mee static inline int
nfsd_uuid_parse(char ** mesg,char * buf,unsigned char ** puuid)57712ce5f8cSChristoph Hellwig nfsd_uuid_parse(char **mesg, char *buf, unsigned char **puuid)
5780d63790cSKinglong Mee {
5790d63790cSKinglong Mee 	int len;
5800d63790cSKinglong Mee 
581be69da80SKinglong Mee 	/* more than one uuid */
582be69da80SKinglong Mee 	if (*puuid)
583be69da80SKinglong Mee 		return -EINVAL;
584be69da80SKinglong Mee 
5850d63790cSKinglong Mee 	/* expect a 16 byte uuid encoded as \xXXXX... */
5860d63790cSKinglong Mee 	len = qword_get(mesg, buf, PAGE_SIZE);
58794eb3689SKinglong Mee 	if (len != EX_UUID_LEN)
5880d63790cSKinglong Mee 		return -EINVAL;
5890d63790cSKinglong Mee 
59094eb3689SKinglong Mee 	*puuid = kmemdup(buf, EX_UUID_LEN, GFP_KERNEL);
5910d63790cSKinglong Mee 	if (*puuid == NULL)
5920d63790cSKinglong Mee 		return -ENOMEM;
5930d63790cSKinglong Mee 
5940d63790cSKinglong Mee 	return 0;
5950d63790cSKinglong Mee }
5960d63790cSKinglong Mee 
svc_export_parse(struct cache_detail * cd,char * mesg,int mlen)5971da177e4SLinus Torvalds static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
5981da177e4SLinus Torvalds {
5991da177e4SLinus Torvalds 	/* client path expiry [flags anonuid anongid fsid] */
6001da177e4SLinus Torvalds 	char *buf;
6011da177e4SLinus Torvalds 	int err;
6021da177e4SLinus Torvalds 	struct auth_domain *dom = NULL;
603c1a2a475SAl Viro 	struct svc_export exp = {}, *expp;
6041da177e4SLinus Torvalds 	int an_int;
6051da177e4SLinus Torvalds 
6061da177e4SLinus Torvalds 	if (mesg[mlen-1] != '\n')
6071da177e4SLinus Torvalds 		return -EINVAL;
6081da177e4SLinus Torvalds 	mesg[mlen-1] = 0;
6091da177e4SLinus Torvalds 
6101da177e4SLinus Torvalds 	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
611c1a2a475SAl Viro 	if (!buf)
612c1a2a475SAl Viro 		return -ENOMEM;
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	/* client */
6151da177e4SLinus Torvalds 	err = -EINVAL;
61675bfb704SColin Ian King 	if (qword_get(&mesg, buf, PAGE_SIZE) <= 0)
617c1a2a475SAl Viro 		goto out;
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 	err = -ENOENT;
6201da177e4SLinus Torvalds 	dom = auth_domain_find(buf);
6211da177e4SLinus Torvalds 	if (!dom)
6221da177e4SLinus Torvalds 		goto out;
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	/* path */
6251da177e4SLinus Torvalds 	err = -EINVAL;
62675bfb704SColin Ian King 	if (qword_get(&mesg, buf, PAGE_SIZE) <= 0)
627c1a2a475SAl Viro 		goto out1;
6281da177e4SLinus Torvalds 
629c1a2a475SAl Viro 	err = kern_path(buf, 0, &exp.ex_path);
630c1a2a475SAl Viro 	if (err)
631c1a2a475SAl Viro 		goto out1;
632c1a2a475SAl Viro 
6331da177e4SLinus Torvalds 	exp.ex_client = dom;
634db3a3532SStanislav Kinsbursky 	exp.cd = cd;
6359cf514ccSChristoph Hellwig 	exp.ex_devid_map = NULL;
6369280c577SChuck Lever 	exp.ex_xprtsec_modes = NFSEXP_XPRTSEC_ALL;
637c1a2a475SAl Viro 
6381da177e4SLinus Torvalds 	/* expiry */
639cf64b9bcSNeilBrown 	err = get_expiry(&mesg, &exp.h.expiry_time);
640cf64b9bcSNeilBrown 	if (err)
641c1a2a475SAl Viro 		goto out3;
6421da177e4SLinus Torvalds 
6431da177e4SLinus Torvalds 	/* flags */
6441da177e4SLinus Torvalds 	err = get_int(&mesg, &an_int);
6454a4b8831SJ. Bruce Fields 	if (err == -ENOENT) {
6464a4b8831SJ. Bruce Fields 		err = 0;
6471da177e4SLinus Torvalds 		set_bit(CACHE_NEGATIVE, &exp.h.flags);
6484a4b8831SJ. Bruce Fields 	} else {
649c1a2a475SAl Viro 		if (err || an_int < 0)
650c1a2a475SAl Viro 			goto out3;
6511da177e4SLinus Torvalds 		exp.ex_flags= an_int;
6521da177e4SLinus Torvalds 
6531da177e4SLinus Torvalds 		/* anon uid */
6541da177e4SLinus Torvalds 		err = get_int(&mesg, &an_int);
655c1a2a475SAl Viro 		if (err)
656c1a2a475SAl Viro 			goto out3;
657e45d1a18STrond Myklebust 		exp.ex_anon_uid= make_kuid(current_user_ns(), an_int);
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds 		/* anon gid */
6601da177e4SLinus Torvalds 		err = get_int(&mesg, &an_int);
661c1a2a475SAl Viro 		if (err)
662c1a2a475SAl Viro 			goto out3;
663e45d1a18STrond Myklebust 		exp.ex_anon_gid= make_kgid(current_user_ns(), an_int);
6641da177e4SLinus Torvalds 
6651da177e4SLinus Torvalds 		/* fsid */
6661da177e4SLinus Torvalds 		err = get_int(&mesg, &an_int);
667c1a2a475SAl Viro 		if (err)
668c1a2a475SAl Viro 			goto out3;
6691da177e4SLinus Torvalds 		exp.ex_fsid = an_int;
6701da177e4SLinus Torvalds 
67175bfb704SColin Ian King 		while (qword_get(&mesg, buf, PAGE_SIZE) > 0) {
672af6a4e28SNeilBrown 			if (strcmp(buf, "fsloc") == 0)
67393346919SManoj Naik 				err = fsloc_parse(&mesg, buf, &exp.ex_fslocs);
6740d63790cSKinglong Mee 			else if (strcmp(buf, "uuid") == 0)
67512ce5f8cSChristoph Hellwig 				err = nfsd_uuid_parse(&mesg, buf, &exp.ex_uuid);
6760d63790cSKinglong Mee 			else if (strcmp(buf, "secinfo") == 0)
677e677bfe4SAndy Adamson 				err = secinfo_parse(&mesg, buf, &exp);
6789280c577SChuck Lever 			else if (strcmp(buf, "xprtsec") == 0)
6799280c577SChuck Lever 				err = xprtsec_parse(&mesg, buf, &exp);
680e677bfe4SAndy Adamson 			else
681af6a4e28SNeilBrown 				/* quietly ignore unknown words and anything
682af6a4e28SNeilBrown 				 * following. Newer user-space can try to set
683af6a4e28SNeilBrown 				 * new values, then see what the result was.
684af6a4e28SNeilBrown 				 */
685af6a4e28SNeilBrown 				break;
68693346919SManoj Naik 			if (err)
687c1a2a475SAl Viro 				goto out4;
6881da177e4SLinus Torvalds 		}
6891da177e4SLinus Torvalds 
690899bf2ceSChristian Brauner 		err = check_export(&exp.ex_path, &exp.ex_flags, exp.ex_uuid);
691c1a2a475SAl Viro 		if (err)
692c1a2a475SAl Viro 			goto out4;
6939280c577SChuck Lever 
694427d6c66SJ. Bruce Fields 		/*
6956f6cc320SJ. Bruce Fields 		 * No point caching this if it would immediately expire.
6966f6cc320SJ. Bruce Fields 		 * Also, this protects exportfs's dummy export from the
6976f6cc320SJ. Bruce Fields 		 * anon_uid/anon_gid checks:
6986f6cc320SJ. Bruce Fields 		 */
6996f6cc320SJ. Bruce Fields 		if (exp.h.expiry_time < seconds_since_boot())
7006f6cc320SJ. Bruce Fields 			goto out4;
7016f6cc320SJ. Bruce Fields 		/*
702427d6c66SJ. Bruce Fields 		 * For some reason exportfs has been passing down an
703427d6c66SJ. Bruce Fields 		 * invalid (-1) uid & gid on the "dummy" export which it
704427d6c66SJ. Bruce Fields 		 * uses to test export support.  To make sure exportfs
705427d6c66SJ. Bruce Fields 		 * sees errors from check_export we therefore need to
706427d6c66SJ. Bruce Fields 		 * delay these checks till after check_export:
707427d6c66SJ. Bruce Fields 		 */
7086f6cc320SJ. Bruce Fields 		err = -EINVAL;
709427d6c66SJ. Bruce Fields 		if (!uid_valid(exp.ex_anon_uid))
710427d6c66SJ. Bruce Fields 			goto out4;
711427d6c66SJ. Bruce Fields 		if (!gid_valid(exp.ex_anon_gid))
712427d6c66SJ. Bruce Fields 			goto out4;
7136f6cc320SJ. Bruce Fields 		err = 0;
7149cf514ccSChristoph Hellwig 
7159cf514ccSChristoph Hellwig 		nfsd4_setup_layout_type(&exp);
716af6a4e28SNeilBrown 	}
717af6a4e28SNeilBrown 
7184f7774c3SNeilBrown 	expp = svc_export_lookup(&exp);
7196a30e47fSTrond Myklebust 	if (!expp) {
7206a30e47fSTrond Myklebust 		err = -ENOMEM;
7216a30e47fSTrond Myklebust 		goto out4;
7226a30e47fSTrond Myklebust 	}
7234f7774c3SNeilBrown 	expp = svc_export_update(&exp, expp);
7246a30e47fSTrond Myklebust 	if (expp) {
7256a30e47fSTrond Myklebust 		trace_nfsd_export_update(expp);
7261da177e4SLinus Torvalds 		cache_flush();
7274f7774c3SNeilBrown 		exp_put(expp);
7286a30e47fSTrond Myklebust 	} else
7296a30e47fSTrond Myklebust 		err = -ENOMEM;
730c1a2a475SAl Viro out4:
731af6a4e28SNeilBrown 	nfsd4_fslocs_free(&exp.ex_fslocs);
732af6a4e28SNeilBrown 	kfree(exp.ex_uuid);
733c1a2a475SAl Viro out3:
734c1a2a475SAl Viro 	path_put(&exp.ex_path);
735c1a2a475SAl Viro out1:
7361da177e4SLinus Torvalds 	auth_domain_put(dom);
737c1a2a475SAl Viro out:
7381da177e4SLinus Torvalds 	kfree(buf);
7391da177e4SLinus Torvalds 	return err;
7401da177e4SLinus Torvalds }
7411da177e4SLinus Torvalds 
74293346919SManoj Naik static void exp_flags(struct seq_file *m, int flag, int fsid,
7434c1e1b34SEric W. Biederman 		kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fslocs);
74491fe39d3SJ. Bruce Fields static void show_secinfo(struct seq_file *m, struct svc_export *exp);
7451da177e4SLinus Torvalds 
is_export_stats_file(struct seq_file * m)74620ad856eSAmir Goldstein static int is_export_stats_file(struct seq_file *m)
74720ad856eSAmir Goldstein {
74820ad856eSAmir Goldstein 	/*
74920ad856eSAmir Goldstein 	 * The export_stats file uses the same ops as the exports file.
75020ad856eSAmir Goldstein 	 * We use the file's name to determine the reported info per export.
75120ad856eSAmir Goldstein 	 * There is no rename in nsfdfs, so d_name.name is stable.
75220ad856eSAmir Goldstein 	 */
75320ad856eSAmir Goldstein 	return !strcmp(m->file->f_path.dentry->d_name.name, "export_stats");
75420ad856eSAmir Goldstein }
75520ad856eSAmir Goldstein 
svc_export_show(struct seq_file * m,struct cache_detail * cd,struct cache_head * h)7561da177e4SLinus Torvalds static int svc_export_show(struct seq_file *m,
7571da177e4SLinus Torvalds 			   struct cache_detail *cd,
7581da177e4SLinus Torvalds 			   struct cache_head *h)
7591da177e4SLinus Torvalds {
7601da177e4SLinus Torvalds 	struct svc_export *exp;
76120ad856eSAmir Goldstein 	bool export_stats = is_export_stats_file(m);
7621da177e4SLinus Torvalds 
7631da177e4SLinus Torvalds 	if (h == NULL) {
76420ad856eSAmir Goldstein 		if (export_stats)
76520ad856eSAmir Goldstein 			seq_puts(m, "#path domain start-time\n#\tstats\n");
76620ad856eSAmir Goldstein 		else
7671da177e4SLinus Torvalds 			seq_puts(m, "#path domain(flags)\n");
7681da177e4SLinus Torvalds 		return 0;
7691da177e4SLinus Torvalds 	}
7701da177e4SLinus Torvalds 	exp = container_of(h, struct svc_export, h);
771c32c2f63SJan Blunck 	seq_path(m, &exp->ex_path, " \t\n\\");
7721da177e4SLinus Torvalds 	seq_putc(m, '\t');
7731da177e4SLinus Torvalds 	seq_escape(m, exp->ex_client->name, " \t\n\\");
77420ad856eSAmir Goldstein 	if (export_stats) {
7759b31d561SChuck Lever 		struct percpu_counter *counter = exp->ex_stats->counter;
7769b31d561SChuck Lever 
7779b31d561SChuck Lever 		seq_printf(m, "\t%lld\n", exp->ex_stats->start_time);
77820ad856eSAmir Goldstein 		seq_printf(m, "\tfh_stale: %lld\n",
7799b31d561SChuck Lever 			   percpu_counter_sum_positive(&counter[EXP_STATS_FH_STALE]));
78020ad856eSAmir Goldstein 		seq_printf(m, "\tio_read: %lld\n",
7819b31d561SChuck Lever 			   percpu_counter_sum_positive(&counter[EXP_STATS_IO_READ]));
78220ad856eSAmir Goldstein 		seq_printf(m, "\tio_write: %lld\n",
7839b31d561SChuck Lever 			   percpu_counter_sum_positive(&counter[EXP_STATS_IO_WRITE]));
78420ad856eSAmir Goldstein 		seq_putc(m, '\n');
78520ad856eSAmir Goldstein 		return 0;
78620ad856eSAmir Goldstein 	}
7871da177e4SLinus Torvalds 	seq_putc(m, '(');
7881da177e4SLinus Torvalds 	if (test_bit(CACHE_VALID, &h->flags) &&
789af6a4e28SNeilBrown 	    !test_bit(CACHE_NEGATIVE, &h->flags)) {
7901da177e4SLinus Torvalds 		exp_flags(m, exp->ex_flags, exp->ex_fsid,
79193346919SManoj Naik 			  exp->ex_anon_uid, exp->ex_anon_gid, &exp->ex_fslocs);
792af6a4e28SNeilBrown 		if (exp->ex_uuid) {
793af6a4e28SNeilBrown 			int i;
794af6a4e28SNeilBrown 			seq_puts(m, ",uuid=");
79594eb3689SKinglong Mee 			for (i = 0; i < EX_UUID_LEN; i++) {
796af6a4e28SNeilBrown 				if ((i&3) == 0 && i)
797af6a4e28SNeilBrown 					seq_putc(m, ':');
798af6a4e28SNeilBrown 				seq_printf(m, "%02x", exp->ex_uuid[i]);
799af6a4e28SNeilBrown 			}
800af6a4e28SNeilBrown 		}
80191fe39d3SJ. Bruce Fields 		show_secinfo(m, exp);
802af6a4e28SNeilBrown 	}
8031da177e4SLinus Torvalds 	seq_puts(m, ")\n");
8041da177e4SLinus Torvalds 	return 0;
8051da177e4SLinus Torvalds }
svc_export_match(struct cache_head * a,struct cache_head * b)8064f7774c3SNeilBrown static int svc_export_match(struct cache_head *a, struct cache_head *b)
8074f7774c3SNeilBrown {
8084f7774c3SNeilBrown 	struct svc_export *orig = container_of(a, struct svc_export, h);
8094f7774c3SNeilBrown 	struct svc_export *new = container_of(b, struct svc_export, h);
8104f7774c3SNeilBrown 	return orig->ex_client == new->ex_client &&
811b77a4b2eSKinglong Mee 		path_equal(&orig->ex_path, &new->ex_path);
8124f7774c3SNeilBrown }
8134f7774c3SNeilBrown 
svc_export_init(struct cache_head * cnew,struct cache_head * citem)8144f7774c3SNeilBrown static void svc_export_init(struct cache_head *cnew, struct cache_head *citem)
8154f7774c3SNeilBrown {
8164f7774c3SNeilBrown 	struct svc_export *new = container_of(cnew, struct svc_export, h);
8174f7774c3SNeilBrown 	struct svc_export *item = container_of(citem, struct svc_export, h);
8184f7774c3SNeilBrown 
8194f7774c3SNeilBrown 	kref_get(&item->ex_client->ref);
8204f7774c3SNeilBrown 	new->ex_client = item->ex_client;
8210da22a91SKinglong Mee 	new->ex_path = item->ex_path;
8220da22a91SKinglong Mee 	path_get(&item->ex_path);
82393346919SManoj Naik 	new->ex_fslocs.locations = NULL;
82493346919SManoj Naik 	new->ex_fslocs.locations_count = 0;
82593346919SManoj Naik 	new->ex_fslocs.migrated = 0;
8268a4c3926SJeff Layton 	new->ex_layout_types = 0;
8272eeb9b2aSJeff Layton 	new->ex_uuid = NULL;
828db3a3532SStanislav Kinsbursky 	new->cd = item->cd;
8299b31d561SChuck Lever 	export_stats_reset(new->ex_stats);
8304f7774c3SNeilBrown }
8314f7774c3SNeilBrown 
export_update(struct cache_head * cnew,struct cache_head * citem)8324f7774c3SNeilBrown static void export_update(struct cache_head *cnew, struct cache_head *citem)
8334f7774c3SNeilBrown {
8344f7774c3SNeilBrown 	struct svc_export *new = container_of(cnew, struct svc_export, h);
8354f7774c3SNeilBrown 	struct svc_export *item = container_of(citem, struct svc_export, h);
836e677bfe4SAndy Adamson 	int i;
8374f7774c3SNeilBrown 
8384f7774c3SNeilBrown 	new->ex_flags = item->ex_flags;
8394f7774c3SNeilBrown 	new->ex_anon_uid = item->ex_anon_uid;
8404f7774c3SNeilBrown 	new->ex_anon_gid = item->ex_anon_gid;
8414f7774c3SNeilBrown 	new->ex_fsid = item->ex_fsid;
8429cf514ccSChristoph Hellwig 	new->ex_devid_map = item->ex_devid_map;
8439cf514ccSChristoph Hellwig 	item->ex_devid_map = NULL;
844af6a4e28SNeilBrown 	new->ex_uuid = item->ex_uuid;
845af6a4e28SNeilBrown 	item->ex_uuid = NULL;
84693346919SManoj Naik 	new->ex_fslocs.locations = item->ex_fslocs.locations;
84793346919SManoj Naik 	item->ex_fslocs.locations = NULL;
84893346919SManoj Naik 	new->ex_fslocs.locations_count = item->ex_fslocs.locations_count;
84993346919SManoj Naik 	item->ex_fslocs.locations_count = 0;
85093346919SManoj Naik 	new->ex_fslocs.migrated = item->ex_fslocs.migrated;
85193346919SManoj Naik 	item->ex_fslocs.migrated = 0;
8528a4c3926SJeff Layton 	new->ex_layout_types = item->ex_layout_types;
853e677bfe4SAndy Adamson 	new->ex_nflavors = item->ex_nflavors;
854e677bfe4SAndy Adamson 	for (i = 0; i < MAX_SECINFO_LIST; i++) {
855e677bfe4SAndy Adamson 		new->ex_flavors[i] = item->ex_flavors[i];
856e677bfe4SAndy Adamson 	}
8579280c577SChuck Lever 	new->ex_xprtsec_modes = item->ex_xprtsec_modes;
8584f7774c3SNeilBrown }
8594f7774c3SNeilBrown 
svc_export_alloc(void)8604f7774c3SNeilBrown static struct cache_head *svc_export_alloc(void)
8614f7774c3SNeilBrown {
8624f7774c3SNeilBrown 	struct svc_export *i = kmalloc(sizeof(*i), GFP_KERNEL);
86320ad856eSAmir Goldstein 	if (!i)
8644f7774c3SNeilBrown 		return NULL;
86520ad856eSAmir Goldstein 
8669b31d561SChuck Lever 	i->ex_stats = kmalloc(sizeof(*(i->ex_stats)), GFP_KERNEL);
8679b31d561SChuck Lever 	if (!i->ex_stats) {
8689b31d561SChuck Lever 		kfree(i);
8699b31d561SChuck Lever 		return NULL;
8709b31d561SChuck Lever 	}
8719b31d561SChuck Lever 
8729b31d561SChuck Lever 	if (export_stats_init(i->ex_stats)) {
8739b31d561SChuck Lever 		kfree(i->ex_stats);
87420ad856eSAmir Goldstein 		kfree(i);
87520ad856eSAmir Goldstein 		return NULL;
87620ad856eSAmir Goldstein 	}
87720ad856eSAmir Goldstein 
87820ad856eSAmir Goldstein 	return &i->h;
8794f7774c3SNeilBrown }
8804f7774c3SNeilBrown 
881ae2e408eSBhumika Goyal static const struct cache_detail svc_export_cache_template = {
882f35279d3SBruce Allan 	.owner		= THIS_MODULE,
8831da177e4SLinus Torvalds 	.hash_size	= EXPORT_HASHMAX,
8841da177e4SLinus Torvalds 	.name		= "nfsd.export",
8851da177e4SLinus Torvalds 	.cache_put	= svc_export_put,
88665286b88STrond Myklebust 	.cache_upcall	= svc_export_upcall,
88773fb847aSStanislav Kinsbursky 	.cache_request	= svc_export_request,
8881da177e4SLinus Torvalds 	.cache_parse	= svc_export_parse,
8891da177e4SLinus Torvalds 	.cache_show	= svc_export_show,
8904f7774c3SNeilBrown 	.match		= svc_export_match,
8914f7774c3SNeilBrown 	.init		= svc_export_init,
8924f7774c3SNeilBrown 	.update		= export_update,
8934f7774c3SNeilBrown 	.alloc		= svc_export_alloc,
8941da177e4SLinus Torvalds };
8951da177e4SLinus Torvalds 
89661f8603dSNeilBrown static int
svc_export_hash(struct svc_export * exp)89761f8603dSNeilBrown svc_export_hash(struct svc_export *exp)
89861f8603dSNeilBrown {
89961f8603dSNeilBrown 	int hash;
90061f8603dSNeilBrown 
90161f8603dSNeilBrown 	hash = hash_ptr(exp->ex_client, EXPORT_HASHBITS);
90261f8603dSNeilBrown 	hash ^= hash_ptr(exp->ex_path.dentry, EXPORT_HASHBITS);
90361f8603dSNeilBrown 	hash ^= hash_ptr(exp->ex_path.mnt, EXPORT_HASHBITS);
90461f8603dSNeilBrown 	return hash;
90561f8603dSNeilBrown }
90661f8603dSNeilBrown 
9074f7774c3SNeilBrown static struct svc_export *
svc_export_lookup(struct svc_export * exp)9084f7774c3SNeilBrown svc_export_lookup(struct svc_export *exp)
9091da177e4SLinus Torvalds {
9104f7774c3SNeilBrown 	struct cache_head *ch;
91161f8603dSNeilBrown 	int hash = svc_export_hash(exp);
9124f7774c3SNeilBrown 
9139ceddd9dSTrond Myklebust 	ch = sunrpc_cache_lookup_rcu(exp->cd, &exp->h, hash);
9144f7774c3SNeilBrown 	if (ch)
9154f7774c3SNeilBrown 		return container_of(ch, struct svc_export, h);
9164f7774c3SNeilBrown 	else
9174f7774c3SNeilBrown 		return NULL;
9181da177e4SLinus Torvalds }
9191da177e4SLinus Torvalds 
92074cae61aSAdrian Bunk static struct svc_export *
svc_export_update(struct svc_export * new,struct svc_export * old)9214f7774c3SNeilBrown svc_export_update(struct svc_export *new, struct svc_export *old)
9221da177e4SLinus Torvalds {
9234f7774c3SNeilBrown 	struct cache_head *ch;
92461f8603dSNeilBrown 	int hash = svc_export_hash(old);
9251da177e4SLinus Torvalds 
926db3a3532SStanislav Kinsbursky 	ch = sunrpc_cache_update(old->cd, &new->h, &old->h, hash);
9274f7774c3SNeilBrown 	if (ch)
9284f7774c3SNeilBrown 		return container_of(ch, struct svc_export, h);
9294f7774c3SNeilBrown 	else
9304f7774c3SNeilBrown 		return NULL;
9314f7774c3SNeilBrown }
9321da177e4SLinus Torvalds 
9331da177e4SLinus Torvalds 
93474cae61aSAdrian Bunk static struct svc_expkey *
exp_find_key(struct cache_detail * cd,struct auth_domain * clp,int fsid_type,u32 * fsidv,struct cache_req * reqp)935e6d615f7SKinglong Mee exp_find_key(struct cache_detail *cd, struct auth_domain *clp, int fsid_type,
936c89172e3SStanislav Kinsbursky 	     u32 *fsidv, struct cache_req *reqp)
9371da177e4SLinus Torvalds {
9381da177e4SLinus Torvalds 	struct svc_expkey key, *ek;
9391da177e4SLinus Torvalds 	int err;
9401da177e4SLinus Torvalds 
9411da177e4SLinus Torvalds 	if (!clp)
9422d3bb252SJ. Bruce Fields 		return ERR_PTR(-ENOENT);
9431da177e4SLinus Torvalds 
9441da177e4SLinus Torvalds 	key.ek_client = clp;
9451da177e4SLinus Torvalds 	key.ek_fsidtype = fsid_type;
9461da177e4SLinus Torvalds 	memcpy(key.ek_fsid, fsidv, key_len(fsid_type));
9471da177e4SLinus Torvalds 
948c89172e3SStanislav Kinsbursky 	ek = svc_expkey_lookup(cd, &key);
9492d3bb252SJ. Bruce Fields 	if (ek == NULL)
9502d3bb252SJ. Bruce Fields 		return ERR_PTR(-ENOMEM);
951c89172e3SStanislav Kinsbursky 	err = cache_check(cd, &ek->h, reqp);
952cf749f3cSTrond Myklebust 	if (err) {
953cf749f3cSTrond Myklebust 		trace_nfsd_exp_find_key(&key, err);
9542d3bb252SJ. Bruce Fields 		return ERR_PTR(err);
955cf749f3cSTrond Myklebust 	}
9561da177e4SLinus Torvalds 	return ek;
9571da177e4SLinus Torvalds }
9581da177e4SLinus Torvalds 
959e6d615f7SKinglong Mee static struct svc_export *
exp_get_by_name(struct cache_detail * cd,struct auth_domain * clp,const struct path * path,struct cache_req * reqp)960e6d615f7SKinglong Mee exp_get_by_name(struct cache_detail *cd, struct auth_domain *clp,
9612a75cfa6SStanislav Kinsbursky 		const struct path *path, struct cache_req *reqp)
9621da177e4SLinus Torvalds {
9631da177e4SLinus Torvalds 	struct svc_export *exp, key;
9642d3bb252SJ. Bruce Fields 	int err;
9651da177e4SLinus Torvalds 
9661da177e4SLinus Torvalds 	if (!clp)
9672d3bb252SJ. Bruce Fields 		return ERR_PTR(-ENOENT);
9681da177e4SLinus Torvalds 
9691da177e4SLinus Torvalds 	key.ex_client = clp;
97055430e2eSAl Viro 	key.ex_path = *path;
9712a75cfa6SStanislav Kinsbursky 	key.cd = cd;
9721da177e4SLinus Torvalds 
9734f7774c3SNeilBrown 	exp = svc_export_lookup(&key);
9742d3bb252SJ. Bruce Fields 	if (exp == NULL)
9752d3bb252SJ. Bruce Fields 		return ERR_PTR(-ENOMEM);
9762a75cfa6SStanislav Kinsbursky 	err = cache_check(cd, &exp->h, reqp);
977cf749f3cSTrond Myklebust 	if (err) {
978cf749f3cSTrond Myklebust 		trace_nfsd_exp_get_by_name(&key, err);
9792d3bb252SJ. Bruce Fields 		return ERR_PTR(err);
980cf749f3cSTrond Myklebust 	}
9811da177e4SLinus Torvalds 	return exp;
9821da177e4SLinus Torvalds }
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds /*
9851da177e4SLinus Torvalds  * Find the export entry for a given dentry.
9861da177e4SLinus Torvalds  */
987e6d615f7SKinglong Mee static struct svc_export *
exp_parent(struct cache_detail * cd,struct auth_domain * clp,struct path * path)988e6d615f7SKinglong Mee exp_parent(struct cache_detail *cd, struct auth_domain *clp, struct path *path)
9891da177e4SLinus Torvalds {
9905bf3bd2bSAl Viro 	struct dentry *saved = dget(path->dentry);
991e6d615f7SKinglong Mee 	struct svc_export *exp = exp_get_by_name(cd, clp, path, NULL);
9921da177e4SLinus Torvalds 
9935bf3bd2bSAl Viro 	while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(path->dentry)) {
9945bf3bd2bSAl Viro 		struct dentry *parent = dget_parent(path->dentry);
9955bf3bd2bSAl Viro 		dput(path->dentry);
9965bf3bd2bSAl Viro 		path->dentry = parent;
9972a75cfa6SStanislav Kinsbursky 		exp = exp_get_by_name(cd, clp, path, NULL);
9981da177e4SLinus Torvalds 	}
9995bf3bd2bSAl Viro 	dput(path->dentry);
10005bf3bd2bSAl Viro 	path->dentry = saved;
10011da177e4SLinus Torvalds 	return exp;
10021da177e4SLinus Torvalds }
10031da177e4SLinus Torvalds 
10041da177e4SLinus Torvalds 
10051da177e4SLinus Torvalds 
10061da177e4SLinus Torvalds /*
10071da177e4SLinus Torvalds  * Obtain the root fh on behalf of a client.
10081da177e4SLinus Torvalds  * This could be done in user space, but I feel that it adds some safety
10091da177e4SLinus Torvalds  * since its harder to fool a kernel module than a user space program.
10101da177e4SLinus Torvalds  */
10111da177e4SLinus Torvalds int
exp_rootfh(struct net * net,struct auth_domain * clp,char * name,struct knfsd_fh * f,int maxsize)1012e6d615f7SKinglong Mee exp_rootfh(struct net *net, struct auth_domain *clp, char *name,
10132a75cfa6SStanislav Kinsbursky 	   struct knfsd_fh *f, int maxsize)
10141da177e4SLinus Torvalds {
10151da177e4SLinus Torvalds 	struct svc_export	*exp;
1016a63bb996SAl Viro 	struct path		path;
10171da177e4SLinus Torvalds 	struct inode		*inode;
10181da177e4SLinus Torvalds 	struct svc_fh		fh;
10191da177e4SLinus Torvalds 	int			err;
1020b3853e0eSStanislav Kinsbursky 	struct nfsd_net		*nn = net_generic(net, nfsd_net_id);
1021b3853e0eSStanislav Kinsbursky 	struct cache_detail	*cd = nn->svc_export_cache;
10221da177e4SLinus Torvalds 
10231da177e4SLinus Torvalds 	err = -EPERM;
10241da177e4SLinus Torvalds 	/* NB: we probably ought to check that it's NUL-terminated */
1025a63bb996SAl Viro 	if (kern_path(name, 0, &path)) {
1026a63bb996SAl Viro 		printk("nfsd: exp_rootfh path not found %s", name);
10271da177e4SLinus Torvalds 		return err;
10281da177e4SLinus Torvalds 	}
10292b0143b5SDavid Howells 	inode = d_inode(path.dentry);
10301da177e4SLinus Torvalds 
10311da177e4SLinus Torvalds 	dprintk("nfsd: exp_rootfh(%s [%p] %s:%s/%ld)\n",
1032a63bb996SAl Viro 		 name, path.dentry, clp->name,
10331da177e4SLinus Torvalds 		 inode->i_sb->s_id, inode->i_ino);
10342a75cfa6SStanislav Kinsbursky 	exp = exp_parent(cd, clp, &path);
10354b41bd85SJ.Bruce Fields 	if (IS_ERR(exp)) {
10364b41bd85SJ.Bruce Fields 		err = PTR_ERR(exp);
10374b41bd85SJ.Bruce Fields 		goto out;
10384b41bd85SJ.Bruce Fields 	}
10391da177e4SLinus Torvalds 
10401da177e4SLinus Torvalds 	/*
10411da177e4SLinus Torvalds 	 * fh must be initialized before calling fh_compose
10421da177e4SLinus Torvalds 	 */
10431da177e4SLinus Torvalds 	fh_init(&fh, maxsize);
1044a63bb996SAl Viro 	if (fh_compose(&fh, exp, path.dentry, NULL))
10451da177e4SLinus Torvalds 		err = -EINVAL;
10461da177e4SLinus Torvalds 	else
10471da177e4SLinus Torvalds 		err = 0;
10481da177e4SLinus Torvalds 	memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh));
10491da177e4SLinus Torvalds 	fh_put(&fh);
10501da177e4SLinus Torvalds 	exp_put(exp);
10511da177e4SLinus Torvalds out:
1052a63bb996SAl Viro 	path_put(&path);
10531da177e4SLinus Torvalds 	return err;
10541da177e4SLinus Torvalds }
10551da177e4SLinus Torvalds 
exp_find(struct cache_detail * cd,struct auth_domain * clp,int fsid_type,u32 * fsidv,struct cache_req * reqp)10562a75cfa6SStanislav Kinsbursky static struct svc_export *exp_find(struct cache_detail *cd,
10572a75cfa6SStanislav Kinsbursky 				   struct auth_domain *clp, int fsid_type,
1058cce76f9bSAdrian Bunk 				   u32 *fsidv, struct cache_req *reqp)
1059eab7e2e6SNeilBrown {
1060eab7e2e6SNeilBrown 	struct svc_export *exp;
1061e5f06f72SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(cd->net, nfsd_net_id);
1062e5f06f72SStanislav Kinsbursky 	struct svc_expkey *ek = exp_find_key(nn->svc_expkey_cache, clp, fsid_type, fsidv, reqp);
10632d3bb252SJ. Bruce Fields 	if (IS_ERR(ek))
1064e231c2eeSDavid Howells 		return ERR_CAST(ek);
1065eab7e2e6SNeilBrown 
10662a75cfa6SStanislav Kinsbursky 	exp = exp_get_by_name(cd, clp, &ek->ek_path, reqp);
1067e5f06f72SStanislav Kinsbursky 	cache_put(&ek->h, nn->svc_expkey_cache);
1068eab7e2e6SNeilBrown 
10692d3bb252SJ. Bruce Fields 	if (IS_ERR(exp))
1070e231c2eeSDavid Howells 		return ERR_CAST(exp);
1071eab7e2e6SNeilBrown 	return exp;
1072eab7e2e6SNeilBrown }
1073eab7e2e6SNeilBrown 
check_nfsd_access(struct svc_export * exp,struct svc_rqst * rqstp)107432c1eb0cSAndy Adamson __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
107532c1eb0cSAndy Adamson {
10769280c577SChuck Lever 	struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors;
10779280c577SChuck Lever 	struct svc_xprt *xprt = rqstp->rq_xprt;
107832c1eb0cSAndy Adamson 
10799280c577SChuck Lever 	if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_NONE) {
10809280c577SChuck Lever 		if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags))
10819280c577SChuck Lever 			goto ok;
10829280c577SChuck Lever 	}
10839280c577SChuck Lever 	if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_TLS) {
10849280c577SChuck Lever 		if (test_bit(XPT_TLS_SESSION, &xprt->xpt_flags) &&
10859280c577SChuck Lever 		    !test_bit(XPT_PEER_AUTH, &xprt->xpt_flags))
10869280c577SChuck Lever 			goto ok;
10879280c577SChuck Lever 	}
10889280c577SChuck Lever 	if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_MTLS) {
10899280c577SChuck Lever 		if (test_bit(XPT_TLS_SESSION, &xprt->xpt_flags) &&
10909280c577SChuck Lever 		    test_bit(XPT_PEER_AUTH, &xprt->xpt_flags))
10919280c577SChuck Lever 			goto ok;
10929280c577SChuck Lever 	}
10939280c577SChuck Lever 	goto denied;
10949280c577SChuck Lever 
10959280c577SChuck Lever ok:
109632c1eb0cSAndy Adamson 	/* legacy gss-only clients are always OK: */
109732c1eb0cSAndy Adamson 	if (exp->ex_client == rqstp->rq_gssclient)
109832c1eb0cSAndy Adamson 		return 0;
109932c1eb0cSAndy Adamson 	/* ip-address based client; check sec= export option: */
110032c1eb0cSAndy Adamson 	for (f = exp->ex_flavors; f < end; f++) {
1101d5497fc6SJ. Bruce Fields 		if (f->pseudoflavor == rqstp->rq_cred.cr_flavor)
110232c1eb0cSAndy Adamson 			return 0;
110332c1eb0cSAndy Adamson 	}
110432c1eb0cSAndy Adamson 	/* defaults in absence of sec= options: */
110532c1eb0cSAndy Adamson 	if (exp->ex_nflavors == 0) {
1106d5497fc6SJ. Bruce Fields 		if (rqstp->rq_cred.cr_flavor == RPC_AUTH_NULL ||
1107d5497fc6SJ. Bruce Fields 		    rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)
110832c1eb0cSAndy Adamson 			return 0;
110932c1eb0cSAndy Adamson 	}
1110ed941643SAndrew Elble 
1111ed941643SAndrew Elble 	/* If the compound op contains a spo_must_allowed op,
1112ed941643SAndrew Elble 	 * it will be sent with integrity/protection which
1113ed941643SAndrew Elble 	 * will have to be expressly allowed on mounts that
1114ed941643SAndrew Elble 	 * don't support it
1115ed941643SAndrew Elble 	 */
1116ed941643SAndrew Elble 
1117ed941643SAndrew Elble 	if (nfsd4_spo_must_allow(rqstp))
1118ed941643SAndrew Elble 		return 0;
1119ed941643SAndrew Elble 
11209280c577SChuck Lever denied:
11214b74fd79SChuck Lever 	return rqstp->rq_vers < 4 ? nfserr_acces : nfserr_wrongsec;
112232c1eb0cSAndy Adamson }
112332c1eb0cSAndy Adamson 
11240989a788SJ. Bruce Fields /*
11252ea2209fSJ. Bruce Fields  * Uses rq_client and rq_gssclient to find an export; uses rq_client (an
11262ea2209fSJ. Bruce Fields  * auth_unix client) if it's available and has secinfo information;
11272ea2209fSJ. Bruce Fields  * otherwise, will try to use rq_gssclient.
11282ea2209fSJ. Bruce Fields  *
11290989a788SJ. Bruce Fields  * Called from functions that handle requests; functions that do work on
11300989a788SJ. Bruce Fields  * behalf of mountd are passed a single client name to use, and should
11310989a788SJ. Bruce Fields  * use exp_get_by_name() or exp_find().
11320989a788SJ. Bruce Fields  */
11330989a788SJ. Bruce Fields struct svc_export *
rqst_exp_get_by_name(struct svc_rqst * rqstp,struct path * path)113491c9fa8fSAl Viro rqst_exp_get_by_name(struct svc_rqst *rqstp, struct path *path)
11350989a788SJ. Bruce Fields {
11369a25b96cSJ. Bruce Fields 	struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT);
11379695c705SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
1138b3853e0eSStanislav Kinsbursky 	struct cache_detail *cd = nn->svc_export_cache;
11393ab4d8b1SJ. Bruce Fields 
11402ea2209fSJ. Bruce Fields 	if (rqstp->rq_client == NULL)
11412ea2209fSJ. Bruce Fields 		goto gss;
11422ea2209fSJ. Bruce Fields 
11432ea2209fSJ. Bruce Fields 	/* First try the auth_unix client: */
11442a75cfa6SStanislav Kinsbursky 	exp = exp_get_by_name(cd, rqstp->rq_client, path, &rqstp->rq_chandle);
11452ea2209fSJ. Bruce Fields 	if (PTR_ERR(exp) == -ENOENT)
11462ea2209fSJ. Bruce Fields 		goto gss;
11472ea2209fSJ. Bruce Fields 	if (IS_ERR(exp))
11482ea2209fSJ. Bruce Fields 		return exp;
11492ea2209fSJ. Bruce Fields 	/* If it has secinfo, assume there are no gss/... clients */
11502ea2209fSJ. Bruce Fields 	if (exp->ex_nflavors > 0)
11512ea2209fSJ. Bruce Fields 		return exp;
11522ea2209fSJ. Bruce Fields gss:
11532ea2209fSJ. Bruce Fields 	/* Otherwise, try falling back on gss client */
11542ea2209fSJ. Bruce Fields 	if (rqstp->rq_gssclient == NULL)
11552ea2209fSJ. Bruce Fields 		return exp;
11562a75cfa6SStanislav Kinsbursky 	gssexp = exp_get_by_name(cd, rqstp->rq_gssclient, path, &rqstp->rq_chandle);
11572ea2209fSJ. Bruce Fields 	if (PTR_ERR(gssexp) == -ENOENT)
11582ea2209fSJ. Bruce Fields 		return exp;
11599a25b96cSJ. Bruce Fields 	if (!IS_ERR(exp))
11602ea2209fSJ. Bruce Fields 		exp_put(exp);
11612ea2209fSJ. Bruce Fields 	return gssexp;
11620989a788SJ. Bruce Fields }
11630989a788SJ. Bruce Fields 
11640989a788SJ. Bruce Fields struct svc_export *
rqst_exp_find(struct svc_rqst * rqstp,int fsid_type,u32 * fsidv)11650989a788SJ. Bruce Fields rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv)
11660989a788SJ. Bruce Fields {
11679a25b96cSJ. Bruce Fields 	struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT);
11689695c705SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
1169b3853e0eSStanislav Kinsbursky 	struct cache_detail *cd = nn->svc_export_cache;
11703ab4d8b1SJ. Bruce Fields 
11712ea2209fSJ. Bruce Fields 	if (rqstp->rq_client == NULL)
11722ea2209fSJ. Bruce Fields 		goto gss;
11732ea2209fSJ. Bruce Fields 
11742ea2209fSJ. Bruce Fields 	/* First try the auth_unix client: */
11752a75cfa6SStanislav Kinsbursky 	exp = exp_find(cd, rqstp->rq_client, fsid_type,
11762a75cfa6SStanislav Kinsbursky 		       fsidv, &rqstp->rq_chandle);
11772ea2209fSJ. Bruce Fields 	if (PTR_ERR(exp) == -ENOENT)
11782ea2209fSJ. Bruce Fields 		goto gss;
11792ea2209fSJ. Bruce Fields 	if (IS_ERR(exp))
11802ea2209fSJ. Bruce Fields 		return exp;
11812ea2209fSJ. Bruce Fields 	/* If it has secinfo, assume there are no gss/... clients */
11822ea2209fSJ. Bruce Fields 	if (exp->ex_nflavors > 0)
11832ea2209fSJ. Bruce Fields 		return exp;
11842ea2209fSJ. Bruce Fields gss:
11852ea2209fSJ. Bruce Fields 	/* Otherwise, try falling back on gss client */
11862ea2209fSJ. Bruce Fields 	if (rqstp->rq_gssclient == NULL)
11872ea2209fSJ. Bruce Fields 		return exp;
11882a75cfa6SStanislav Kinsbursky 	gssexp = exp_find(cd, rqstp->rq_gssclient, fsid_type, fsidv,
11892ea2209fSJ. Bruce Fields 						&rqstp->rq_chandle);
11902ea2209fSJ. Bruce Fields 	if (PTR_ERR(gssexp) == -ENOENT)
11912ea2209fSJ. Bruce Fields 		return exp;
11929a25b96cSJ. Bruce Fields 	if (!IS_ERR(exp))
11932ea2209fSJ. Bruce Fields 		exp_put(exp);
11942ea2209fSJ. Bruce Fields 	return gssexp;
11950989a788SJ. Bruce Fields }
11960989a788SJ. Bruce Fields 
11970989a788SJ. Bruce Fields struct svc_export *
rqst_exp_parent(struct svc_rqst * rqstp,struct path * path)1198e64c390cSAl Viro rqst_exp_parent(struct svc_rqst *rqstp, struct path *path)
11990989a788SJ. Bruce Fields {
1200e64c390cSAl Viro 	struct dentry *saved = dget(path->dentry);
1201e64c390cSAl Viro 	struct svc_export *exp = rqst_exp_get_by_name(rqstp, path);
12023ab4d8b1SJ. Bruce Fields 
1203e64c390cSAl Viro 	while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(path->dentry)) {
1204e64c390cSAl Viro 		struct dentry *parent = dget_parent(path->dentry);
1205e64c390cSAl Viro 		dput(path->dentry);
1206e64c390cSAl Viro 		path->dentry = parent;
1207e64c390cSAl Viro 		exp = rqst_exp_get_by_name(rqstp, path);
12082ea2209fSJ. Bruce Fields 	}
1209e64c390cSAl Viro 	dput(path->dentry);
1210e64c390cSAl Viro 	path->dentry = saved;
12112ea2209fSJ. Bruce Fields 	return exp;
12120989a788SJ. Bruce Fields }
1213eab7e2e6SNeilBrown 
rqst_find_fsidzero_export(struct svc_rqst * rqstp)1214ed748aacSTrond Myklebust struct svc_export *rqst_find_fsidzero_export(struct svc_rqst *rqstp)
1215f39bde24SJ. Bruce Fields {
1216f39bde24SJ. Bruce Fields 	u32 fsidv[2];
1217f39bde24SJ. Bruce Fields 
1218f39bde24SJ. Bruce Fields 	mk_fsid(FSID_NUM, fsidv, 0, 0, 0, NULL);
1219f39bde24SJ. Bruce Fields 
1220260c64d2SJ. Bruce Fields 	return rqst_exp_find(rqstp, FSID_NUM, fsidv);
1221f39bde24SJ. Bruce Fields }
1222f39bde24SJ. Bruce Fields 
12231da177e4SLinus Torvalds /*
12241da177e4SLinus Torvalds  * Called when we need the filehandle for the root of the pseudofs,
12251da177e4SLinus Torvalds  * for a given NFSv4 client.   The root is defined to be the
12261da177e4SLinus Torvalds  * export point with fsid==0
12271da177e4SLinus Torvalds  */
1228c7afef1fSAl Viro __be32
exp_pseudoroot(struct svc_rqst * rqstp,struct svc_fh * fhp)1229df547efbSJ. Bruce Fields exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp)
12301da177e4SLinus Torvalds {
1231eab7e2e6SNeilBrown 	struct svc_export *exp;
1232c7afef1fSAl Viro 	__be32 rv;
12331da177e4SLinus Torvalds 
1234ed748aacSTrond Myklebust 	exp = rqst_find_fsidzero_export(rqstp);
12356899320cSJ.Bruce Fields 	if (IS_ERR(exp))
12366899320cSJ.Bruce Fields 		return nfserrno(PTR_ERR(exp));
123754775491SJan Blunck 	rv = fh_compose(fhp, exp, exp->ex_path.dentry, NULL);
1238f2d39586SNeilBrown 	exp_put(exp);
12391da177e4SLinus Torvalds 	return rv;
12401da177e4SLinus Torvalds }
12411da177e4SLinus Torvalds 
12421da177e4SLinus Torvalds static struct flags {
12431da177e4SLinus Torvalds 	int flag;
12441da177e4SLinus Torvalds 	char *name[2];
12451da177e4SLinus Torvalds } expflags[] = {
12461da177e4SLinus Torvalds 	{ NFSEXP_READONLY, {"ro", "rw"}},
12471da177e4SLinus Torvalds 	{ NFSEXP_INSECURE_PORT, {"insecure", ""}},
12481da177e4SLinus Torvalds 	{ NFSEXP_ROOTSQUASH, {"root_squash", "no_root_squash"}},
12491da177e4SLinus Torvalds 	{ NFSEXP_ALLSQUASH, {"all_squash", ""}},
12501da177e4SLinus Torvalds 	{ NFSEXP_ASYNC, {"async", "sync"}},
12511da177e4SLinus Torvalds 	{ NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}},
125218c01ab3SRajesh Ghanekar 	{ NFSEXP_NOREADDIRPLUS, {"nordirplus", ""}},
12531da177e4SLinus Torvalds 	{ NFSEXP_NOHIDE, {"nohide", ""}},
12541da177e4SLinus Torvalds 	{ NFSEXP_CROSSMOUNT, {"crossmnt", ""}},
12551da177e4SLinus Torvalds 	{ NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
12561da177e4SLinus Torvalds 	{ NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
1257eb4c86c6SSteve Dickson 	{ NFSEXP_V4ROOT, {"v4root", ""}},
12589b3075c5SChristoph Hellwig 	{ NFSEXP_PNFS, {"pnfs", ""}},
125932ddd944SJ. Bruce Fields 	{ NFSEXP_SECURITY_LABEL, {"security_label", ""}},
12601da177e4SLinus Torvalds 	{ 0, {"", ""}}
12611da177e4SLinus Torvalds };
12621da177e4SLinus Torvalds 
show_expflags(struct seq_file * m,int flags,int mask)1263ac34cdb0SJ. Bruce Fields static void show_expflags(struct seq_file *m, int flags, int mask)
12641da177e4SLinus Torvalds {
12651da177e4SLinus Torvalds 	struct flags *flg;
1266ac34cdb0SJ. Bruce Fields 	int state, first = 0;
12671da177e4SLinus Torvalds 
12681da177e4SLinus Torvalds 	for (flg = expflags; flg->flag; flg++) {
1269ac34cdb0SJ. Bruce Fields 		if (flg->flag & ~mask)
1270ac34cdb0SJ. Bruce Fields 			continue;
1271ac34cdb0SJ. Bruce Fields 		state = (flg->flag & flags) ? 0 : 1;
12721da177e4SLinus Torvalds 		if (*flg->name[state])
12731da177e4SLinus Torvalds 			seq_printf(m, "%s%s", first++?",":"", flg->name[state]);
12741da177e4SLinus Torvalds 	}
1275ac34cdb0SJ. Bruce Fields }
1276ac34cdb0SJ. Bruce Fields 
show_secinfo_flags(struct seq_file * m,int flags)127791fe39d3SJ. Bruce Fields static void show_secinfo_flags(struct seq_file *m, int flags)
127891fe39d3SJ. Bruce Fields {
127991fe39d3SJ. Bruce Fields 	seq_printf(m, ",");
128091fe39d3SJ. Bruce Fields 	show_expflags(m, flags, NFSEXP_SECINFO_FLAGS);
128191fe39d3SJ. Bruce Fields }
128291fe39d3SJ. Bruce Fields 
secinfo_flags_equal(int f,int g)128374ec1e12SJ. Bruce Fields static bool secinfo_flags_equal(int f, int g)
128474ec1e12SJ. Bruce Fields {
128574ec1e12SJ. Bruce Fields 	f &= NFSEXP_SECINFO_FLAGS;
128674ec1e12SJ. Bruce Fields 	g &= NFSEXP_SECINFO_FLAGS;
128774ec1e12SJ. Bruce Fields 	return f == g;
128874ec1e12SJ. Bruce Fields }
128974ec1e12SJ. Bruce Fields 
show_secinfo_run(struct seq_file * m,struct exp_flavor_info ** fp,struct exp_flavor_info * end)129074ec1e12SJ. Bruce Fields static int show_secinfo_run(struct seq_file *m, struct exp_flavor_info **fp, struct exp_flavor_info *end)
129174ec1e12SJ. Bruce Fields {
129274ec1e12SJ. Bruce Fields 	int flags;
129374ec1e12SJ. Bruce Fields 
129474ec1e12SJ. Bruce Fields 	flags = (*fp)->flags;
129574ec1e12SJ. Bruce Fields 	seq_printf(m, ",sec=%d", (*fp)->pseudoflavor);
129674ec1e12SJ. Bruce Fields 	(*fp)++;
129774ec1e12SJ. Bruce Fields 	while (*fp != end && secinfo_flags_equal(flags, (*fp)->flags)) {
129874ec1e12SJ. Bruce Fields 		seq_printf(m, ":%d", (*fp)->pseudoflavor);
129974ec1e12SJ. Bruce Fields 		(*fp)++;
130074ec1e12SJ. Bruce Fields 	}
130174ec1e12SJ. Bruce Fields 	return flags;
130274ec1e12SJ. Bruce Fields }
130374ec1e12SJ. Bruce Fields 
show_secinfo(struct seq_file * m,struct svc_export * exp)130491fe39d3SJ. Bruce Fields static void show_secinfo(struct seq_file *m, struct svc_export *exp)
130591fe39d3SJ. Bruce Fields {
130691fe39d3SJ. Bruce Fields 	struct exp_flavor_info *f;
130791fe39d3SJ. Bruce Fields 	struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
130874ec1e12SJ. Bruce Fields 	int flags;
130991fe39d3SJ. Bruce Fields 
131091fe39d3SJ. Bruce Fields 	if (exp->ex_nflavors == 0)
131191fe39d3SJ. Bruce Fields 		return;
131274ec1e12SJ. Bruce Fields 	f = exp->ex_flavors;
131374ec1e12SJ. Bruce Fields 	flags = show_secinfo_run(m, &f, end);
131474ec1e12SJ. Bruce Fields 	if (!secinfo_flags_equal(flags, exp->ex_flags))
131574ec1e12SJ. Bruce Fields 		show_secinfo_flags(m, flags);
131674ec1e12SJ. Bruce Fields 	while (f != end) {
131774ec1e12SJ. Bruce Fields 		flags = show_secinfo_run(m, &f, end);
131874ec1e12SJ. Bruce Fields 		show_secinfo_flags(m, flags);
131991fe39d3SJ. Bruce Fields 	}
132091fe39d3SJ. Bruce Fields }
132191fe39d3SJ. Bruce Fields 
exp_flags(struct seq_file * m,int flag,int fsid,kuid_t anonu,kgid_t anong,struct nfsd4_fs_locations * fsloc)1322ac34cdb0SJ. Bruce Fields static void exp_flags(struct seq_file *m, int flag, int fsid,
13234c1e1b34SEric W. Biederman 		kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fsloc)
1324ac34cdb0SJ. Bruce Fields {
1325e45d1a18STrond Myklebust 	struct user_namespace *userns = m->file->f_cred->user_ns;
1326e45d1a18STrond Myklebust 
1327ac34cdb0SJ. Bruce Fields 	show_expflags(m, flag, NFSEXP_ALLFLAGS);
13281da177e4SLinus Torvalds 	if (flag & NFSEXP_FSID)
1329ac34cdb0SJ. Bruce Fields 		seq_printf(m, ",fsid=%d", fsid);
1330e45d1a18STrond Myklebust 	if (!uid_eq(anonu, make_kuid(userns, (uid_t)-2)) &&
1331e45d1a18STrond Myklebust 	    !uid_eq(anonu, make_kuid(userns, 0x10000-2)))
1332e45d1a18STrond Myklebust 		seq_printf(m, ",anonuid=%u", from_kuid_munged(userns, anonu));
1333e45d1a18STrond Myklebust 	if (!gid_eq(anong, make_kgid(userns, (gid_t)-2)) &&
1334e45d1a18STrond Myklebust 	    !gid_eq(anong, make_kgid(userns, 0x10000-2)))
1335e45d1a18STrond Myklebust 		seq_printf(m, ",anongid=%u", from_kgid_munged(userns, anong));
133693346919SManoj Naik 	if (fsloc && fsloc->locations_count > 0) {
133793346919SManoj Naik 		char *loctype = (fsloc->migrated) ? "refer" : "replicas";
133893346919SManoj Naik 		int i;
133993346919SManoj Naik 
1340ac34cdb0SJ. Bruce Fields 		seq_printf(m, ",%s=", loctype);
134193346919SManoj Naik 		seq_escape(m, fsloc->locations[0].path, ",;@ \t\n\\");
134293346919SManoj Naik 		seq_putc(m, '@');
134393346919SManoj Naik 		seq_escape(m, fsloc->locations[0].hosts, ",;@ \t\n\\");
134493346919SManoj Naik 		for (i = 1; i < fsloc->locations_count; i++) {
134593346919SManoj Naik 			seq_putc(m, ';');
134693346919SManoj Naik 			seq_escape(m, fsloc->locations[i].path, ",;@ \t\n\\");
134793346919SManoj Naik 			seq_putc(m, '@');
134893346919SManoj Naik 			seq_escape(m, fsloc->locations[i].hosts, ",;@ \t\n\\");
134993346919SManoj Naik 		}
135093346919SManoj Naik 	}
13511da177e4SLinus Torvalds }
13521da177e4SLinus Torvalds 
e_show(struct seq_file * m,void * p)13531da177e4SLinus Torvalds static int e_show(struct seq_file *m, void *p)
13541da177e4SLinus Torvalds {
13551da177e4SLinus Torvalds 	struct cache_head *cp = p;
13561da177e4SLinus Torvalds 	struct svc_export *exp = container_of(cp, struct svc_export, h);
1357f2c7ea10SStanislav Kinsbursky 	struct cache_detail *cd = m->private;
135820ad856eSAmir Goldstein 	bool export_stats = is_export_stats_file(m);
13591da177e4SLinus Torvalds 
1360bc6f02e5SGreg Banks 	if (p == SEQ_START_TOKEN) {
13611da177e4SLinus Torvalds 		seq_puts(m, "# Version 1.1\n");
136220ad856eSAmir Goldstein 		if (export_stats)
136320ad856eSAmir Goldstein 			seq_puts(m, "# Path Client Start-time\n#\tStats\n");
136420ad856eSAmir Goldstein 		else
13651da177e4SLinus Torvalds 			seq_puts(m, "# Path Client(Flags) # IPs\n");
13661da177e4SLinus Torvalds 		return 0;
13671da177e4SLinus Torvalds 	}
13681da177e4SLinus Torvalds 
13691cecfdbcSYang Erkun 	if (!cache_get_rcu(&exp->h))
13701cecfdbcSYang Erkun 		return 0;
13711cecfdbcSYang Erkun 
1372f2c7ea10SStanislav Kinsbursky 	if (cache_check(cd, &exp->h, NULL))
13731da177e4SLinus Torvalds 		return 0;
13741cecfdbcSYang Erkun 
1375a09581f2SStanislav Kinsbursky 	exp_put(exp);
1376f2c7ea10SStanislav Kinsbursky 	return svc_export_show(m, cd, cp);
13771da177e4SLinus Torvalds }
13781da177e4SLinus Torvalds 
137988e9d34cSJames Morris const struct seq_operations nfs_exports_op = {
13809ceddd9dSTrond Myklebust 	.start	= cache_seq_start_rcu,
13819ceddd9dSTrond Myklebust 	.next	= cache_seq_next_rcu,
13829ceddd9dSTrond Myklebust 	.stop	= cache_seq_stop_rcu,
13831da177e4SLinus Torvalds 	.show	= e_show,
13841da177e4SLinus Torvalds };
13851da177e4SLinus Torvalds 
13861da177e4SLinus Torvalds /*
13871da177e4SLinus Torvalds  * Initialize the exports module.
13881da177e4SLinus Torvalds  */
1389dbf847ecSJ. Bruce Fields int
nfsd_export_init(struct net * net)1390b89109beSStanislav Kinsbursky nfsd_export_init(struct net *net)
13911da177e4SLinus Torvalds {
1392dbf847ecSJ. Bruce Fields 	int rv;
1393b3853e0eSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1394b3853e0eSStanislav Kinsbursky 
1395ba589528SVasily Averin 	dprintk("nfsd: initializing export module (net: %x).\n", net->ns.inum);
13961da177e4SLinus Torvalds 
1397b3853e0eSStanislav Kinsbursky 	nn->svc_export_cache = cache_create_net(&svc_export_cache_template, net);
1398b3853e0eSStanislav Kinsbursky 	if (IS_ERR(nn->svc_export_cache))
1399b3853e0eSStanislav Kinsbursky 		return PTR_ERR(nn->svc_export_cache);
1400b3853e0eSStanislav Kinsbursky 	rv = cache_register_net(nn->svc_export_cache, net);
1401dbf847ecSJ. Bruce Fields 	if (rv)
1402b3853e0eSStanislav Kinsbursky 		goto destroy_export_cache;
1403b3853e0eSStanislav Kinsbursky 
1404e5f06f72SStanislav Kinsbursky 	nn->svc_expkey_cache = cache_create_net(&svc_expkey_cache_template, net);
1405e5f06f72SStanislav Kinsbursky 	if (IS_ERR(nn->svc_expkey_cache)) {
1406e5f06f72SStanislav Kinsbursky 		rv = PTR_ERR(nn->svc_expkey_cache);
1407b3853e0eSStanislav Kinsbursky 		goto unregister_export_cache;
1408e5f06f72SStanislav Kinsbursky 	}
1409e5f06f72SStanislav Kinsbursky 	rv = cache_register_net(nn->svc_expkey_cache, net);
1410e5f06f72SStanislav Kinsbursky 	if (rv)
1411e5f06f72SStanislav Kinsbursky 		goto destroy_expkey_cache;
1412b3853e0eSStanislav Kinsbursky 	return 0;
14131da177e4SLinus Torvalds 
1414e5f06f72SStanislav Kinsbursky destroy_expkey_cache:
1415e5f06f72SStanislav Kinsbursky 	cache_destroy_net(nn->svc_expkey_cache, net);
1416b3853e0eSStanislav Kinsbursky unregister_export_cache:
1417b3853e0eSStanislav Kinsbursky 	cache_unregister_net(nn->svc_export_cache, net);
1418b3853e0eSStanislav Kinsbursky destroy_export_cache:
1419b3853e0eSStanislav Kinsbursky 	cache_destroy_net(nn->svc_export_cache, net);
1420b3853e0eSStanislav Kinsbursky 	return rv;
14211da177e4SLinus Torvalds }
14221da177e4SLinus Torvalds 
14231da177e4SLinus Torvalds /*
14241da177e4SLinus Torvalds  * Flush exports table - called when last nfsd thread is killed
14251da177e4SLinus Torvalds  */
14261da177e4SLinus Torvalds void
nfsd_export_flush(struct net * net)1427b3853e0eSStanislav Kinsbursky nfsd_export_flush(struct net *net)
14281da177e4SLinus Torvalds {
1429b3853e0eSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1430b3853e0eSStanislav Kinsbursky 
1431e5f06f72SStanislav Kinsbursky 	cache_purge(nn->svc_expkey_cache);
1432b3853e0eSStanislav Kinsbursky 	cache_purge(nn->svc_export_cache);
14331da177e4SLinus Torvalds }
14341da177e4SLinus Torvalds 
14351da177e4SLinus Torvalds /*
14361da177e4SLinus Torvalds  * Shutdown the exports module.
14371da177e4SLinus Torvalds  */
14381da177e4SLinus Torvalds void
nfsd_export_shutdown(struct net * net)1439b89109beSStanislav Kinsbursky nfsd_export_shutdown(struct net *net)
14401da177e4SLinus Torvalds {
1441b3853e0eSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
14421da177e4SLinus Torvalds 
1443ba589528SVasily Averin 	dprintk("nfsd: shutting down export module (net: %x).\n", net->ns.inum);
14441da177e4SLinus Torvalds 
1445e5f06f72SStanislav Kinsbursky 	cache_unregister_net(nn->svc_expkey_cache, net);
1446b3853e0eSStanislav Kinsbursky 	cache_unregister_net(nn->svc_export_cache, net);
1447e5f06f72SStanislav Kinsbursky 	cache_destroy_net(nn->svc_expkey_cache, net);
1448b3853e0eSStanislav Kinsbursky 	cache_destroy_net(nn->svc_export_cache, net);
1449e5f06f72SStanislav Kinsbursky 	svcauth_unix_purge(net);
14501da177e4SLinus Torvalds 
1451ba589528SVasily Averin 	dprintk("nfsd: export shutdown complete (net: %x).\n", net->ns.inum);
14521da177e4SLinus Torvalds }
1453