Blame view

fs/nfs/namespace.c 9.32 KB
55a975937   Trond Myklebust   NFS: Ensure the c...
1
2
3
4
  /*
   * linux/fs/nfs/namespace.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>
55a975937   Trond Myklebust   NFS: Ensure the c...
6
7
8
   *
   * NFS namespace
   */
55a975937   Trond Myklebust   NFS: Ensure the c...
9
  #include <linux/dcache.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
10
  #include <linux/gfp.h>
55a975937   Trond Myklebust   NFS: Ensure the c...
11
12
13
14
15
16
  #include <linux/mount.h>
  #include <linux/namei.h>
  #include <linux/nfs_fs.h>
  #include <linux/string.h>
  #include <linux/sunrpc/clnt.h>
  #include <linux/vfs.h>
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
17
  #include <linux/sunrpc/gss_api.h>
f7b422b17   David Howells   NFS: Split fs/nfs...
18
  #include "internal.h"
55a975937   Trond Myklebust   NFS: Ensure the c...
19
20
  
  #define NFSDBG_FACILITY		NFSDBG_VFS
65f27f384   David Howells   WorkStruct: Pass ...
21
  static void nfs_expire_automounts(struct work_struct *work);
f7b422b17   David Howells   NFS: Split fs/nfs...
22

a3dab2935   Adrian Bunk   make nfs_automoun...
23
  static LIST_HEAD(nfs_automount_list);
65f27f384   David Howells   WorkStruct: Pass ...
24
  static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts);
51d8fa6a1   Trond Myklebust   NFS: Add timeout ...
25
  int nfs_mountpoint_expiry_timeout = 500 * HZ;
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
26
  static struct vfsmount *nfs_do_submount(struct dentry *dentry,
66f37509f   Adrian Bunk   [PATCH] fs/nfs/: ...
27
  					struct nfs_fh *fh,
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
28
29
  					struct nfs_fattr *fattr,
  					rpc_authflavor_t authflavor);
66f37509f   Adrian Bunk   [PATCH] fs/nfs/: ...
30

55a975937   Trond Myklebust   NFS: Ensure the c...
31
  /*
f7b422b17   David Howells   NFS: Split fs/nfs...
32
   * nfs_path - reconstruct the path given an arbitrary dentry
b514f872f   Al Viro   nfs: make nfs_pat...
33
   * @base - used to return pointer to the end of devname part of path
f7b422b17   David Howells   NFS: Split fs/nfs...
34
35
36
37
   * @dentry - pointer to dentry
   * @buffer - result buffer
   * @buflen - length of buffer
   *
b514f872f   Al Viro   nfs: make nfs_pat...
38
39
   * Helper function for constructing the server pathname
   * by arbitrary hashed dentry.
f7b422b17   David Howells   NFS: Split fs/nfs...
40
41
   *
   * This is mainly for use in figuring out the path on the
b514f872f   Al Viro   nfs: make nfs_pat...
42
43
   * server side when automounting on top of an existing partition
   * and in generating /proc/mounts and friends.
f7b422b17   David Howells   NFS: Split fs/nfs...
44
   */
b514f872f   Al Viro   nfs: make nfs_pat...
45
  char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen)
