blob: 28108c82b88742d6e325a0736f820bf8e044910e [file] [log] [blame]
David Howellsf7b422b2006-06-09 09:34:33 -04001/*
2 * linux/fs/nfs/super.c
3 *
4 * Copyright (C) 1992 Rick Sladkey
5 *
6 * nfs superblock handling functions
7 *
8 * Modularised by Alan Cox <Alan.Cox@linux.org>, while hacking some
9 * experimental NFS changes. Modularisation taken straight from SYS5 fs.
10 *
11 * Change to nfs_read_super() to permit NFS mounts to multi-homed hosts.
12 * J.S.Peatfield@damtp.cam.ac.uk
13 *
14 * Split from inode.c by David Howells <dhowells@redhat.com>
15 *
David Howells54ceac42006-08-22 20:06:13 -040016 * - superblocks are indexed on server only - all inodes, dentries, etc. associated with a
17 * particular server are held in the same superblock
18 * - NFS superblocks can have several effective roots to the dentry tree
19 * - directory type roots are spliced into the tree when a path from one root reaches the root
20 * of another (see nfs_lookup())
David Howellsf7b422b2006-06-09 09:34:33 -040021 */
22
David Howellsf7b422b2006-06-09 09:34:33 -040023#include <linux/module.h>
24#include <linux/init.h>
25
26#include <linux/time.h>
27#include <linux/kernel.h>
28#include <linux/mm.h>
29#include <linux/string.h>
30#include <linux/stat.h>
31#include <linux/errno.h>
32#include <linux/unistd.h>
33#include <linux/sunrpc/clnt.h>
34#include <linux/sunrpc/stats.h>
35#include <linux/sunrpc/metrics.h>
36#include <linux/nfs_fs.h>
37#include <linux/nfs_mount.h>
38#include <linux/nfs4_mount.h>
39#include <linux/lockd/bind.h>
40#include <linux/smp_lock.h>
41#include <linux/seq_file.h>
42#include <linux/mount.h>
43#include <linux/nfs_idmap.h>
44#include <linux/vfs.h>
45#include <linux/inet.h>
46#include <linux/nfs_xdr.h>
47
48#include <asm/system.h>
49#include <asm/uaccess.h>
50
51#include "nfs4_fs.h"
52#include "callback.h"
53#include "delegation.h"
54#include "iostat.h"
55#include "internal.h"
56
57#define NFSDBG_FACILITY NFSDBG_VFS
58
David Howellsf7b422b2006-06-09 09:34:33 -040059static void nfs_umount_begin(struct vfsmount *, int);
Trond Myklebust816724e2006-06-24 08:41:41 -040060static int nfs_statfs(struct dentry *, struct kstatfs *);
David Howellsf7b422b2006-06-09 09:34:33 -040061static int nfs_show_options(struct seq_file *, struct vfsmount *);
62static int nfs_show_stats(struct seq_file *, struct vfsmount *);
Trond Myklebust816724e2006-06-24 08:41:41 -040063static int nfs_get_sb(struct file_system_type *, int, const char *, void *, struct vfsmount *);
David Howells54ceac42006-08-22 20:06:13 -040064static int nfs_xdev_get_sb(struct file_system_type *fs_type,
Trond Myklebust816724e2006-06-24 08:41:41 -040065 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
David Howellsf7b422b2006-06-09 09:34:33 -040066static void nfs_kill_super(struct super_block *);
67
68static struct file_system_type nfs_fs_type = {
69 .owner = THIS_MODULE,
70 .name = "nfs",
71 .get_sb = nfs_get_sb,
72 .kill_sb = nfs_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -070073 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -040074};
75
David Howells54ceac42006-08-22 20:06:13 -040076struct file_system_type nfs_xdev_fs_type = {
David Howellsf7b422b2006-06-09 09:34:33 -040077 .owner = THIS_MODULE,
78 .name = "nfs",
David Howells54ceac42006-08-22 20:06:13 -040079 .get_sb = nfs_xdev_get_sb,
David Howellsf7b422b2006-06-09 09:34:33 -040080 .kill_sb = nfs_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -070081 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -040082};
83
84static struct super_operations nfs_sops = {
85 .alloc_inode = nfs_alloc_inode,
86 .destroy_inode = nfs_destroy_inode,
87 .write_inode = nfs_write_inode,
88 .statfs = nfs_statfs,
89 .clear_inode = nfs_clear_inode,
90 .umount_begin = nfs_umount_begin,
91 .show_options = nfs_show_options,
92 .show_stats = nfs_show_stats,
93};
94
95#ifdef CONFIG_NFS_V4
Trond Myklebust816724e2006-06-24 08:41:41 -040096static int nfs4_get_sb(struct file_system_type *fs_type,
97 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
David Howells54ceac42006-08-22 20:06:13 -040098static int nfs4_xdev_get_sb(struct file_system_type *fs_type,
99 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
100static int nfs4_referral_get_sb(struct file_system_type *fs_type,
101 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
David Howellsf7b422b2006-06-09 09:34:33 -0400102static void nfs4_kill_super(struct super_block *sb);
103
104static struct file_system_type nfs4_fs_type = {
105 .owner = THIS_MODULE,
106 .name = "nfs4",
107 .get_sb = nfs4_get_sb,
108 .kill_sb = nfs4_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -0700109 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -0400110};
111
David Howells54ceac42006-08-22 20:06:13 -0400112struct file_system_type nfs4_xdev_fs_type = {
David Howellsf7b422b2006-06-09 09:34:33 -0400113 .owner = THIS_MODULE,
114 .name = "nfs4",
David Howells54ceac42006-08-22 20:06:13 -0400115 .get_sb = nfs4_xdev_get_sb,
David Howellsf7b422b2006-06-09 09:34:33 -0400116 .kill_sb = nfs4_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -0700117 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -0400118};
119
David Howells54ceac42006-08-22 20:06:13 -0400120struct file_system_type nfs4_referral_fs_type = {
David Howellsf7b422b2006-06-09 09:34:33 -0400121 .owner = THIS_MODULE,
122 .name = "nfs4",
David Howells54ceac42006-08-22 20:06:13 -0400123 .get_sb = nfs4_referral_get_sb,
David Howellsf7b422b2006-06-09 09:34:33 -0400124 .kill_sb = nfs4_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -0700125 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -0400126};
127
128static struct super_operations nfs4_sops = {
129 .alloc_inode = nfs_alloc_inode,
130 .destroy_inode = nfs_destroy_inode,
131 .write_inode = nfs_write_inode,
132 .statfs = nfs_statfs,
133 .clear_inode = nfs4_clear_inode,
134 .umount_begin = nfs_umount_begin,
135 .show_options = nfs_show_options,
136 .show_stats = nfs_show_stats,
137};
138#endif
139
Trond Myklebust979df722006-07-25 11:28:19 -0400140static struct shrinker *acl_shrinker;
141
David Howellsf7b422b2006-06-09 09:34:33 -0400142/*
143 * Register the NFS filesystems
144 */
145int __init register_nfs_fs(void)
146{
147 int ret;
148
149 ret = register_filesystem(&nfs_fs_type);
150 if (ret < 0)
151 goto error_0;
152
153#ifdef CONFIG_NFS_V4
154 ret = nfs_register_sysctl();
155 if (ret < 0)
156 goto error_1;
157 ret = register_filesystem(&nfs4_fs_type);
158 if (ret < 0)
159 goto error_2;
160#endif
Trond Myklebust979df722006-07-25 11:28:19 -0400161 acl_shrinker = set_shrinker(DEFAULT_SEEKS, nfs_access_cache_shrinker);
David Howellsf7b422b2006-06-09 09:34:33 -0400162 return 0;
163
164#ifdef CONFIG_NFS_V4
165error_2:
166 nfs_unregister_sysctl();
167error_1:
168 unregister_filesystem(&nfs_fs_type);
169#endif
170error_0:
171 return ret;
172}
173
174/*
175 * Unregister the NFS filesystems
176 */
177void __exit unregister_nfs_fs(void)
178{
Trond Myklebust979df722006-07-25 11:28:19 -0400179 if (acl_shrinker != NULL)
180 remove_shrinker(acl_shrinker);
David Howellsf7b422b2006-06-09 09:34:33 -0400181#ifdef CONFIG_NFS_V4
182 unregister_filesystem(&nfs4_fs_type);
183 nfs_unregister_sysctl();
184#endif
185 unregister_filesystem(&nfs_fs_type);
186}
187
188/*
189 * Deliver file system statistics to userspace
190 */
Trond Myklebust816724e2006-06-24 08:41:41 -0400191static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
David Howellsf7b422b2006-06-09 09:34:33 -0400192{
David Howells0c7d90c2006-08-22 20:06:10 -0400193 struct nfs_server *server = NFS_SB(dentry->d_sb);
David Howellsf7b422b2006-06-09 09:34:33 -0400194 unsigned char blockbits;
195 unsigned long blockres;
David Howells0c7d90c2006-08-22 20:06:10 -0400196 struct nfs_fh *fh = NFS_FH(dentry->d_inode);
David Howellsf7b422b2006-06-09 09:34:33 -0400197 struct nfs_fattr fattr;
198 struct nfs_fsstat res = {
199 .fattr = &fattr,
200 };
201 int error;
202
203 lock_kernel();
204
David Howells8fa5c002006-08-22 20:06:12 -0400205 error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
David Howellsf7b422b2006-06-09 09:34:33 -0400206 buf->f_type = NFS_SUPER_MAGIC;
207 if (error < 0)
208 goto out_err;
209
210 /*
211 * Current versions of glibc do not correctly handle the
212 * case where f_frsize != f_bsize. Eventually we want to
213 * report the value of wtmult in this field.
214 */
David Howells0c7d90c2006-08-22 20:06:10 -0400215 buf->f_frsize = dentry->d_sb->s_blocksize;
David Howellsf7b422b2006-06-09 09:34:33 -0400216
217 /*
218 * On most *nix systems, f_blocks, f_bfree, and f_bavail
219 * are reported in units of f_frsize. Linux hasn't had
220 * an f_frsize field in its statfs struct until recently,
221 * thus historically Linux's sys_statfs reports these
222 * fields in units of f_bsize.
223 */
David Howells0c7d90c2006-08-22 20:06:10 -0400224 buf->f_bsize = dentry->d_sb->s_blocksize;
225 blockbits = dentry->d_sb->s_blocksize_bits;
David Howellsf7b422b2006-06-09 09:34:33 -0400226 blockres = (1 << blockbits) - 1;
227 buf->f_blocks = (res.tbytes + blockres) >> blockbits;
228 buf->f_bfree = (res.fbytes + blockres) >> blockbits;
229 buf->f_bavail = (res.abytes + blockres) >> blockbits;
230
231 buf->f_files = res.tfiles;
232 buf->f_ffree = res.afiles;
233
234 buf->f_namelen = server->namelen;
235 out:
236 unlock_kernel();
237 return 0;
238
239 out_err:
240 dprintk("%s: statfs error = %d\n", __FUNCTION__, -error);
241 buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
242 goto out;
243
244}
245
David Howells7d4e2742006-08-22 20:06:07 -0400246/*
247 * Map the security flavour number to a name
248 */
Trond Myklebust81039f12006-06-09 09:34:34 -0400249static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour)
250{
David Howells7d4e2742006-08-22 20:06:07 -0400251 static const struct {
Trond Myklebust81039f12006-06-09 09:34:34 -0400252 rpc_authflavor_t flavour;
253 const char *str;
254 } sec_flavours[] = {
255 { RPC_AUTH_NULL, "null" },
256 { RPC_AUTH_UNIX, "sys" },
257 { RPC_AUTH_GSS_KRB5, "krb5" },
258 { RPC_AUTH_GSS_KRB5I, "krb5i" },
259 { RPC_AUTH_GSS_KRB5P, "krb5p" },
260 { RPC_AUTH_GSS_LKEY, "lkey" },
261 { RPC_AUTH_GSS_LKEYI, "lkeyi" },
262 { RPC_AUTH_GSS_LKEYP, "lkeyp" },
263 { RPC_AUTH_GSS_SPKM, "spkm" },
264 { RPC_AUTH_GSS_SPKMI, "spkmi" },
265 { RPC_AUTH_GSS_SPKMP, "spkmp" },
266 { -1, "unknown" }
267 };
268 int i;
269
270 for (i=0; sec_flavours[i].flavour != -1; i++) {
271 if (sec_flavours[i].flavour == flavour)
272 break;
273 }
274 return sec_flavours[i].str;
275}
276
David Howellsf7b422b2006-06-09 09:34:33 -0400277/*
278 * Describe the mount options in force on this server representation
279 */
280static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults)
281{
David Howells509de812006-08-22 20:06:11 -0400282 static const struct proc_nfs_info {
David Howellsf7b422b2006-06-09 09:34:33 -0400283 int flag;
David Howells509de812006-08-22 20:06:11 -0400284 const char *str;
285 const char *nostr;
David Howellsf7b422b2006-06-09 09:34:33 -0400286 } nfs_info[] = {
287 { NFS_MOUNT_SOFT, ",soft", ",hard" },
288 { NFS_MOUNT_INTR, ",intr", "" },
289 { NFS_MOUNT_NOCTO, ",nocto", "" },
290 { NFS_MOUNT_NOAC, ",noac", "" },
291 { NFS_MOUNT_NONLM, ",nolock", "" },
292 { NFS_MOUNT_NOACL, ",noacl", "" },
293 { 0, NULL, NULL }
294 };
David Howells509de812006-08-22 20:06:11 -0400295 const struct proc_nfs_info *nfs_infop;
David Howells8fa5c002006-08-22 20:06:12 -0400296 struct nfs_client *clp = nfss->nfs_client;
David Howellsf7b422b2006-06-09 09:34:33 -0400297 char buf[12];
David Howells509de812006-08-22 20:06:11 -0400298 const char *proto;
David Howellsf7b422b2006-06-09 09:34:33 -0400299
David Howells8fa5c002006-08-22 20:06:12 -0400300 seq_printf(m, ",vers=%d", clp->rpc_ops->version);
David Howellsf7b422b2006-06-09 09:34:33 -0400301 seq_printf(m, ",rsize=%d", nfss->rsize);
302 seq_printf(m, ",wsize=%d", nfss->wsize);
303 if (nfss->acregmin != 3*HZ || showdefaults)
304 seq_printf(m, ",acregmin=%d", nfss->acregmin/HZ);
305 if (nfss->acregmax != 60*HZ || showdefaults)
306 seq_printf(m, ",acregmax=%d", nfss->acregmax/HZ);
307 if (nfss->acdirmin != 30*HZ || showdefaults)
308 seq_printf(m, ",acdirmin=%d", nfss->acdirmin/HZ);
309 if (nfss->acdirmax != 60*HZ || showdefaults)
310 seq_printf(m, ",acdirmax=%d", nfss->acdirmax/HZ);
311 for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) {
312 if (nfss->flags & nfs_infop->flag)
313 seq_puts(m, nfs_infop->str);
314 else
315 seq_puts(m, nfs_infop->nostr);
316 }
317 switch (nfss->client->cl_xprt->prot) {
318 case IPPROTO_TCP:
319 proto = "tcp";
320 break;
321 case IPPROTO_UDP:
322 proto = "udp";
323 break;
324 default:
325 snprintf(buf, sizeof(buf), "%u", nfss->client->cl_xprt->prot);
326 proto = buf;
327 }
328 seq_printf(m, ",proto=%s", proto);
David Howells5006a762006-08-22 20:06:12 -0400329 seq_printf(m, ",timeo=%lu", 10U * clp->retrans_timeo / HZ);
330 seq_printf(m, ",retrans=%u", clp->retrans_count);
Trond Myklebust81039f12006-06-09 09:34:34 -0400331 seq_printf(m, ",sec=%s", nfs_pseudoflavour_to_name(nfss->client->cl_auth->au_flavor));
David Howellsf7b422b2006-06-09 09:34:33 -0400332}
333
334/*
335 * Describe the mount options on this VFS mountpoint
336 */
337static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
338{
339 struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
340
341 nfs_show_mount_options(m, nfss, 0);
342
343 seq_puts(m, ",addr=");
David Howells54ceac42006-08-22 20:06:13 -0400344 seq_escape(m, nfss->nfs_client->cl_hostname, " \t\n\\");
David Howellsf7b422b2006-06-09 09:34:33 -0400345
346 return 0;
347}
348
349/*
350 * Present statistical information for this VFS mountpoint
351 */
352static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
353{
354 int i, cpu;
355 struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
356 struct rpc_auth *auth = nfss->client->cl_auth;
357 struct nfs_iostats totals = { };
358
359 seq_printf(m, "statvers=%s", NFS_IOSTAT_VERS);
360
361 /*
362 * Display all mount option settings
363 */
364 seq_printf(m, "\n\topts:\t");
365 seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw");
366 seq_puts(m, mnt->mnt_sb->s_flags & MS_SYNCHRONOUS ? ",sync" : "");
367 seq_puts(m, mnt->mnt_sb->s_flags & MS_NOATIME ? ",noatime" : "");
368 seq_puts(m, mnt->mnt_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : "");
369 nfs_show_mount_options(m, nfss, 1);
370
371 seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ);
372
373 seq_printf(m, "\n\tcaps:\t");
374 seq_printf(m, "caps=0x%x", nfss->caps);
375 seq_printf(m, ",wtmult=%d", nfss->wtmult);
376 seq_printf(m, ",dtsize=%d", nfss->dtsize);
377 seq_printf(m, ",bsize=%d", nfss->bsize);
378 seq_printf(m, ",namelen=%d", nfss->namelen);
379
380#ifdef CONFIG_NFS_V4
David Howells8fa5c002006-08-22 20:06:12 -0400381 if (nfss->nfs_client->cl_nfsversion == 4) {
David Howellsf7b422b2006-06-09 09:34:33 -0400382 seq_printf(m, "\n\tnfsv4:\t");
383 seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
384 seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
385 seq_printf(m, ",acl=0x%x", nfss->acl_bitmask);
386 }
387#endif
388
389 /*
390 * Display security flavor in effect for this mount
391 */
392 seq_printf(m, "\n\tsec:\tflavor=%d", auth->au_ops->au_flavor);
393 if (auth->au_flavor)
394 seq_printf(m, ",pseudoflavor=%d", auth->au_flavor);
395
396 /*
397 * Display superblock I/O counters
398 */
399 for_each_possible_cpu(cpu) {
400 struct nfs_iostats *stats;
401
402 preempt_disable();
403 stats = per_cpu_ptr(nfss->io_stats, cpu);
404
405 for (i = 0; i < __NFSIOS_COUNTSMAX; i++)
406 totals.events[i] += stats->events[i];
407 for (i = 0; i < __NFSIOS_BYTESMAX; i++)
408 totals.bytes[i] += stats->bytes[i];
409
410 preempt_enable();
411 }
412
413 seq_printf(m, "\n\tevents:\t");
414 for (i = 0; i < __NFSIOS_COUNTSMAX; i++)
415 seq_printf(m, "%lu ", totals.events[i]);
416 seq_printf(m, "\n\tbytes:\t");
417 for (i = 0; i < __NFSIOS_BYTESMAX; i++)
418 seq_printf(m, "%Lu ", totals.bytes[i]);
419 seq_printf(m, "\n");
420
421 rpc_print_iostats(m, nfss->client);
422
423 return 0;
424}
425
426/*
427 * Begin unmount by attempting to remove all automounted mountpoints we added
David Howells54ceac42006-08-22 20:06:13 -0400428 * in response to xdev traversals and referrals
David Howellsf7b422b2006-06-09 09:34:33 -0400429 */
430static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags)
431{
David Howellsf7b422b2006-06-09 09:34:33 -0400432 shrink_submounts(vfsmnt, &nfs_automount_list);
David Howellsf7b422b2006-06-09 09:34:33 -0400433}
434
435/*
David Howells54ceac42006-08-22 20:06:13 -0400436 * Validate the NFS2/NFS3 mount data
437 * - fills in the mount root filehandle
David Howellsf7b422b2006-06-09 09:34:33 -0400438 */
David Howells54ceac42006-08-22 20:06:13 -0400439static int nfs_validate_mount_data(struct nfs_mount_data *data,
440 struct nfs_fh *mntfh)
David Howellsf7b422b2006-06-09 09:34:33 -0400441{
David Howellsf7b422b2006-06-09 09:34:33 -0400442 if (data == NULL) {
443 dprintk("%s: missing data argument\n", __FUNCTION__);
David Howells54ceac42006-08-22 20:06:13 -0400444 return -EINVAL;
David Howellsf7b422b2006-06-09 09:34:33 -0400445 }
David Howells54ceac42006-08-22 20:06:13 -0400446
David Howellsf7b422b2006-06-09 09:34:33 -0400447 if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) {
448 dprintk("%s: bad mount version\n", __FUNCTION__);
David Howells54ceac42006-08-22 20:06:13 -0400449 return -EINVAL;
David Howellsf7b422b2006-06-09 09:34:33 -0400450 }
David Howells54ceac42006-08-22 20:06:13 -0400451
David Howellsf7b422b2006-06-09 09:34:33 -0400452 switch (data->version) {
453 case 1:
454 data->namlen = 0;
455 case 2:
456 data->bsize = 0;
457 case 3:
458 if (data->flags & NFS_MOUNT_VER3) {
459 dprintk("%s: mount structure version %d does not support NFSv3\n",
460 __FUNCTION__,
461 data->version);
David Howells54ceac42006-08-22 20:06:13 -0400462 return -EINVAL;
David Howellsf7b422b2006-06-09 09:34:33 -0400463 }
464 data->root.size = NFS2_FHSIZE;
465 memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
466 case 4:
467 if (data->flags & NFS_MOUNT_SECFLAVOUR) {
468 dprintk("%s: mount structure version %d does not support strong security\n",
469 __FUNCTION__,
470 data->version);
David Howells54ceac42006-08-22 20:06:13 -0400471 return -EINVAL;
David Howellsf7b422b2006-06-09 09:34:33 -0400472 }
473 case 5:
474 memset(data->context, 0, sizeof(data->context));
475 }
David Howells54ceac42006-08-22 20:06:13 -0400476
Trond Myklebust36b15c52006-08-22 20:06:14 -0400477 /* Set the pseudoflavor */
478 if (!(data->flags & NFS_MOUNT_SECFLAVOUR))
479 data->pseudoflavor = RPC_AUTH_UNIX;
480
David Howellsf7b422b2006-06-09 09:34:33 -0400481#ifndef CONFIG_NFS_V3
482 /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */
David Howellsf7b422b2006-06-09 09:34:33 -0400483 if (data->flags & NFS_MOUNT_VER3) {
484 dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__);
David Howells54ceac42006-08-22 20:06:13 -0400485 return -EPROTONOSUPPORT;
David Howellsf7b422b2006-06-09 09:34:33 -0400486 }
487#endif /* CONFIG_NFS_V3 */
488
David Howellsf7b422b2006-06-09 09:34:33 -0400489 /* We now require that the mount process passes the remote address */
David Howells54ceac42006-08-22 20:06:13 -0400490 if (data->addr.sin_addr.s_addr == INADDR_ANY) {
David Howellsf7b422b2006-06-09 09:34:33 -0400491 dprintk("%s: mount program didn't pass remote address!\n",
David Howells54ceac42006-08-22 20:06:13 -0400492 __FUNCTION__);
493 return -EINVAL;
David Howellsf7b422b2006-06-09 09:34:33 -0400494 }
495
David Howells54ceac42006-08-22 20:06:13 -0400496 /* Prepare the root filehandle */
497 if (data->flags & NFS_MOUNT_VER3)
498 mntfh->size = data->root.size;
499 else
500 mntfh->size = NFS2_FHSIZE;
501
502 if (mntfh->size > sizeof(mntfh->data)) {
503 dprintk("%s: invalid root filehandle\n", __FUNCTION__);
504 return -EINVAL;
505 }
506
507 memcpy(mntfh->data, data->root.data, mntfh->size);
508 if (mntfh->size < sizeof(mntfh->data))
509 memset(mntfh->data + mntfh->size, 0,
510 sizeof(mntfh->data) - mntfh->size);
511
512 return 0;
513}
514
515/*
516 * Initialise the common bits of the superblock
517 */
518static inline void nfs_initialise_sb(struct super_block *sb)
519{
520 struct nfs_server *server = NFS_SB(sb);
521
522 sb->s_magic = NFS_SUPER_MAGIC;
523
524 /* We probably want something more informative here */
525 snprintf(sb->s_id, sizeof(sb->s_id),
526 "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev));
527
528 if (sb->s_blocksize == 0)
529 sb->s_blocksize = nfs_block_bits(server->wsize,
530 &sb->s_blocksize_bits);
531
532 if (server->flags & NFS_MOUNT_NOAC)
533 sb->s_flags |= MS_SYNCHRONOUS;
534
535 nfs_super_set_maxbytes(sb, server->maxfilesize);
536}
537
538/*
539 * Finish setting up an NFS2/3 superblock
540 */
541static void nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data)
542{
543 struct nfs_server *server = NFS_SB(sb);
544
545 sb->s_blocksize_bits = 0;
546 sb->s_blocksize = 0;
547 if (data->bsize)
548 sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
549
550 if (server->flags & NFS_MOUNT_VER3) {
551 /* The VFS shouldn't apply the umask to mode bits. We will do
552 * so ourselves when necessary.
553 */
554 sb->s_flags |= MS_POSIXACL;
555 sb->s_time_gran = 1;
556 }
557
558 sb->s_op = &nfs_sops;
559 nfs_initialise_sb(sb);
560}
561
562/*
563 * Finish setting up a cloned NFS2/3 superblock
564 */
565static void nfs_clone_super(struct super_block *sb,
566 const struct super_block *old_sb)
567{
568 struct nfs_server *server = NFS_SB(sb);
569
570 sb->s_blocksize_bits = old_sb->s_blocksize_bits;
571 sb->s_blocksize = old_sb->s_blocksize;
572 sb->s_maxbytes = old_sb->s_maxbytes;
573
574 if (server->flags & NFS_MOUNT_VER3) {
575 /* The VFS shouldn't apply the umask to mode bits. We will do
576 * so ourselves when necessary.
577 */
578 sb->s_flags |= MS_POSIXACL;
579 sb->s_time_gran = 1;
580 }
581
582 sb->s_op = old_sb->s_op;
583 nfs_initialise_sb(sb);
584}
585
586static int nfs_set_super(struct super_block *s, void *_server)
587{
588 struct nfs_server *server = _server;
589 int ret;
590
591 s->s_fs_info = server;
592 ret = set_anon_super(s, server);
593 if (ret == 0)
594 server->s_dev = s->s_dev;
595 return ret;
596}
597
598static int nfs_compare_super(struct super_block *sb, void *data)
599{
600 struct nfs_server *server = data, *old = NFS_SB(sb);
601
602 if (old->nfs_client != server->nfs_client)
603 return 0;
604 if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0)
605 return 0;
606 return 1;
607}
608
609static int nfs_get_sb(struct file_system_type *fs_type,
610 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
611{
612 struct nfs_server *server = NULL;
613 struct super_block *s;
614 struct nfs_fh mntfh;
615 struct nfs_mount_data *data = raw_data;
616 struct dentry *mntroot;
617 int error;
618
619 /* Validate the mount data */
620 error = nfs_validate_mount_data(data, &mntfh);
621 if (error < 0)
622 return error;
623
624 /* Get a volume representation */
625 server = nfs_create_server(data, &mntfh);
626 if (IS_ERR(server)) {
627 error = PTR_ERR(server);
628 goto out_err_noserver;
629 }
630
631 /* Get a superblock - note that we may end up sharing one that already exists */
David Howellsf7b422b2006-06-09 09:34:33 -0400632 s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
Trond Myklebust816724e2006-06-24 08:41:41 -0400633 if (IS_ERR(s)) {
634 error = PTR_ERR(s);
David Howells54ceac42006-08-22 20:06:13 -0400635 goto out_err_nosb;
Trond Myklebust816724e2006-06-24 08:41:41 -0400636 }
637
David Howells54ceac42006-08-22 20:06:13 -0400638 if (s->s_fs_info != server) {
639 nfs_free_server(server);
640 server = NULL;
David Howellsf7b422b2006-06-09 09:34:33 -0400641 }
David Howells54ceac42006-08-22 20:06:13 -0400642
643 if (!s->s_root) {
644 /* initial superblock/root creation */
645 s->s_flags = flags;
646 nfs_fill_super(s, data);
647 }
648
649 mntroot = nfs_get_root(s, &mntfh);
650 if (IS_ERR(mntroot)) {
651 error = PTR_ERR(mntroot);
652 goto error_splat_super;
653 }
654
David Howellsf7b422b2006-06-09 09:34:33 -0400655 s->s_flags |= MS_ACTIVE;
David Howells54ceac42006-08-22 20:06:13 -0400656 mnt->mnt_sb = s;
657 mnt->mnt_root = mntroot;
658 return 0;
Trond Myklebust816724e2006-06-24 08:41:41 -0400659
David Howells54ceac42006-08-22 20:06:13 -0400660out_err_nosb:
661 nfs_free_server(server);
Trond Myklebust816724e2006-06-24 08:41:41 -0400662out_err_noserver:
663 return error;
David Howells54ceac42006-08-22 20:06:13 -0400664
665error_splat_super:
666 up_write(&s->s_umount);
667 deactivate_super(s);
668 return error;
David Howellsf7b422b2006-06-09 09:34:33 -0400669}
670
David Howells54ceac42006-08-22 20:06:13 -0400671/*
672 * Destroy an NFS2/3 superblock
673 */
David Howellsf7b422b2006-06-09 09:34:33 -0400674static void nfs_kill_super(struct super_block *s)
675{
676 struct nfs_server *server = NFS_SB(s);
677
678 kill_anon_super(s);
David Howells54ceac42006-08-22 20:06:13 -0400679 nfs_free_server(server);
David Howellsf7b422b2006-06-09 09:34:33 -0400680}
681
David Howells54ceac42006-08-22 20:06:13 -0400682/*
683 * Clone an NFS2/3 server record on xdev traversal (FSID-change)
684 */
685static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags,
686 const char *dev_name, void *raw_data,
687 struct vfsmount *mnt)
David Howellsf7b422b2006-06-09 09:34:33 -0400688{
689 struct nfs_clone_mount *data = raw_data;
David Howells54ceac42006-08-22 20:06:13 -0400690 struct super_block *s;
691 struct nfs_server *server;
692 struct dentry *mntroot;
693 int error;
694
695 dprintk("--> nfs_xdev_get_sb()\n");
696
697 /* create a new volume representation */
698 server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr);
699 if (IS_ERR(server)) {
700 error = PTR_ERR(server);
701 goto out_err_noserver;
702 }
703
704 /* Get a superblock - note that we may end up sharing one that already exists */
705 s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
706 if (IS_ERR(s)) {
707 error = PTR_ERR(s);
708 goto out_err_nosb;
709 }
710
711 if (s->s_fs_info != server) {
712 nfs_free_server(server);
713 server = NULL;
714 }
715
716 if (!s->s_root) {
717 /* initial superblock/root creation */
718 s->s_flags = flags;
719 nfs_clone_super(s, data->sb);
720 }
721
722 mntroot = nfs_get_root(s, data->fh);
723 if (IS_ERR(mntroot)) {
724 error = PTR_ERR(mntroot);
725 goto error_splat_super;
726 }
727
728 s->s_flags |= MS_ACTIVE;
729 mnt->mnt_sb = s;
730 mnt->mnt_root = mntroot;
731
732 dprintk("<-- nfs_xdev_get_sb() = 0\n");
733 return 0;
734
735out_err_nosb:
736 nfs_free_server(server);
737out_err_noserver:
738 dprintk("<-- nfs_xdev_get_sb() = %d [error]\n", error);
739 return error;
740
741error_splat_super:
742 up_write(&s->s_umount);
743 deactivate_super(s);
744 dprintk("<-- nfs_xdev_get_sb() = %d [splat]\n", error);
745 return error;
David Howellsf7b422b2006-06-09 09:34:33 -0400746}
747
748#ifdef CONFIG_NFS_V4
David Howells54ceac42006-08-22 20:06:13 -0400749
750/*
751 * Finish setting up a cloned NFS4 superblock
752 */
753static void nfs4_clone_super(struct super_block *sb,
754 const struct super_block *old_sb)
David Howellsf7b422b2006-06-09 09:34:33 -0400755{
David Howells54ceac42006-08-22 20:06:13 -0400756 sb->s_blocksize_bits = old_sb->s_blocksize_bits;
757 sb->s_blocksize = old_sb->s_blocksize;
758 sb->s_maxbytes = old_sb->s_maxbytes;
759 sb->s_time_gran = 1;
760 sb->s_op = old_sb->s_op;
761 nfs_initialise_sb(sb);
David Howellsf7b422b2006-06-09 09:34:33 -0400762}
763
764/*
765 * Set up an NFS4 superblock
766 */
David Howells54ceac42006-08-22 20:06:13 -0400767static void nfs4_fill_super(struct super_block *sb)
David Howellsf7b422b2006-06-09 09:34:33 -0400768{
David Howellsf7b422b2006-06-09 09:34:33 -0400769 sb->s_time_gran = 1;
David Howellsf7b422b2006-06-09 09:34:33 -0400770 sb->s_op = &nfs4_sops;
David Howells54ceac42006-08-22 20:06:13 -0400771 nfs_initialise_sb(sb);
David Howellsf7b422b2006-06-09 09:34:33 -0400772}
773
David Howells54ceac42006-08-22 20:06:13 -0400774static void *nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen)
David Howellsf7b422b2006-06-09 09:34:33 -0400775{
776 void *p = NULL;
777
778 if (!src->len)
779 return ERR_PTR(-EINVAL);
780 if (src->len < maxlen)
781 maxlen = src->len;
782 if (dst == NULL) {
783 p = dst = kmalloc(maxlen + 1, GFP_KERNEL);
784 if (p == NULL)
785 return ERR_PTR(-ENOMEM);
786 }
787 if (copy_from_user(dst, src->data, maxlen)) {
788 kfree(p);
789 return ERR_PTR(-EFAULT);
790 }
791 dst[maxlen] = '\0';
792 return dst;
793}
794
David Howells54ceac42006-08-22 20:06:13 -0400795/*
796 * Get the superblock for an NFS4 mountpoint
797 */
Trond Myklebust816724e2006-06-24 08:41:41 -0400798static int nfs4_get_sb(struct file_system_type *fs_type,
799 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
David Howellsf7b422b2006-06-09 09:34:33 -0400800{
David Howellsf7b422b2006-06-09 09:34:33 -0400801 struct nfs4_mount_data *data = raw_data;
David Howells54ceac42006-08-22 20:06:13 -0400802 struct super_block *s;
803 struct nfs_server *server;
804 struct sockaddr_in addr;
805 rpc_authflavor_t authflavour;
806 struct nfs_fh mntfh;
807 struct dentry *mntroot;
808 char *mntpath = NULL, *hostname = NULL, ip_addr[16];
David Howellsf7b422b2006-06-09 09:34:33 -0400809 void *p;
David Howells54ceac42006-08-22 20:06:13 -0400810 int error;
David Howellsf7b422b2006-06-09 09:34:33 -0400811
812 if (data == NULL) {
813 dprintk("%s: missing data argument\n", __FUNCTION__);
Trond Myklebust816724e2006-06-24 08:41:41 -0400814 return -EINVAL;
David Howellsf7b422b2006-06-09 09:34:33 -0400815 }
816 if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) {
817 dprintk("%s: bad mount version\n", __FUNCTION__);
Trond Myklebust816724e2006-06-24 08:41:41 -0400818 return -EINVAL;
David Howellsf7b422b2006-06-09 09:34:33 -0400819 }
820
David Howells54ceac42006-08-22 20:06:13 -0400821 /* We now require that the mount process passes the remote address */
822 if (data->host_addrlen != sizeof(addr))
823 return -EINVAL;
824
825 if (copy_from_user(&addr, data->host_addr, sizeof(addr)))
826 return -EFAULT;
827
828 if (addr.sin_family != AF_INET ||
829 addr.sin_addr.s_addr == INADDR_ANY
830 ) {
831 dprintk("%s: mount program didn't pass remote IP address!\n",
832 __FUNCTION__);
833 return -EINVAL;
834 }
Trond Myklebust51b6ded2006-09-15 16:31:56 -0400835 /* RFC3530: The default port for NFS is 2049 */
836 if (addr.sin_port == 0)
Trond Myklebust575b5c72006-10-19 23:28:37 -0700837 addr.sin_port = htons(NFS_PORT);
David Howells54ceac42006-08-22 20:06:13 -0400838
839 /* Grab the authentication type */
840 authflavour = RPC_AUTH_UNIX;
841 if (data->auth_flavourlen != 0) {
842 if (data->auth_flavourlen != 1) {
843 dprintk("%s: Invalid number of RPC auth flavours %d.\n",
844 __FUNCTION__, data->auth_flavourlen);
845 error = -EINVAL;
846 goto out_err_noserver;
847 }
848
849 if (copy_from_user(&authflavour, data->auth_flavours,
850 sizeof(authflavour))) {
851 error = -EFAULT;
852 goto out_err_noserver;
853 }
854 }
David Howellsf7b422b2006-06-09 09:34:33 -0400855
856 p = nfs_copy_user_string(NULL, &data->hostname, 256);
857 if (IS_ERR(p))
858 goto out_err;
David Howells54ceac42006-08-22 20:06:13 -0400859 hostname = p;
David Howellsf7b422b2006-06-09 09:34:33 -0400860
861 p = nfs_copy_user_string(NULL, &data->mnt_path, 1024);
862 if (IS_ERR(p))
863 goto out_err;
David Howells54ceac42006-08-22 20:06:13 -0400864 mntpath = p;
David Howellsf7b422b2006-06-09 09:34:33 -0400865
David Howells54ceac42006-08-22 20:06:13 -0400866 dprintk("MNTPATH: %s\n", mntpath);
867
868 p = nfs_copy_user_string(ip_addr, &data->client_addr,
869 sizeof(ip_addr) - 1);
David Howellsf7b422b2006-06-09 09:34:33 -0400870 if (IS_ERR(p))
871 goto out_err;
872
David Howells54ceac42006-08-22 20:06:13 -0400873 /* Get a volume representation */
874 server = nfs4_create_server(data, hostname, &addr, mntpath, ip_addr,
875 authflavour, &mntfh);
876 if (IS_ERR(server)) {
877 error = PTR_ERR(server);
878 goto out_err_noserver;
David Howellsf7b422b2006-06-09 09:34:33 -0400879 }
880
David Howells54ceac42006-08-22 20:06:13 -0400881 /* Get a superblock - note that we may end up sharing one that already exists */
882 s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
Trond Myklebust816724e2006-06-24 08:41:41 -0400883 if (IS_ERR(s)) {
884 error = PTR_ERR(s);
David Howellsf7b422b2006-06-09 09:34:33 -0400885 goto out_free;
Trond Myklebust816724e2006-06-24 08:41:41 -0400886 }
887
Trond Myklebust5dd31772006-08-24 01:03:05 -0400888 if (s->s_fs_info != server) {
889 nfs_free_server(server);
890 server = NULL;
891 }
892
David Howells54ceac42006-08-22 20:06:13 -0400893 if (!s->s_root) {
894 /* initial superblock/root creation */
895 s->s_flags = flags;
David Howells54ceac42006-08-22 20:06:13 -0400896 nfs4_fill_super(s);
Trond Myklebust816724e2006-06-24 08:41:41 -0400897 }
David Howellsf7b422b2006-06-09 09:34:33 -0400898
David Howells54ceac42006-08-22 20:06:13 -0400899 mntroot = nfs4_get_root(s, &mntfh);
900 if (IS_ERR(mntroot)) {
901 error = PTR_ERR(mntroot);
902 goto error_splat_super;
David Howellsf7b422b2006-06-09 09:34:33 -0400903 }
David Howells54ceac42006-08-22 20:06:13 -0400904
David Howellsf7b422b2006-06-09 09:34:33 -0400905 s->s_flags |= MS_ACTIVE;
David Howells54ceac42006-08-22 20:06:13 -0400906 mnt->mnt_sb = s;
907 mnt->mnt_root = mntroot;
908 kfree(mntpath);
909 kfree(hostname);
910 return 0;
911
David Howellsf7b422b2006-06-09 09:34:33 -0400912out_err:
Trond Myklebust816724e2006-06-24 08:41:41 -0400913 error = PTR_ERR(p);
David Howells54ceac42006-08-22 20:06:13 -0400914 goto out_err_noserver;
915
David Howellsf7b422b2006-06-09 09:34:33 -0400916out_free:
David Howells54ceac42006-08-22 20:06:13 -0400917 nfs_free_server(server);
918out_err_noserver:
919 kfree(mntpath);
920 kfree(hostname);
Trond Myklebust816724e2006-06-24 08:41:41 -0400921 return error;
David Howells54ceac42006-08-22 20:06:13 -0400922
923error_splat_super:
924 up_write(&s->s_umount);
925 deactivate_super(s);
926 goto out_err_noserver;
David Howellsf7b422b2006-06-09 09:34:33 -0400927}
928
929static void nfs4_kill_super(struct super_block *sb)
930{
931 struct nfs_server *server = NFS_SB(sb);
932
933 nfs_return_all_delegations(sb);
934 kill_anon_super(sb);
935
936 nfs4_renewd_prepare_shutdown(server);
David Howells54ceac42006-08-22 20:06:13 -0400937 nfs_free_server(server);
David Howellsf7b422b2006-06-09 09:34:33 -0400938}
939
940/*
David Howells54ceac42006-08-22 20:06:13 -0400941 * Clone an NFS4 server record on xdev traversal (FSID-change)
David Howellsf7b422b2006-06-09 09:34:33 -0400942 */
David Howells54ceac42006-08-22 20:06:13 -0400943static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags,
944 const char *dev_name, void *raw_data,
945 struct vfsmount *mnt)
David Howellsf7b422b2006-06-09 09:34:33 -0400946{
947 struct nfs_clone_mount *data = raw_data;
David Howells54ceac42006-08-22 20:06:13 -0400948 struct super_block *s;
949 struct nfs_server *server;
950 struct dentry *mntroot;
951 int error;
952
953 dprintk("--> nfs4_xdev_get_sb()\n");
954
955 /* create a new volume representation */
956 server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr);
957 if (IS_ERR(server)) {
958 error = PTR_ERR(server);
959 goto out_err_noserver;
960 }
961
962 /* Get a superblock - note that we may end up sharing one that already exists */
963 s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
964 if (IS_ERR(s)) {
965 error = PTR_ERR(s);
966 goto out_err_nosb;
967 }
968
969 if (s->s_fs_info != server) {
970 nfs_free_server(server);
971 server = NULL;
972 }
973
974 if (!s->s_root) {
975 /* initial superblock/root creation */
976 s->s_flags = flags;
977 nfs4_clone_super(s, data->sb);
978 }
979
980 mntroot = nfs4_get_root(s, data->fh);
981 if (IS_ERR(mntroot)) {
982 error = PTR_ERR(mntroot);
983 goto error_splat_super;
984 }
985
986 s->s_flags |= MS_ACTIVE;
987 mnt->mnt_sb = s;
988 mnt->mnt_root = mntroot;
989
990 dprintk("<-- nfs4_xdev_get_sb() = 0\n");
991 return 0;
992
993out_err_nosb:
994 nfs_free_server(server);
995out_err_noserver:
996 dprintk("<-- nfs4_xdev_get_sb() = %d [error]\n", error);
997 return error;
998
999error_splat_super:
1000 up_write(&s->s_umount);
1001 deactivate_super(s);
1002 dprintk("<-- nfs4_xdev_get_sb() = %d [splat]\n", error);
1003 return error;
David Howellsf7b422b2006-06-09 09:34:33 -04001004}
1005
David Howells54ceac42006-08-22 20:06:13 -04001006/*
1007 * Create an NFS4 server record on referral traversal
1008 */
1009static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags,
1010 const char *dev_name, void *raw_data,
1011 struct vfsmount *mnt)
David Howellsf7b422b2006-06-09 09:34:33 -04001012{
1013 struct nfs_clone_mount *data = raw_data;
David Howells54ceac42006-08-22 20:06:13 -04001014 struct super_block *s;
1015 struct nfs_server *server;
1016 struct dentry *mntroot;
1017 struct nfs_fh mntfh;
1018 int error;
1019
1020 dprintk("--> nfs4_referral_get_sb()\n");
1021
1022 /* create a new volume representation */
1023 server = nfs4_create_referral_server(data, &mntfh);
1024 if (IS_ERR(server)) {
1025 error = PTR_ERR(server);
1026 goto out_err_noserver;
1027 }
1028
1029 /* Get a superblock - note that we may end up sharing one that already exists */
1030 s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
1031 if (IS_ERR(s)) {
1032 error = PTR_ERR(s);
1033 goto out_err_nosb;
1034 }
1035
1036 if (s->s_fs_info != server) {
1037 nfs_free_server(server);
1038 server = NULL;
1039 }
1040
1041 if (!s->s_root) {
1042 /* initial superblock/root creation */
1043 s->s_flags = flags;
1044 nfs4_fill_super(s);
1045 }
1046
1047 mntroot = nfs4_get_root(s, data->fh);
1048 if (IS_ERR(mntroot)) {
1049 error = PTR_ERR(mntroot);
1050 goto error_splat_super;
1051 }
1052
1053 s->s_flags |= MS_ACTIVE;
1054 mnt->mnt_sb = s;
1055 mnt->mnt_root = mntroot;
1056
1057 dprintk("<-- nfs4_referral_get_sb() = 0\n");
1058 return 0;
1059
1060out_err_nosb:
1061 nfs_free_server(server);
1062out_err_noserver:
1063 dprintk("<-- nfs4_referral_get_sb() = %d [error]\n", error);
1064 return error;
1065
1066error_splat_super:
1067 up_write(&s->s_umount);
1068 deactivate_super(s);
1069 dprintk("<-- nfs4_referral_get_sb() = %d [splat]\n", error);
1070 return error;
David Howellsf7b422b2006-06-09 09:34:33 -04001071}
1072
David Howells54ceac42006-08-22 20:06:13 -04001073#endif /* CONFIG_NFS_V4 */