Blame view

fs/nfs/nfs4namespace.c 6.28 KB
f7b422b17   David Howells   NFS: Split fs/nfs...
1
2
3
4
  /*
   * linux/fs/nfs/nfs4namespace.c
   *
   * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
54ceac451   David Howells   NFS: Share NFS su...
5
   * - Modified by David Howells <dhowells@redhat.com>
f7b422b17   David Howells   NFS: Split fs/nfs...
6
7
8
   *
   * NFSv4 namespace
   */
f7b422b17   David Howells   NFS: Split fs/nfs...
9
10
11
12
  #include <linux/dcache.h>
  #include <linux/mount.h>
  #include <linux/namei.h>
  #include <linux/nfs_fs.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
f7b422b17   David Howells   NFS: Split fs/nfs...
14
15
16
17
18
  #include <linux/string.h>
  #include <linux/sunrpc/clnt.h>
  #include <linux/vfs.h>
  #include <linux/inet.h>
  #include "internal.h"
c228fd3ae   Trond Myklebust   NFSv4: Cleanups f...
19
  #include "nfs4_fs.h"
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
20
  #include "dns_resolve.h"
f7b422b17   David Howells   NFS: Split fs/nfs...
21
22
23
24
  
  #define NFSDBG_FACILITY		NFSDBG_VFS
  
  /*
ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
25
26
27
   * Convert the NFSv4 pathname components into a standard posix path.
   *
   * Note that the resulting string will be placed at the end of the buffer
f7b422b17   David Howells   NFS: Split fs/nfs...
28
   */
509de8111   David Howells   NFS: Add extra co...
29
  static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
f7b422b17   David Howells   NFS: Split fs/nfs...
30
31
32
33
34
35
36
37
38
39
  					 char *buffer, ssize_t buflen)
  {
  	char *end = buffer + buflen;
  	int n;
  
  	*--end = '\0';
  	buflen--;
  
  	n = pathname->ncomponents;
  	while (--n >= 0) {
509de8111   David Howells   NFS: Add extra co...
40
  		const struct nfs4_string *component = &pathname->components[n];
f7b422b17   David Howells   NFS: Split fs/nfs...
41
42
43
44
45
46
47
48
49
50
51
  		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);
  }
54ceac451   David Howells   NFS: Share NFS su...
52
53
54
  /*
   * Determine the mount path as a string
   */
b514f872f   Al Viro   nfs: make nfs_pat...
55
  static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
54ceac451   David Howells   NFS: Share NFS su...
56
  {
b514f872f   Al Viro   nfs: make nfs_pat...
57
58
59
60
61
62
63
64
  	char *limit;
  	char *path = nfs_path(&limit, dentry, buffer, buflen);
  	if (!IS_ERR(path)) {
  		char *colon = strchr(path, ':');
  		if (colon && colon < limit)
  			path = colon + 1;
  	}
  	return path;
54ceac451   David Howells   NFS: Share NFS su...
65
66
67
68
69
70
  }
  
  /*
   * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
   * believe to be the server path to this dentry
   */
b514f872f   Al Viro   nfs: make nfs_pat...
71
  static int nfs4_validate_fspath(struct dentry *dentry,
54ceac451   David Howells   NFS: Share NFS su...
72
73
74
75
  				const struct nfs4_fs_locations *locations,
  				char *page, char *page2)
  {
  	const char *path, *fs_path;
b514f872f   Al Viro   nfs: make nfs_pat...
76
  	path = nfs4_path(dentry, page, PAGE_SIZE);
54ceac451   David Howells   NFS: Share NFS su...
77
78
79
80
81
82
83
84
85
86
  	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
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
87
  			__func__, path, fs_path);
54ceac451   David Howells   NFS: Share NFS su...
88
89
90
91
92
  		return -ENOENT;
  	}
  
  	return 0;
  }
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
93
94
95
96
97
98
99
100
101
102
103
104
105
  static size_t nfs_parse_server_name(char *string, size_t len,
  		struct sockaddr *sa, size_t salen)
  {
  	ssize_t ret;
  
  	ret = rpc_pton(string, len, sa, salen);
  	if (ret == 0) {
  		ret = nfs_dns_resolve_name(string, len, sa, salen);
  		if (ret < 0)
  			ret = 0;
  	}
  	return ret;
  }
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
106
107
108
109
  static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
  				     char *page, char *page2,
  				     const struct nfs4_fs_location *location)
  {
364d015e5   Trond Myklebust   NFSv4: Reduce the...
110
  	const size_t addr_bufsize = sizeof(struct sockaddr_storage);
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
111
112
  	struct vfsmount *mnt = ERR_PTR(-ENOENT);
  	char *mnt_path;
ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
113
  	unsigned int maxbuflen;
460cdbc83   J. Bruce Fields   nfs: replace whil...
114
  	unsigned int s;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
115
116
117
  
  	mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE);
  	if (IS_ERR(mnt_path))