f7b422b17   David Howells   NFS: Split fs/nfs...
46
  {
949854d02   Nick Piggin   fs: Use rename lo...
47
  	char *end;
f7b422b17   David Howells   NFS: Split fs/nfs...
48
  	int namelen;
949854d02   Nick Piggin   fs: Use rename lo...
49
  	unsigned seq;
b514f872f   Al Viro   nfs: make nfs_pat...
50
  	const char *base;
f7b422b17   David Howells   NFS: Split fs/nfs...
51

949854d02   Nick Piggin   fs: Use rename lo...
52
53
  rename_retry:
  	end = buffer+buflen;
f7b422b17   David Howells   NFS: Split fs/nfs...
54
55
  	*--end = '\0';
  	buflen--;
949854d02   Nick Piggin   fs: Use rename lo...
56
57
58
  
  	seq = read_seqbegin(&rename_lock);
  	rcu_read_lock();
b514f872f   Al Viro   nfs: make nfs_pat...
59
60
61
62
  	while (1) {
  		spin_lock(&dentry->d_lock);
  		if (IS_ROOT(dentry))
  			break;
f7b422b17   David Howells   NFS: Split fs/nfs...
63
64
65
  		namelen = dentry->d_name.len;
  		buflen -= namelen + 1;
  		if (buflen < 0)
ce5101932   Josh Triplett   NFS: Release dcac...
66
  			goto Elong_unlock;
f7b422b17   David Howells   NFS: Split fs/nfs...
67
68
69
  		end -= namelen;
  		memcpy(end, dentry->d_name.name, namelen);
  		*--end = '/';
b514f872f   Al Viro   nfs: make nfs_pat...
70
  		spin_unlock(&dentry->d_lock);
f7b422b17   David Howells   NFS: Split fs/nfs...
71
72
  		dentry = dentry->d_parent;
  	}
b514f872f   Al Viro   nfs: make nfs_pat...
73
74
75
  	if (read_seqretry(&rename_lock, seq)) {
  		spin_unlock(&dentry->d_lock);
  		rcu_read_unlock();
949854d02   Nick Piggin   fs: Use rename lo...
76
  		goto rename_retry;
b514f872f   Al Viro   nfs: make nfs_pat...
77
  	}
0b75b35c7   Trond Myklebust   NFS: Fix nfs_path...
78
  	if (*end != '/') {
b514f872f   Al Viro   nfs: make nfs_pat...
79
80
81
  		if (--buflen < 0) {
  			spin_unlock(&dentry->d_lock);
  			rcu_read_unlock();
0b75b35c7   Trond Myklebust   NFS: Fix nfs_path...
82
  			goto Elong;
b514f872f   Al Viro   nfs: make nfs_pat...
83
  		}
0b75b35c7   Trond Myklebust   NFS: Fix nfs_path...
84
85
  		*--end = '/';
  	}
b514f872f   Al Viro   nfs: make nfs_pat...
86
87
88
89
90
91
92
93
  	*p = end;
  	base = dentry->d_fsdata;
  	if (!base) {
  		spin_unlock(&dentry->d_lock);
  		rcu_read_unlock();
  		WARN_ON(1);
  		return end;
  	}
f7b422b17   David Howells   NFS: Split fs/nfs...
94
95
96
97
98
  	namelen = strlen(base);
  	/* Strip off excess slashes in base string */
  	while (namelen > 0 && base[namelen - 1] == '/')
  		namelen--;
  	buflen -= namelen;
b514f872f   Al Viro   nfs: make nfs_pat...
99
  	if (buflen < 0) {
1c34092ad   Dan Carpenter   nfs: lock() vs un...
100
  		spin_unlock(&dentry->d_lock);
b514f872f   Al Viro   nfs: make nfs_pat...
101
  		rcu_read_unlock();
f7b422b17   David Howells   NFS: Split fs/nfs...
102
  		goto Elong;
b514f872f   Al Viro   nfs: make nfs_pat...
103
  	}
f7b422b17   David Howells   NFS: Split fs/nfs...
104
105
  	end -= namelen;
  	memcpy(end, base, namelen);
b514f872f   Al Viro   nfs: make nfs_pat...
106
107
  	spin_unlock(&dentry->d_lock);
  	rcu_read_unlock();
f7b422b17   David Howells   NFS: Split fs/nfs...
108
  	return end;
ce5101932   Josh Triplett   NFS: Release dcac...
109
  Elong_unlock:
1c34092ad   Dan Carpenter   nfs: lock() vs un...
110
  	spin_unlock(&dentry->d_lock);
949854d02   Nick Piggin   fs: Use rename lo...
111
112
113
  	rcu_read_unlock();
  	if (read_seqretry(&rename_lock, seq))
  		goto rename_retry;
f7b422b17   David Howells   NFS: Split fs/nfs...
114
115
116
  Elong:
  	return ERR_PTR(-ENAMETOOLONG);
  }
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
117
  #ifdef CONFIG_NFS_V4
