nfs4namespace.c 8.0 KB
Newer Older
D
David Howells 已提交
1 2 3 4
/*
 * linux/fs/nfs/nfs4namespace.c
 *
 * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
5
 * - Modified by David Howells <dhowells@redhat.com>
D
David Howells 已提交
6 7 8 9 10 11 12 13
 *
 * NFSv4 namespace
 */

#include <linux/dcache.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/nfs_fs.h>
14
#include <linux/slab.h>
D
David Howells 已提交
15 16 17 18 19
#include <linux/string.h>
#include <linux/sunrpc/clnt.h>
#include <linux/vfs.h>
#include <linux/inet.h>
#include "internal.h"
20
#include "nfs4_fs.h"
21
#include "dns_resolve.h"
D
David Howells 已提交
22 23 24 25

#define NFSDBG_FACILITY		NFSDBG_VFS

/*
26 27 28
 * Convert the NFSv4 pathname components into a standard posix path.
 *
 * Note that the resulting string will be placed at the end of the buffer
D
David Howells 已提交
29
 */
D
David Howells 已提交
30
static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
D
David Howells 已提交
31 32 33 34 35 36 37 38 39 40
					 char *buffer, ssize_t buflen)
{
	char *end = buffer + buflen;
	int n;

	*--end = '\0';
	buflen--;

	n = pathname->ncomponents;
	while (--n >= 0) {
D
David Howells 已提交
41
		const struct nfs4_string *component = &pathname->components[n];
D
David Howells 已提交
42 43 44 45 46 47 48 49 50 51 52 53
		buflen -= component->len + 1;
		if (buflen < 0)
			goto Elong;
		end -= component->len;
		memcpy(end, component->data, component->len);
		*--end = '/';
	}
	return end;
Elong:
	return ERR_PTR(-ENAMETOOLONG);
}

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
/*
 * return the path component of "<server>:<path>"
 *  nfspath - the "<server>:<path>" string
 *  end - one past the last char that could contain "<server>:"
 * returns NULL on failure
 */
static char *nfs_path_component(const char *nfspath, const char *end)
{
	char *p;

	if (*nfspath == '[') {
		/* parse [] escaped IPv6 addrs */
		p = strchr(nfspath, ']');
		if (p != NULL && ++p < end && *p == ':')
			return p + 1;
	} else {
		/* otherwise split on first colon */
		p = strchr(nfspath, ':');
		if (p != NULL && p < end)
			return p + 1;
	}
	return NULL;
}

78 79 80
/*
 * Determine the mount path as a string
 */
81
static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
82
{
83 84 85
	char *limit;
	char *path = nfs_path(&limit, dentry, buffer, buflen);
	if (!IS_ERR(path)) {
86 87 88
		char *path_component = nfs_path_component(path, limit);
		if (path_component)
			return path_component;
89 90
	}
	return path;
91 92 93 94 95 96
}

/*
 * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
 * believe to be the server path to this dentry
 */
97
static int nfs4_validate_fspath(struct dentry *dentry,
98 99 100 101 102
				const struct nfs4_fs_locations *locations,
				char *page, char *page2)
{
	const char *path, *fs_path;

103
	path = nfs4_path(dentry, page, PAGE_SIZE);
104 105 106 107 108 109 110 111 112
	if (IS_ERR(path))
		return PTR_ERR(path);

	fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE);
	if (IS_ERR(fs_path))
		return PTR_ERR(fs_path);

	if (strncmp(path, fs_path, strlen(fs_path)) != 0) {
		dprintk("%s: path %s does not begin with fsroot %s\n",
113
			__func__, path, fs_path);
114 115 116 117 118 119
		return -ENOENT;
	}

	return 0;
}

120
static size_t nfs_parse_server_name(char *string, size_t len,
121
		struct sockaddr *sa, size_t salen, struct nfs_server *server)
122
{
123
	struct net *net = rpc_net_ns(server->client);
124 125
	ssize_t ret;

126
	ret = rpc_pton(net, string, len, sa, salen);
127
	if (ret == 0) {
128
		ret = nfs_dns_resolve_name(net, string, len, sa, salen);
129 130 131 132 133 134
		if (ret < 0)
			ret = 0;
	}
	return ret;
}

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
{
	struct page *page;
	struct nfs4_secinfo_flavors *flavors;
	rpc_authflavor_t flavor;
	int err;

	page = alloc_page(GFP_KERNEL);
	if (!page)
		return -ENOMEM;
	flavors = page_address(page);

	err = nfs4_proc_secinfo(inode, name, flavors);
	if (err < 0) {
		flavor = err;
		goto out;
	}

	flavor = nfs_find_best_sec(flavors);

out:
	put_page(page);
	return flavor;
}

/*
 * Please call rpc_shutdown_client() when you are done with this client.
 */
struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode,
					struct qstr *name)
{
	struct rpc_clnt *clone;
	struct rpc_auth *auth;
	rpc_authflavor_t flavor;

	flavor = nfs4_negotiate_security(inode, name);
	if (flavor < 0)
		return ERR_PTR(flavor);

	clone = rpc_clone_client(clnt);
	if (IS_ERR(clone))
		return clone;

	auth = rpcauth_create(flavor, clone);
	if (!auth) {
		rpc_shutdown_client(clone);
		clone = ERR_PTR(-EIO);
	}

	return clone;
}

187 188 189 190
static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
				     char *page, char *page2,
				     const struct nfs4_fs_location *location)
{
191
	const size_t addr_bufsize = sizeof(struct sockaddr_storage);
192 193
	struct vfsmount *mnt = ERR_PTR(-ENOENT);
	char *mnt_path;
194
	unsigned int maxbuflen;
195
	unsigned int s;
196 197 198

	mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE);
	if (IS_ERR(mnt_path))