517be09de   Trond Myklebust   NFSv4: Fix the re...
118
  		return ERR_CAST(mnt_path);
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
119
  	mountdata->mnt_path = mnt_path;
ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
120
  	maxbuflen = mnt_path - 1 - page2;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
121

364d015e5   Trond Myklebust   NFSv4: Reduce the...
122
123
124
  	mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL);
  	if (mountdata->addr == NULL)
  		return ERR_PTR(-ENOMEM);
460cdbc83   J. Bruce Fields   nfs: replace whil...
125
  	for (s = 0; s < location->nservers; s++) {
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
126
  		const struct nfs4_string *buf = &location->servers[s];
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
127

ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
128
  		if (buf->len <= 0 || buf->len >= maxbuflen)
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
129
  			continue;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
130

ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
131
132
  		if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
  			continue;
517be09de   Trond Myklebust   NFSv4: Fix the re...
133
134
  
  		mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len,
364d015e5   Trond Myklebust   NFSv4: Reduce the...
135
  				mountdata->addr, addr_bufsize);
53a0b9c4c   Chuck Lever   NFS: Replace nfs_...
136
  		if (mountdata->addrlen == 0)
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
137
  			continue;
517be09de   Trond Myklebust   NFSv4: Fix the re...
138

ec6ee6125   Chuck Lever   NFS: Replace nfs_...
139
  		rpc_set_port(mountdata->addr, NFS_PORT);
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
140

ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
141
142
  		memcpy(page2, buf->data, buf->len);
  		page2[buf->len] = '\0';
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
143
  		mountdata->hostname = page2;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
144
145
146
147
148
149
150
151
  
  		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;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
152
  	}
364d015e5   Trond Myklebust   NFSv4: Reduce the...
153
  	kfree(mountdata->addr);
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
154
155
  	return mnt;
  }
f7b422b17   David Howells   NFS: Split fs/nfs...
156
157
  /**
   * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
f7b422b17   David Howells   NFS: Split fs/nfs...
158
   * @dentry - parent directory
3f43c6667   Chuck Lever   NFS: Address a co...
159
   * @locations - array of NFSv4 server location information
f7b422b17   David Howells   NFS: Split fs/nfs...
160
161
   *
   */
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
162
  static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
509de8111   David Howells   NFS: Add extra co...
163
  					    const struct nfs4_fs_locations *locations)