fca78d6d2   Bryan Schumaker   NFS: Add SECINFO_...
118
  rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  {
  	struct gss_api_mech *mech;
  	struct xdr_netobj oid;
  	int i;
  	rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
  
  	for (i = 0; i < flavors->num_flavors; i++) {
  		struct nfs4_secinfo_flavor *flavor;
  		flavor = &flavors->flavors[i];
  
  		if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
  			pseudoflavor = flavor->flavor;
  			break;
  		} else if (flavor->flavor == RPC_AUTH_GSS) {
  			oid.len  = flavor->gss.sec_oid4.len;
  			oid.data = flavor->gss.sec_oid4.data;
  			mech = gss_mech_get_by_OID(&oid);
  			if (!mech)
  				continue;
  			pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service);
  			gss_mech_put(mech);
  			break;
  		}
  	}
  
  	return pseudoflavor;
  }
418875900   Bryan Schumaker   NFS: Fix a signed...
146
147
148
  static int nfs_negotiate_security(const struct dentry *parent,
  				  const struct dentry *dentry,
  				  rpc_authflavor_t *flavor)
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
149
  {
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
150
151
152
  	struct page *page;
  	struct nfs4_secinfo_flavors *flavors;
  	int (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
418875900   Bryan Schumaker   NFS: Fix a signed...
153
  	int ret = -EPERM;
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
154
155
156
157
158
  
  	secinfo = NFS_PROTO(parent->d_inode)->secinfo;
  	if (secinfo != NULL) {
  		page = alloc_page(GFP_KERNEL);
  		if (!page) {
418875900   Bryan Schumaker   NFS: Fix a signed...
159
  			ret = -ENOMEM;
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
160
161
162
  			goto out;
  		}
  		flavors = page_address(page);
418875900   Bryan Schumaker   NFS: Fix a signed...
163
  		ret = secinfo(parent->d_inode, &dentry->d_name, flavors);
561f0b0ad   Bryan Schumaker   NFS: Remove unuse...
164
  		*flavor = nfs_find_best_sec(flavors);
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
165
166
  		put_page(page);
  	}
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
167
  out:
418875900   Bryan Schumaker   NFS: Fix a signed...
168
  	return ret;
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
169
  }
418875900   Bryan Schumaker   NFS: Fix a signed...
170
171
172
173
  static int nfs_lookup_with_sec(struct nfs_server *server, struct dentry *parent,
  			       struct dentry *dentry, struct path *path,
  			       struct nfs_fh *fh, struct nfs_fattr *fattr,
  			       rpc_authflavor_t *flavor)
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
174
  {
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
175
176
177
  	struct rpc_clnt *clone;
  	struct rpc_auth *auth;
  	int err;
418875900   Bryan Schumaker   NFS: Fix a signed...
178
179
  	err = nfs_negotiate_security(parent, path->dentry, flavor);
  	if (err < 0)
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
180
181
  		goto out;
  	clone  = rpc_clone_client(server->client);
418875900   Bryan Schumaker   NFS: Fix a signed...
182
  	auth   = rpcauth_create(*flavor, clone);
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
183
  	if (!auth) {
418875900   Bryan Schumaker   NFS: Fix a signed...
184
  		err = -EIO;
a0e7e3cf7   Trond Myklebust   NFS: Don't leak R...
185
  		goto out_shutdown;
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
186
187
188
189
  	}
  	err = server->nfs_client->rpc_ops->lookup(clone, parent->d_inode,
  						  &path->dentry->d_name,
  						  fh, fattr);
a0e7e3cf7   Trond Myklebust   NFS: Don't leak R...
190
191
  out_shutdown:
  	rpc_shutdown_client(clone);
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
192
  out:
418875900   Bryan Schumaker   NFS: Fix a signed...
193
  	return err;
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
194
195
  }
  #else /* CONFIG_NFS_V4 */
418875900   Bryan Schumaker   NFS: Fix a signed...
196
197
198
199
200
  static inline int nfs_lookup_with_sec(struct nfs_server *server,
  				      struct dentry *parent, struct dentry *dentry,
  				      struct path *path, struct nfs_fh *fh,
  				      struct nfs_fattr *fattr,
  				      rpc_authflavor_t *flavor)
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
201
202
203
204
  {
  	return -EPERM;
  }
  #endif /* CONFIG_NFS_V4 */
f7b422b17   David Howells   NFS: Split fs/nfs...
205
  /*
36d43a437   David Howells   NFS: Use d_automo...
206
207
   * nfs_d_automount - Handle crossing a mountpoint on the server
   * @path - The mountpoint
55a975937   Trond Myklebust   NFS: Ensure the c...
208
209
210
211
212
213
214
215
216
   *
   * When we encounter a mountpoint on the server, we want to set up
   * a mountpoint on the client too, to prevent inode numbers from
   * colliding, and to allow "df" to work properly.
   * On NFSv4, we also want to allow for the fact that different
   * filesystems may be migrated to different servers in a failover
   * situation, and that different filesystems may want to use
   * different security flavours.
   */
36d43a437   David Howells   NFS: Use d_automo...
217
  struct vfsmount *nfs_d_automount(struct path *path)
55a975937   Trond Myklebust   NFS: Ensure the c...
218
219
  {
  	struct vfsmount *mnt;
36d43a437   David Howells   NFS: Use d_automo...
220
  	struct nfs_server *server = NFS_SERVER(path->dentry->d_inode);
55a975937   Trond Myklebust   NFS: Ensure the c...
221
  	struct dentry *parent;
a4d7f1680   Trond Myklebust   NFS: Reduce the s...
222
223
  	struct nfs_fh *fh = NULL;
  	struct nfs_fattr *fattr = NULL;
55a975937   Trond Myklebust   NFS: Ensure the c...
224
  	int err;
418875900   Bryan Schumaker   NFS: Fix a signed...
225
  	rpc_authflavor_t flavor = RPC_AUTH_UNIX;
55a975937   Trond Myklebust   NFS: Ensure the c...
226

36d43a437   David Howells   NFS: Use d_automo...
227
228
  	dprintk("--> nfs_d_automount()
  ");
54ceac451   David Howells   NFS: Share NFS su...
229

36d43a437   David Howells   NFS: Use d_automo...
230
231
232
  	mnt = ERR_PTR(-ESTALE);
  	if (IS_ROOT(path->dentry))
  		goto out_nofree;
44d5759d3   Denis V. Lunev   nfs: BUG_ON in nf...
233

36d43a437   David Howells   NFS: Use d_automo...
234
  	mnt = ERR_PTR(-ENOMEM);
a4d7f1680   Trond Myklebust   NFS: Reduce the s...
235
236
237
  	fh = nfs_alloc_fhandle();
  	fattr = nfs_alloc_fattr();
  	if (fh == NULL || fattr == NULL)
36d43a437   David Howells   NFS: Use d_automo...
238
  		goto out;
a4d7f1680   Trond Myklebust   NFS: Reduce the s...
239

3110ff804   Harvey Harrison   nfs: replace rema...
240
241
  	dprintk("%s: enter
  ", __func__);
54ceac451   David Howells   NFS: Share NFS su...
242

36d43a437   David Howells   NFS: Use d_automo...
243
244
  	/* Look it up again to get its attributes */
  	parent = dget_parent(path->dentry);
7c5130588   Bryan Schumaker   NFS: lookup suppo...
245
  	err = server->nfs_client->rpc_ops->lookup(server->client, parent->d_inode,
36d43a437   David Howells   NFS: Use d_automo...
246
  						  &path->dentry->d_name,
a4d7f1680   Trond Myklebust   NFS: Reduce the s...
247
  						  fh, fattr);
418875900   Bryan Schumaker   NFS: Fix a signed...
248
249
  	if (err == -EPERM && NFS_PROTO(parent->d_inode)->secinfo != NULL)
  		err = nfs_lookup_with_sec(server, parent, path->dentry, path, fh, fattr, &flavor);
55a975937   Trond Myklebust   NFS: Ensure the c...
250
  	dput(parent);
36d43a437   David Howells   NFS: Use d_automo...
251
252
253
254
  	if (err != 0) {
  		mnt = ERR_PTR(err);
  		goto out;
  	}
55a975937   Trond Myklebust   NFS: Ensure the c...
255

a4d7f1680   Trond Myklebust   NFS: Reduce the s...
256
  	if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
257
  		mnt = nfs_do_refmount(path->dentry);
6b97fd3da   Manoj Naik   NFSv4: Follow a r...
258
  	else
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
259
  		mnt = nfs_do_submount(path->dentry, fh, fattr, flavor);
55a975937   Trond Myklebust   NFS: Ensure the c...
260
  	if (IS_ERR(mnt))
36d43a437   David Howells   NFS: Use d_automo...
261
  		goto out;
55a975937   Trond Myklebust   NFS: Ensure the c...
262

ea5b778a8   David Howells   Unexport do_add_m...
263
264
265
266
267
  	dprintk("%s: done, success
  ", __func__);
  	mntget(mnt); /* prevent immediate expiration */
  	mnt_set_expiry(mnt, &nfs_automount_list);
  	schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
36d43a437   David Howells   NFS: Use d_automo...
268

55a975937   Trond Myklebust   NFS: Ensure the c...
269
  out:
a4d7f1680   Trond Myklebust   NFS: Reduce the s...
270
271
  	nfs_free_fattr(fattr);
  	nfs_free_fhandle(fh);
36d43a437   David Howells   NFS: Use d_automo...
272
273
274
275
  out_nofree:
  	dprintk("<-- nfs_follow_mountpoint() = %p
  ", mnt);
  	return mnt;
55a975937   Trond Myklebust   NFS: Ensure the c...
276
  }
92e1d5be9   Arjan van de Ven   [PATCH] mark stru...
277
  const struct inode_operations nfs_mountpoint_inode_operations = {
55a975937   Trond Myklebust   NFS: Ensure the c...
278
279
  	.getattr	= nfs_getattr,
  };
51d8fa6a1   Trond Myklebust   NFS: Add timeout ...
280

92e1d5be9   Arjan van de Ven   [PATCH] mark stru...
281
  const struct inode_operations nfs_referral_inode_operations = {
6b97fd3da   Manoj Naik   NFSv4: Follow a r...
282
  };
65f27f384   David Howells   WorkStruct: Pass ...
283
  static void nfs_expire_automounts(struct work_struct *work)
51d8fa6a1   Trond Myklebust   NFS: Add timeout ...
284
  {
65f27f384   David Howells   WorkStruct: Pass ...
285
  	struct list_head *list = &nfs_automount_list;
51d8fa6a1   Trond Myklebust   NFS: Add timeout ...
286
287
288
289
290
291
292
293
  
  	mark_mounts_for_expiry(list);
  	if (!list_empty(list))
  		schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
  }
  
  void nfs_release_automount_timer(void)
  {
3d39c691f   Trond Myklebust   NFS: Replace flus...
294
  	if (list_empty(&nfs_automount_list))
560aef745   Trond Myklebust   NFS: Fix use of c...
295
  		cancel_delayed_work(&nfs_automount_task);
51d8fa6a1   Trond Myklebust   NFS: Add timeout ...
296
  }
f7b422b17   David Howells   NFS: Split fs/nfs...
297
298
299
300
  
  /*
   * Clone a mountpoint of the appropriate type
   */
509de8111   David Howells   NFS: Add extra co...
301
302
  static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
  					   const char *devname,
f7b422b17   David Howells   NFS: Split fs/nfs...
303
304
305
  					   struct nfs_clone_mount *mountdata)
  {
  #ifdef CONFIG_NFS_V4
fd08d7e9d   Denis V. Lunev   nfs: ERR_PTR is e...
306
  	struct vfsmount *mnt = ERR_PTR(-EINVAL);
40c553193   Trond Myklebust   NFS: Remove the r...
307
  	switch (server->nfs_client->rpc_ops->version) {
f7b422b17   David Howells   NFS: Split fs/nfs...
308
309
  		case 2:
  		case 3:
54ceac451   David Howells   NFS: Share NFS su...
310
  			mnt = vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata);
f7b422b17   David Howells   NFS: Split fs/nfs...
311
312
  			break;
  		case 4:
54ceac451   David Howells   NFS: Share NFS su...
313
  			mnt = vfs_kern_mount(&nfs4_xdev_fs_type, 0, devname, mountdata);
f7b422b17   David Howells   NFS: Split fs/nfs...
314
315
316
  	}
  	return mnt;
  #else
54ceac451   David Howells   NFS: Share NFS su...
317
  	return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata);
f7b422b17   David Howells   NFS: Split fs/nfs...
318
319
320
321
322
  #endif
  }
  
  /**
   * nfs_do_submount - set up mountpoint when crossing a filesystem boundary
f7b422b17   David Howells   NFS: Split fs/nfs...
323
324
325
   * @dentry - parent directory
   * @fh - filehandle for new root dentry
   * @fattr - attributes for new root inode
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
326
   * @authflavor - security flavor to use when performing the mount
f7b422b17   David Howells   NFS: Split fs/nfs...
327
328
   *
   */
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
329
  static struct vfsmount *nfs_do_submount(struct dentry *dentry,
66f37509f   Adrian Bunk   [PATCH] fs/nfs/: ...
330
  					struct nfs_fh *fh,
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
331
332
  					struct nfs_fattr *fattr,
  					rpc_authflavor_t authflavor)
f7b422b17   David Howells   NFS: Split fs/nfs...
333
334
  {
  	struct nfs_clone_mount mountdata = {
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
335
  		.sb = dentry->d_sb,
f7b422b17   David Howells   NFS: Split fs/nfs...
336
337
338
  		.dentry = dentry,
  		.fh = fh,
  		.fattr = fattr,
7ebb93159   Bryan Schumaker   NFS: use secinfo ...
339
  		.authflavor = authflavor,
f7b422b17   David Howells   NFS: Split fs/nfs...
340
341
342
343
  	};
  	struct vfsmount *mnt = ERR_PTR(-ENOMEM);
  	char *page = (char *) __get_free_page(GFP_USER);
  	char *devname;
54ceac451   David Howells   NFS: Share NFS su...
344
345
  	dprintk("--> nfs_do_submount()
  ");
3110ff804   Harvey Harrison   nfs: replace rema...
346
347
  	dprintk("%s: submounting on %s/%s
  ", __func__,
f7b422b17   David Howells   NFS: Split fs/nfs...
348
349
350
351
  			dentry->d_parent->d_name.name,
  			dentry->d_name.name);
  	if (page == NULL)
  		goto out;
b514f872f   Al Viro   nfs: make nfs_pat...
352
  	devname = nfs_devname(dentry, page, PAGE_SIZE);
f7b422b17   David Howells   NFS: Split fs/nfs...
353
354
355
  	mnt = (struct vfsmount *)devname;
  	if (IS_ERR(devname))
  		goto free_page;
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
356
  	mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata);
f7b422b17   David Howells   NFS: Split fs/nfs...
357
358
359
  free_page:
  	free_page((unsigned long)page);
  out:
3110ff804   Harvey Harrison   nfs: replace rema...
360
361
  	dprintk("%s: done
  ", __func__);
54ceac451   David Howells   NFS: Share NFS su...
362
363
364
  
  	dprintk("<-- nfs_do_submount() = %p
  ", mnt);
f7b422b17   David Howells   NFS: Split fs/nfs...
365
366
  	return mnt;
  }