199
		return ERR_CAST(mnt_path);
200
	mountdata->mnt_path = mnt_path;
201
	maxbuflen = mnt_path - 1 - page2;
202

203 204 205 206
	mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL);
	if (mountdata->addr == NULL)
		return ERR_PTR(-ENOMEM);

207
	for (s = 0; s < location->nservers; s++) {
208
		const struct nfs4_string *buf = &location->servers[s];
209

210
		if (buf->len <= 0 || buf->len >= maxbuflen)
211 212
			continue;

213 214
		if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
			continue;
215 216

		mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len,
217 218
				mountdata->addr, addr_bufsize,
				NFS_SB(mountdata->sb));
219
		if (mountdata->addrlen == 0)
220
			continue;
221

222
		rpc_set_port(mountdata->addr, NFS_PORT);
223

224 225
		memcpy(page2, buf->data, buf->len);
		page2[buf->len] = '\0';
226
		mountdata->hostname = page2;
227 228 229 230 231 232 233 234 235

		snprintf(page, PAGE_SIZE, "%s:%s",
				mountdata->hostname,
				mountdata->mnt_path);

		mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, mountdata);
		if (!IS_ERR(mnt))
			break;
	}
236
	kfree(mountdata->addr);
237 238 239
	return mnt;
}

D
David Howells 已提交
240 241 242
/**
 * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
 * @dentry - parent directory
243
 * @locations - array of NFSv4 server location information
D
David Howells 已提交
244 245
 *
 */
246
static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
D
David Howells 已提交
247
					    const struct nfs4_fs_locations *locations)
D
David Howells 已提交
248 249 250
{
	struct vfsmount *mnt = ERR_PTR(-ENOENT);
	struct nfs_clone_mount mountdata = {
251
		.sb = dentry->d_sb,
D
David Howells 已提交
252
		.dentry = dentry,
253
		.authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor,
D
David Howells 已提交
254
	};
255
	char *page = NULL, *page2 = NULL;
256
	int loc, error;
D
David Howells 已提交
257 258 259 260

	if (locations == NULL || locations->nlocations <= 0)
		goto out;

261
	dprintk("%s: referral at %s/%s\n", __func__,
D
David Howells 已提交
262 263 264
		dentry->d_parent->d_name.name, dentry->d_name.name);

	page = (char *) __get_free_page(GFP_USER);
265
	if (!page)
D
David Howells 已提交
266
		goto out;
267

D
David Howells 已提交
268
	page2 = (char *) __get_free_page(GFP_USER);
269
	if (!page2)
D
David Howells 已提交
270 271
		goto out;

272
	/* Ensure fs path is a prefix of current dentry path */
273
	error = nfs4_validate_fspath(dentry, locations, page, page2);
274 275 276
	if (error < 0) {
		mnt = ERR_PTR(error);
		goto out;
D
David Howells 已提交
277 278
	}

279
	for (loc = 0; loc < locations->nlocations; loc++) {
D
David Howells 已提交
280
		const struct nfs4_fs_location *location = &locations->locations[loc];
D
David Howells 已提交
281 282

		if (location == NULL || location->nservers <= 0 ||
283
		    location->rootpath.ncomponents == 0)
D
David Howells 已提交
284 285
			continue;

286 287 288
		mnt = try_location(&mountdata, page, page2, location);
		if (!IS_ERR(mnt))
			break;
D
David Howells 已提交
289 290 291
	}

out:
292 293
	free_page((unsigned long) page);
	free_page((unsigned long) page2);
294
	dprintk("%s: done\n", __func__);
D
David Howells 已提交
295 296 297 298 299 300 301 302
	return mnt;
}

/*
 * nfs_do_refmount - handle crossing a referral on server
 * @dentry - dentry of referral
 *
 */
303
struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
D
David Howells 已提交
304
{
305
	struct vfsmount *mnt = ERR_PTR(-ENOMEM);
D
David Howells 已提交
306 307 308 309 310 311
	struct dentry *parent;
	struct nfs4_fs_locations *fs_locations = NULL;
	struct page *page;
	int err;

	/* BUG_ON(IS_ROOT(dentry)); */
312
	dprintk("%s: enter\n", __func__);
D
David Howells 已提交
313 314 315 316 317 318 319 320 321 322

	page = alloc_page(GFP_KERNEL);
	if (page == NULL)
		goto out;

	fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
	if (fs_locations == NULL)
		goto out_free;

	/* Get locations */
323 324
	mnt = ERR_PTR(-ENOENT);

D
David Howells 已提交
325
	parent = dget_parent(dentry);
326
	dprintk("%s: getting locations for %s/%s\n",
327
		__func__, parent->d_name.name, dentry->d_name.name);
328

329
	err = nfs4_proc_fs_locations(client, parent->d_inode, &dentry->d_name, fs_locations, page);
D
David Howells 已提交
330
	dput(parent);
331 332
	if (err != 0 ||
	    fs_locations->nlocations <= 0 ||
D
David Howells 已提交
333 334 335
	    fs_locations->fs_path.ncomponents <= 0)
		goto out_free;

336
	mnt = nfs_follow_referral(dentry, fs_locations);
D
David Howells 已提交
337 338 339 340
out_free:
	__free_page(page);
	kfree(fs_locations);
out:
341
	dprintk("%s: done\n", __func__);
D
David Howells 已提交
342 343
	return mnt;
}