f7b422b17   David Howells   NFS: Split fs/nfs...
164
165
166
  {
  	struct vfsmount *mnt = ERR_PTR(-ENOENT);
  	struct nfs_clone_mount mountdata = {
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
167
  		.sb = dentry->d_sb,
f7b422b17   David Howells   NFS: Split fs/nfs...
168
  		.dentry = dentry,
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
169
  		.authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor,
f7b422b17   David Howells   NFS: Split fs/nfs...
170
  	};
54ceac451   David Howells   NFS: Share NFS su...
171
  	char *page = NULL, *page2 = NULL;
3f43c6667   Chuck Lever   NFS: Address a co...
172
  	int loc, error;
f7b422b17   David Howells   NFS: Split fs/nfs...
173
174
175
  
  	if (locations == NULL || locations->nlocations <= 0)
  		goto out;
3110ff804   Harvey Harrison   nfs: replace rema...
176
177
  	dprintk("%s: referral at %s/%s
  ", __func__,
f7b422b17   David Howells   NFS: Split fs/nfs...
178
  		dentry->d_parent->d_name.name, dentry->d_name.name);
f7b422b17   David Howells   NFS: Split fs/nfs...
179
  	page = (char *) __get_free_page(GFP_USER);
54ceac451   David Howells   NFS: Share NFS su...
180
  	if (!page)
f7b422b17   David Howells   NFS: Split fs/nfs...
181
  		goto out;
54ceac451   David Howells   NFS: Share NFS su...
182

f7b422b17   David Howells   NFS: Split fs/nfs...
183
  	page2 = (char *) __get_free_page(GFP_USER);
54ceac451   David Howells   NFS: Share NFS su...
184
  	if (!page2)
f7b422b17   David Howells   NFS: Split fs/nfs...
185
  		goto out;
54ceac451   David Howells   NFS: Share NFS su...
186
  	/* Ensure fs path is a prefix of current dentry path */
b514f872f   Al Viro   nfs: make nfs_pat...
187
  	error = nfs4_validate_fspath(dentry, locations, page, page2);
54ceac451   David Howells   NFS: Share NFS su...
188
189
190
  	if (error < 0) {
  		mnt = ERR_PTR(error);
  		goto out;
f7b422b17   David Howells   NFS: Split fs/nfs...
191
  	}
460cdbc83   J. Bruce Fields   nfs: replace whil...
192
  	for (loc = 0; loc < locations->nlocations; loc++) {
509de8111   David Howells   NFS: Add extra co...
193
  		const struct nfs4_fs_location *location = &locations->locations[loc];
f7b422b17   David Howells   NFS: Split fs/nfs...
194
195
  
  		if (location == NULL || location->nservers <= 0 ||
460cdbc83   J. Bruce Fields   nfs: replace whil...
196
  		    location->rootpath.ncomponents == 0)
f7b422b17   David Howells   NFS: Split fs/nfs...
197
  			continue;
f7b422b17   David Howells   NFS: Split fs/nfs...
198

4ada29d5c   J. Bruce Fields   nfs: break up nfs...
199
200
201
  		mnt = try_location(&mountdata, page, page2, location);
  		if (!IS_ERR(mnt))
  			break;
f7b422b17   David Howells   NFS: Split fs/nfs...
202
  	}
f7b422b17   David Howells   NFS: Split fs/nfs...
203
  out:
54ceac451   David Howells   NFS: Share NFS su...
204
205
  	free_page((unsigned long) page);
  	free_page((unsigned long) page2);
3110ff804   Harvey Harrison   nfs: replace rema...
206
207
  	dprintk("%s: done
  ", __func__);
f7b422b17   David Howells   NFS: Split fs/nfs...
208
209
210
211
212
213
  	return mnt;
  }
  
  /*
   * nfs_do_refmount - handle crossing a referral on server
   * @dentry - dentry of referral
f7b422b17   David Howells   NFS: Split fs/nfs...
214
215
   *
   */
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
216
  struct vfsmount *nfs_do_refmount(struct dentry *dentry)
f7b422b17   David Howells   NFS: Split fs/nfs...
217
  {
54ceac451   David Howells   NFS: Share NFS su...
218
  	struct vfsmount *mnt = ERR_PTR(-ENOMEM);
f7b422b17   David Howells   NFS: Split fs/nfs...
219
220
221
222
223
224
  	struct dentry *parent;
  	struct nfs4_fs_locations *fs_locations = NULL;
  	struct page *page;
  	int err;
  
  	/* BUG_ON(IS_ROOT(dentry)); */
3110ff804   Harvey Harrison   nfs: replace rema...
225
226
  	dprintk("%s: enter
  ", __func__);
f7b422b17   David Howells   NFS: Split fs/nfs...
227
228
229
230
231
232
233
234
235
236
  
  	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 */
54ceac451   David Howells   NFS: Share NFS su...
237
  	mnt = ERR_PTR(-ENOENT);
f7b422b17   David Howells   NFS: Split fs/nfs...
238
  	parent = dget_parent(dentry);
54ceac451   David Howells   NFS: Share NFS su...
239
240
  	dprintk("%s: getting locations for %s/%s
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
241
  		__func__, parent->d_name.name, dentry->d_name.name);
54ceac451   David Howells   NFS: Share NFS su...
242

c228fd3ae   Trond Myklebust   NFSv4: Cleanups f...
243
  	err = nfs4_proc_fs_locations(parent->d_inode, &dentry->d_name, fs_locations, page);
f7b422b17   David Howells   NFS: Split fs/nfs...
244
  	dput(parent);
54ceac451   David Howells   NFS: Share NFS su...
245
246
  	if (err != 0 ||
  	    fs_locations->nlocations <= 0 ||
f7b422b17   David Howells   NFS: Split fs/nfs...
247
248
  	    fs_locations->fs_path.ncomponents <= 0)
  		goto out_free;
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
249
  	mnt = nfs_follow_referral(dentry, fs_locations);
f7b422b17   David Howells   NFS: Split fs/nfs...
250
251
252
253
  out_free:
  	__free_page(page);
  	kfree(fs_locations);
  out:
3110ff804   Harvey Harrison   nfs: replace rema...
254
255
  	dprintk("%s: done
  ", __func__);
f7b422b17   David Howells   NFS: Split fs/nfs...
256
257
  	return mnt;
  }