blob: b47e87e6c4063bafd708ce7a645effd0d32e0505 [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>
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -040036#include <linux/sunrpc/xprtsock.h>
David Howellsf7b422b2006-06-09 09:34:33 -040037#include <linux/nfs_fs.h>
38#include <linux/nfs_mount.h>
39#include <linux/nfs4_mount.h>
40#include <linux/lockd/bind.h>
41#include <linux/smp_lock.h>
42#include <linux/seq_file.h>
43#include <linux/mount.h>
44#include <linux/nfs_idmap.h>
45#include <linux/vfs.h>
46#include <linux/inet.h>
47#include <linux/nfs_xdr.h>
Adrian Bunkb5d5dfb2007-02-12 00:53:40 -080048#include <linux/magic.h>
Chuck Leverbf0fd762007-07-01 12:13:44 -040049#include <linux/parser.h>
David Howellsf7b422b2006-06-09 09:34:33 -040050
51#include <asm/system.h>
52#include <asm/uaccess.h>
53
54#include "nfs4_fs.h"
55#include "callback.h"
56#include "delegation.h"
57#include "iostat.h"
58#include "internal.h"
59
60#define NFSDBG_FACILITY NFSDBG_VFS
61
Chuck Leverbf0fd762007-07-01 12:13:44 -040062enum {
63 /* Mount options that take no arguments */
64 Opt_soft, Opt_hard,
65 Opt_intr, Opt_nointr,
66 Opt_posix, Opt_noposix,
67 Opt_cto, Opt_nocto,
68 Opt_ac, Opt_noac,
69 Opt_lock, Opt_nolock,
70 Opt_v2, Opt_v3,
71 Opt_udp, Opt_tcp,
72 Opt_acl, Opt_noacl,
73 Opt_rdirplus, Opt_nordirplus,
Trond Myklebust75180df2007-05-16 16:53:28 -040074 Opt_sharecache, Opt_nosharecache,
Chuck Leverbf0fd762007-07-01 12:13:44 -040075
76 /* Mount options that take integer arguments */
77 Opt_port,
78 Opt_rsize, Opt_wsize, Opt_bsize,
79 Opt_timeo, Opt_retrans,
80 Opt_acregmin, Opt_acregmax,
81 Opt_acdirmin, Opt_acdirmax,
82 Opt_actimeo,
83 Opt_namelen,
84 Opt_mountport,
85 Opt_mountprog, Opt_mountvers,
86 Opt_nfsprog, Opt_nfsvers,
87
88 /* Mount options that take string arguments */
89 Opt_sec, Opt_proto, Opt_mountproto,
Chuck Lever0ac83772007-09-11 18:01:04 -040090 Opt_addr, Opt_mountaddr, Opt_clientaddr,
Chuck Leverbf0fd762007-07-01 12:13:44 -040091
92 /* Mount options that are ignored */
93 Opt_userspace, Opt_deprecated,
94
95 Opt_err
96};
97
98static match_table_t nfs_mount_option_tokens = {
99 { Opt_userspace, "bg" },
100 { Opt_userspace, "fg" },
101 { Opt_soft, "soft" },
102 { Opt_hard, "hard" },
103 { Opt_intr, "intr" },
104 { Opt_nointr, "nointr" },
105 { Opt_posix, "posix" },
106 { Opt_noposix, "noposix" },
107 { Opt_cto, "cto" },
108 { Opt_nocto, "nocto" },
109 { Opt_ac, "ac" },
110 { Opt_noac, "noac" },
111 { Opt_lock, "lock" },
112 { Opt_nolock, "nolock" },
113 { Opt_v2, "v2" },
114 { Opt_v3, "v3" },
115 { Opt_udp, "udp" },
116 { Opt_tcp, "tcp" },
117 { Opt_acl, "acl" },
118 { Opt_noacl, "noacl" },
119 { Opt_rdirplus, "rdirplus" },
120 { Opt_nordirplus, "nordirplus" },
Trond Myklebust75180df2007-05-16 16:53:28 -0400121 { Opt_sharecache, "sharecache" },
122 { Opt_nosharecache, "nosharecache" },
Chuck Leverbf0fd762007-07-01 12:13:44 -0400123
124 { Opt_port, "port=%u" },
125 { Opt_rsize, "rsize=%u" },
126 { Opt_wsize, "wsize=%u" },
127 { Opt_bsize, "bsize=%u" },
128 { Opt_timeo, "timeo=%u" },
129 { Opt_retrans, "retrans=%u" },
130 { Opt_acregmin, "acregmin=%u" },
131 { Opt_acregmax, "acregmax=%u" },
132 { Opt_acdirmin, "acdirmin=%u" },
133 { Opt_acdirmax, "acdirmax=%u" },
134 { Opt_actimeo, "actimeo=%u" },
135 { Opt_userspace, "retry=%u" },
136 { Opt_namelen, "namlen=%u" },
137 { Opt_mountport, "mountport=%u" },
138 { Opt_mountprog, "mountprog=%u" },
139 { Opt_mountvers, "mountvers=%u" },
140 { Opt_nfsprog, "nfsprog=%u" },
141 { Opt_nfsvers, "nfsvers=%u" },
142 { Opt_nfsvers, "vers=%u" },
143
144 { Opt_sec, "sec=%s" },
145 { Opt_proto, "proto=%s" },
146 { Opt_mountproto, "mountproto=%s" },
147 { Opt_addr, "addr=%s" },
148 { Opt_clientaddr, "clientaddr=%s" },
Chuck Lever0ac83772007-09-11 18:01:04 -0400149 { Opt_userspace, "mounthost=%s" },
150 { Opt_mountaddr, "mountaddr=%s" },
Chuck Leverbf0fd762007-07-01 12:13:44 -0400151
152 { Opt_err, NULL }
153};
154
155enum {
156 Opt_xprt_udp, Opt_xprt_tcp,
157
158 Opt_xprt_err
159};
160
161static match_table_t nfs_xprt_protocol_tokens = {
162 { Opt_xprt_udp, "udp" },
163 { Opt_xprt_tcp, "tcp" },
164
165 { Opt_xprt_err, NULL }
166};
167
168enum {
169 Opt_sec_none, Opt_sec_sys,
170 Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p,
171 Opt_sec_lkey, Opt_sec_lkeyi, Opt_sec_lkeyp,
172 Opt_sec_spkm, Opt_sec_spkmi, Opt_sec_spkmp,
173
174 Opt_sec_err
175};
176
177static match_table_t nfs_secflavor_tokens = {
178 { Opt_sec_none, "none" },
179 { Opt_sec_none, "null" },
180 { Opt_sec_sys, "sys" },
181
182 { Opt_sec_krb5, "krb5" },
183 { Opt_sec_krb5i, "krb5i" },
184 { Opt_sec_krb5p, "krb5p" },
185
186 { Opt_sec_lkey, "lkey" },
187 { Opt_sec_lkeyi, "lkeyi" },
188 { Opt_sec_lkeyp, "lkeyp" },
189
190 { Opt_sec_err, NULL }
191};
192
193
David Howellsf7b422b2006-06-09 09:34:33 -0400194static void nfs_umount_begin(struct vfsmount *, int);
Trond Myklebust816724e2006-06-24 08:41:41 -0400195static int nfs_statfs(struct dentry *, struct kstatfs *);
David Howellsf7b422b2006-06-09 09:34:33 -0400196static int nfs_show_options(struct seq_file *, struct vfsmount *);
197static int nfs_show_stats(struct seq_file *, struct vfsmount *);
Trond Myklebust816724e2006-06-24 08:41:41 -0400198static int nfs_get_sb(struct file_system_type *, int, const char *, void *, struct vfsmount *);
David Howells54ceac42006-08-22 20:06:13 -0400199static int nfs_xdev_get_sb(struct file_system_type *fs_type,
Trond Myklebust816724e2006-06-24 08:41:41 -0400200 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
David Howellsf7b422b2006-06-09 09:34:33 -0400201static void nfs_kill_super(struct super_block *);
202
203static struct file_system_type nfs_fs_type = {
204 .owner = THIS_MODULE,
205 .name = "nfs",
206 .get_sb = nfs_get_sb,
207 .kill_sb = nfs_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -0700208 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -0400209};
210
David Howells54ceac42006-08-22 20:06:13 -0400211struct file_system_type nfs_xdev_fs_type = {
David Howellsf7b422b2006-06-09 09:34:33 -0400212 .owner = THIS_MODULE,
213 .name = "nfs",
David Howells54ceac42006-08-22 20:06:13 -0400214 .get_sb = nfs_xdev_get_sb,
David Howellsf7b422b2006-06-09 09:34:33 -0400215 .kill_sb = nfs_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -0700216 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -0400217};
218
Josef 'Jeff' Sipekee9b6d62007-02-12 00:55:41 -0800219static const struct super_operations nfs_sops = {
David Howellsf7b422b2006-06-09 09:34:33 -0400220 .alloc_inode = nfs_alloc_inode,
221 .destroy_inode = nfs_destroy_inode,
222 .write_inode = nfs_write_inode,
223 .statfs = nfs_statfs,
224 .clear_inode = nfs_clear_inode,
225 .umount_begin = nfs_umount_begin,
226 .show_options = nfs_show_options,
227 .show_stats = nfs_show_stats,
228};
229
230#ifdef CONFIG_NFS_V4
Trond Myklebust816724e2006-06-24 08:41:41 -0400231static int nfs4_get_sb(struct file_system_type *fs_type,
232 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
David Howells54ceac42006-08-22 20:06:13 -0400233static int nfs4_xdev_get_sb(struct file_system_type *fs_type,
234 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
235static int nfs4_referral_get_sb(struct file_system_type *fs_type,
236 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
David Howellsf7b422b2006-06-09 09:34:33 -0400237static void nfs4_kill_super(struct super_block *sb);
238
239static struct file_system_type nfs4_fs_type = {
240 .owner = THIS_MODULE,
241 .name = "nfs4",
242 .get_sb = nfs4_get_sb,
243 .kill_sb = nfs4_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -0700244 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -0400245};
246
David Howells54ceac42006-08-22 20:06:13 -0400247struct file_system_type nfs4_xdev_fs_type = {
David Howellsf7b422b2006-06-09 09:34:33 -0400248 .owner = THIS_MODULE,
249 .name = "nfs4",
David Howells54ceac42006-08-22 20:06:13 -0400250 .get_sb = nfs4_xdev_get_sb,
David Howellsf7b422b2006-06-09 09:34:33 -0400251 .kill_sb = nfs4_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -0700252 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -0400253};
254
David Howells54ceac42006-08-22 20:06:13 -0400255struct file_system_type nfs4_referral_fs_type = {
David Howellsf7b422b2006-06-09 09:34:33 -0400256 .owner = THIS_MODULE,
257 .name = "nfs4",
David Howells54ceac42006-08-22 20:06:13 -0400258 .get_sb = nfs4_referral_get_sb,
David Howellsf7b422b2006-06-09 09:34:33 -0400259 .kill_sb = nfs4_kill_super,
Mark Fasheh349457c2006-09-08 14:22:21 -0700260 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
David Howellsf7b422b2006-06-09 09:34:33 -0400261};
262
Josef 'Jeff' Sipekee9b6d62007-02-12 00:55:41 -0800263static const struct super_operations nfs4_sops = {
David Howellsf7b422b2006-06-09 09:34:33 -0400264 .alloc_inode = nfs_alloc_inode,
265 .destroy_inode = nfs_destroy_inode,
266 .write_inode = nfs_write_inode,
267 .statfs = nfs_statfs,
268 .clear_inode = nfs4_clear_inode,
269 .umount_begin = nfs_umount_begin,
270 .show_options = nfs_show_options,
271 .show_stats = nfs_show_stats,
272};
273#endif
274
Rusty Russell8e1f9362007-07-17 04:03:17 -0700275static struct shrinker acl_shrinker = {
276 .shrink = nfs_access_cache_shrinker,
277 .seeks = DEFAULT_SEEKS,
278};
Trond Myklebust979df722006-07-25 11:28:19 -0400279
David Howellsf7b422b2006-06-09 09:34:33 -0400280/*
281 * Register the NFS filesystems
282 */
283int __init register_nfs_fs(void)
284{
285 int ret;
286
287 ret = register_filesystem(&nfs_fs_type);
288 if (ret < 0)
289 goto error_0;
290
David Howellsf7b422b2006-06-09 09:34:33 -0400291 ret = nfs_register_sysctl();
292 if (ret < 0)
293 goto error_1;
Peter Zijlstra89a09142007-03-16 13:38:26 -0800294#ifdef CONFIG_NFS_V4
David Howellsf7b422b2006-06-09 09:34:33 -0400295 ret = register_filesystem(&nfs4_fs_type);
296 if (ret < 0)
297 goto error_2;
298#endif
Rusty Russell8e1f9362007-07-17 04:03:17 -0700299 register_shrinker(&acl_shrinker);
David Howellsf7b422b2006-06-09 09:34:33 -0400300 return 0;
301
302#ifdef CONFIG_NFS_V4
303error_2:
304 nfs_unregister_sysctl();
Peter Zijlstra89a09142007-03-16 13:38:26 -0800305#endif
David Howellsf7b422b2006-06-09 09:34:33 -0400306error_1:
307 unregister_filesystem(&nfs_fs_type);
David Howellsf7b422b2006-06-09 09:34:33 -0400308error_0:
309 return ret;
310}
311
312/*
313 * Unregister the NFS filesystems
314 */
315void __exit unregister_nfs_fs(void)
316{
Rusty Russell8e1f9362007-07-17 04:03:17 -0700317 unregister_shrinker(&acl_shrinker);
David Howellsf7b422b2006-06-09 09:34:33 -0400318#ifdef CONFIG_NFS_V4
319 unregister_filesystem(&nfs4_fs_type);
David Howellsf7b422b2006-06-09 09:34:33 -0400320#endif
Alexey Dobriyan49af7ee2007-09-18 22:46:40 -0700321 nfs_unregister_sysctl();
David Howellsf7b422b2006-06-09 09:34:33 -0400322 unregister_filesystem(&nfs_fs_type);
323}
324
325/*
326 * Deliver file system statistics to userspace
327 */
Trond Myklebust816724e2006-06-24 08:41:41 -0400328static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
David Howellsf7b422b2006-06-09 09:34:33 -0400329{
David Howells0c7d90c2006-08-22 20:06:10 -0400330 struct nfs_server *server = NFS_SB(dentry->d_sb);
David Howellsf7b422b2006-06-09 09:34:33 -0400331 unsigned char blockbits;
332 unsigned long blockres;
David Howells0c7d90c2006-08-22 20:06:10 -0400333 struct nfs_fh *fh = NFS_FH(dentry->d_inode);
David Howellsf7b422b2006-06-09 09:34:33 -0400334 struct nfs_fattr fattr;
335 struct nfs_fsstat res = {
336 .fattr = &fattr,
337 };
338 int error;
339
340 lock_kernel();
341
David Howells8fa5c002006-08-22 20:06:12 -0400342 error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
David Howellsf7b422b2006-06-09 09:34:33 -0400343 if (error < 0)
344 goto out_err;
Amnon Aaronsohn1a0ba9a2007-04-09 22:05:26 -0700345 buf->f_type = NFS_SUPER_MAGIC;
David Howellsf7b422b2006-06-09 09:34:33 -0400346
347 /*
348 * Current versions of glibc do not correctly handle the
349 * case where f_frsize != f_bsize. Eventually we want to
350 * report the value of wtmult in this field.
351 */
David Howells0c7d90c2006-08-22 20:06:10 -0400352 buf->f_frsize = dentry->d_sb->s_blocksize;
David Howellsf7b422b2006-06-09 09:34:33 -0400353
354 /*
355 * On most *nix systems, f_blocks, f_bfree, and f_bavail
356 * are reported in units of f_frsize. Linux hasn't had
357 * an f_frsize field in its statfs struct until recently,
358 * thus historically Linux's sys_statfs reports these
359 * fields in units of f_bsize.
360 */
David Howells0c7d90c2006-08-22 20:06:10 -0400361 buf->f_bsize = dentry->d_sb->s_blocksize;
362 blockbits = dentry->d_sb->s_blocksize_bits;
David Howellsf7b422b2006-06-09 09:34:33 -0400363 blockres = (1 << blockbits) - 1;
364 buf->f_blocks = (res.tbytes + blockres) >> blockbits;
365 buf->f_bfree = (res.fbytes + blockres) >> blockbits;
366 buf->f_bavail = (res.abytes + blockres) >> blockbits;
367
368 buf->f_files = res.tfiles;
369 buf->f_ffree = res.afiles;
370
371 buf->f_namelen = server->namelen;
Amnon Aaronsohn1a0ba9a2007-04-09 22:05:26 -0700372
David Howellsf7b422b2006-06-09 09:34:33 -0400373 unlock_kernel();
374 return 0;
375
376 out_err:
377 dprintk("%s: statfs error = %d\n", __FUNCTION__, -error);
Amnon Aaronsohn1a0ba9a2007-04-09 22:05:26 -0700378 unlock_kernel();
379 return error;
David Howellsf7b422b2006-06-09 09:34:33 -0400380}
381
David Howells7d4e2742006-08-22 20:06:07 -0400382/*
383 * Map the security flavour number to a name
384 */
Trond Myklebust81039f12006-06-09 09:34:34 -0400385static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour)
386{
David Howells7d4e2742006-08-22 20:06:07 -0400387 static const struct {
Trond Myklebust81039f12006-06-09 09:34:34 -0400388 rpc_authflavor_t flavour;
389 const char *str;
390 } sec_flavours[] = {
391 { RPC_AUTH_NULL, "null" },
392 { RPC_AUTH_UNIX, "sys" },
393 { RPC_AUTH_GSS_KRB5, "krb5" },
394 { RPC_AUTH_GSS_KRB5I, "krb5i" },
395 { RPC_AUTH_GSS_KRB5P, "krb5p" },
396 { RPC_AUTH_GSS_LKEY, "lkey" },
397 { RPC_AUTH_GSS_LKEYI, "lkeyi" },
398 { RPC_AUTH_GSS_LKEYP, "lkeyp" },
399 { RPC_AUTH_GSS_SPKM, "spkm" },
400 { RPC_AUTH_GSS_SPKMI, "spkmi" },
401 { RPC_AUTH_GSS_SPKMP, "spkmp" },
Chuck Lever4d81cd12007-07-01 12:12:40 -0400402 { UINT_MAX, "unknown" }
Trond Myklebust81039f12006-06-09 09:34:34 -0400403 };
404 int i;
405
Chuck Lever4d81cd12007-07-01 12:12:40 -0400406 for (i = 0; sec_flavours[i].flavour != UINT_MAX; i++) {
Trond Myklebust81039f12006-06-09 09:34:34 -0400407 if (sec_flavours[i].flavour == flavour)
408 break;
409 }
410 return sec_flavours[i].str;
411}
412
David Howellsf7b422b2006-06-09 09:34:33 -0400413/*
414 * Describe the mount options in force on this server representation
415 */
416static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults)
417{
David Howells509de812006-08-22 20:06:11 -0400418 static const struct proc_nfs_info {
David Howellsf7b422b2006-06-09 09:34:33 -0400419 int flag;
David Howells509de812006-08-22 20:06:11 -0400420 const char *str;
421 const char *nostr;
David Howellsf7b422b2006-06-09 09:34:33 -0400422 } nfs_info[] = {
423 { NFS_MOUNT_SOFT, ",soft", ",hard" },
424 { NFS_MOUNT_INTR, ",intr", "" },
425 { NFS_MOUNT_NOCTO, ",nocto", "" },
426 { NFS_MOUNT_NOAC, ",noac", "" },
427 { NFS_MOUNT_NONLM, ",nolock", "" },
428 { NFS_MOUNT_NOACL, ",noacl", "" },
Steve Dickson74dd34e2007-04-14 17:01:15 -0400429 { NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
Trond Myklebust75180df2007-05-16 16:53:28 -0400430 { NFS_MOUNT_UNSHARED, ",nosharecache", ""},
David Howellsf7b422b2006-06-09 09:34:33 -0400431 { 0, NULL, NULL }
432 };
David Howells509de812006-08-22 20:06:11 -0400433 const struct proc_nfs_info *nfs_infop;
David Howells8fa5c002006-08-22 20:06:12 -0400434 struct nfs_client *clp = nfss->nfs_client;
David Howellsf7b422b2006-06-09 09:34:33 -0400435 char buf[12];
David Howells509de812006-08-22 20:06:11 -0400436 const char *proto;
David Howellsf7b422b2006-06-09 09:34:33 -0400437
David Howells8fa5c002006-08-22 20:06:12 -0400438 seq_printf(m, ",vers=%d", clp->rpc_ops->version);
David Howellsf7b422b2006-06-09 09:34:33 -0400439 seq_printf(m, ",rsize=%d", nfss->rsize);
440 seq_printf(m, ",wsize=%d", nfss->wsize);
441 if (nfss->acregmin != 3*HZ || showdefaults)
442 seq_printf(m, ",acregmin=%d", nfss->acregmin/HZ);
443 if (nfss->acregmax != 60*HZ || showdefaults)
444 seq_printf(m, ",acregmax=%d", nfss->acregmax/HZ);
445 if (nfss->acdirmin != 30*HZ || showdefaults)
446 seq_printf(m, ",acdirmin=%d", nfss->acdirmin/HZ);
447 if (nfss->acdirmax != 60*HZ || showdefaults)
448 seq_printf(m, ",acdirmax=%d", nfss->acdirmax/HZ);
449 for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) {
450 if (nfss->flags & nfs_infop->flag)
451 seq_puts(m, nfs_infop->str);
452 else
453 seq_puts(m, nfs_infop->nostr);
454 }
455 switch (nfss->client->cl_xprt->prot) {
456 case IPPROTO_TCP:
457 proto = "tcp";
458 break;
459 case IPPROTO_UDP:
460 proto = "udp";
461 break;
462 default:
463 snprintf(buf, sizeof(buf), "%u", nfss->client->cl_xprt->prot);
464 proto = buf;
465 }
466 seq_printf(m, ",proto=%s", proto);
David Howells5006a762006-08-22 20:06:12 -0400467 seq_printf(m, ",timeo=%lu", 10U * clp->retrans_timeo / HZ);
468 seq_printf(m, ",retrans=%u", clp->retrans_count);
Trond Myklebust81039f12006-06-09 09:34:34 -0400469 seq_printf(m, ",sec=%s", nfs_pseudoflavour_to_name(nfss->client->cl_auth->au_flavor));
David Howellsf7b422b2006-06-09 09:34:33 -0400470}
471
472/*
473 * Describe the mount options on this VFS mountpoint
474 */
475static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
476{
477 struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
478
479 nfs_show_mount_options(m, nfss, 0);
480
Jeff Laytonddc01c02007-07-30 08:47:38 -0400481 seq_printf(m, ",addr="NIPQUAD_FMT,
482 NIPQUAD(nfss->nfs_client->cl_addr.sin_addr));
David Howellsf7b422b2006-06-09 09:34:33 -0400483
484 return 0;
485}
486
487/*
488 * Present statistical information for this VFS mountpoint
489 */
490static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
491{
492 int i, cpu;
493 struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
494 struct rpc_auth *auth = nfss->client->cl_auth;
495 struct nfs_iostats totals = { };
496
497 seq_printf(m, "statvers=%s", NFS_IOSTAT_VERS);
498
499 /*
500 * Display all mount option settings
501 */
502 seq_printf(m, "\n\topts:\t");
503 seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw");
504 seq_puts(m, mnt->mnt_sb->s_flags & MS_SYNCHRONOUS ? ",sync" : "");
505 seq_puts(m, mnt->mnt_sb->s_flags & MS_NOATIME ? ",noatime" : "");
506 seq_puts(m, mnt->mnt_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : "");
507 nfs_show_mount_options(m, nfss, 1);
508
509 seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ);
510
511 seq_printf(m, "\n\tcaps:\t");
512 seq_printf(m, "caps=0x%x", nfss->caps);
513 seq_printf(m, ",wtmult=%d", nfss->wtmult);
514 seq_printf(m, ",dtsize=%d", nfss->dtsize);
515 seq_printf(m, ",bsize=%d", nfss->bsize);
516 seq_printf(m, ",namelen=%d", nfss->namelen);
517
518#ifdef CONFIG_NFS_V4
David Howells8fa5c002006-08-22 20:06:12 -0400519 if (nfss->nfs_client->cl_nfsversion == 4) {
David Howellsf7b422b2006-06-09 09:34:33 -0400520 seq_printf(m, "\n\tnfsv4:\t");
521 seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
522 seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
523 seq_printf(m, ",acl=0x%x", nfss->acl_bitmask);
524 }
525#endif
526
527 /*
528 * Display security flavor in effect for this mount
529 */
530 seq_printf(m, "\n\tsec:\tflavor=%d", auth->au_ops->au_flavor);
531 if (auth->au_flavor)
532 seq_printf(m, ",pseudoflavor=%d", auth->au_flavor);
533
534 /*
535 * Display superblock I/O counters
536 */
537 for_each_possible_cpu(cpu) {
538 struct nfs_iostats *stats;
539
540 preempt_disable();
541 stats = per_cpu_ptr(nfss->io_stats, cpu);
542
543 for (i = 0; i < __NFSIOS_COUNTSMAX; i++)
544 totals.events[i] += stats->events[i];
545 for (i = 0; i < __NFSIOS_BYTESMAX; i++)
546 totals.bytes[i] += stats->bytes[i];
547
548 preempt_enable();
549 }
550
551 seq_printf(m, "\n\tevents:\t");
552 for (i = 0; i < __NFSIOS_COUNTSMAX; i++)
553 seq_printf(m, "%lu ", totals.events[i]);
554 seq_printf(m, "\n\tbytes:\t");
555 for (i = 0; i < __NFSIOS_BYTESMAX; i++)
556 seq_printf(m, "%Lu ", totals.bytes[i]);
557 seq_printf(m, "\n");
558
559 rpc_print_iostats(m, nfss->client);
560
561 return 0;
562}
563
564/*
565 * Begin unmount by attempting to remove all automounted mountpoints we added
David Howells54ceac42006-08-22 20:06:13 -0400566 * in response to xdev traversals and referrals
David Howellsf7b422b2006-06-09 09:34:33 -0400567 */
568static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags)
569{
Trond Myklebustfc6ae3c2007-06-05 19:13:47 -0400570 struct nfs_server *server = NFS_SB(vfsmnt->mnt_sb);
571 struct rpc_clnt *rpc;
572
David Howellsf7b422b2006-06-09 09:34:33 -0400573 shrink_submounts(vfsmnt, &nfs_automount_list);
Trond Myklebustfc6ae3c2007-06-05 19:13:47 -0400574
575 if (!(flags & MNT_FORCE))
576 return;
577 /* -EIO all pending I/O */
578 rpc = server->client_acl;
579 if (!IS_ERR(rpc))
580 rpc_killall_tasks(rpc);
581 rpc = server->client;
582 if (!IS_ERR(rpc))
583 rpc_killall_tasks(rpc);
David Howellsf7b422b2006-06-09 09:34:33 -0400584}
585
586/*
Chuck Leverfc50d582007-07-01 12:12:46 -0400587 * Sanity-check a server address provided by the mount command
588 */
589static int nfs_verify_server_address(struct sockaddr *addr)
590{
591 switch (addr->sa_family) {
592 case AF_INET: {
593 struct sockaddr_in *sa = (struct sockaddr_in *) addr;
594 if (sa->sin_addr.s_addr != INADDR_ANY)
595 return 1;
596 break;
597 }
598 }
599
600 return 0;
601}
602
603/*
Chuck Leverbf0fd762007-07-01 12:13:44 -0400604 * Error-check and convert a string of mount options from user space into
605 * a data structure
606 */
607static int nfs_parse_mount_options(char *raw,
608 struct nfs_parsed_mount_data *mnt)
609{
610 char *p, *string;
611
612 if (!raw) {
613 dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
614 return 1;
615 }
616 dfprintk(MOUNT, "NFS: nfs mount opts='%s'\n", raw);
617
618 while ((p = strsep(&raw, ",")) != NULL) {
619 substring_t args[MAX_OPT_ARGS];
620 int option, token;
621
622 if (!*p)
623 continue;
624
625 dfprintk(MOUNT, "NFS: parsing nfs mount option '%s'\n", p);
626
627 token = match_token(p, nfs_mount_option_tokens, args);
628 switch (token) {
629 case Opt_soft:
630 mnt->flags |= NFS_MOUNT_SOFT;
631 break;
632 case Opt_hard:
633 mnt->flags &= ~NFS_MOUNT_SOFT;
634 break;
635 case Opt_intr:
636 mnt->flags |= NFS_MOUNT_INTR;
637 break;
638 case Opt_nointr:
639 mnt->flags &= ~NFS_MOUNT_INTR;
640 break;
641 case Opt_posix:
642 mnt->flags |= NFS_MOUNT_POSIX;
643 break;
644 case Opt_noposix:
645 mnt->flags &= ~NFS_MOUNT_POSIX;
646 break;
647 case Opt_cto:
648 mnt->flags &= ~NFS_MOUNT_NOCTO;
649 break;
650 case Opt_nocto:
651 mnt->flags |= NFS_MOUNT_NOCTO;
652 break;
653 case Opt_ac:
654 mnt->flags &= ~NFS_MOUNT_NOAC;
655 break;
656 case Opt_noac:
657 mnt->flags |= NFS_MOUNT_NOAC;
658 break;
659 case Opt_lock:
660 mnt->flags &= ~NFS_MOUNT_NONLM;
661 break;
662 case Opt_nolock:
663 mnt->flags |= NFS_MOUNT_NONLM;
664 break;
665 case Opt_v2:
666 mnt->flags &= ~NFS_MOUNT_VER3;
667 break;
668 case Opt_v3:
669 mnt->flags |= NFS_MOUNT_VER3;
670 break;
671 case Opt_udp:
672 mnt->flags &= ~NFS_MOUNT_TCP;
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -0400673 mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
Chuck Leverbf0fd762007-07-01 12:13:44 -0400674 mnt->timeo = 7;
675 mnt->retrans = 5;
676 break;
677 case Opt_tcp:
678 mnt->flags |= NFS_MOUNT_TCP;
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -0400679 mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
Chuck Leverbf0fd762007-07-01 12:13:44 -0400680 mnt->timeo = 600;
681 mnt->retrans = 2;
682 break;
683 case Opt_acl:
684 mnt->flags &= ~NFS_MOUNT_NOACL;
685 break;
686 case Opt_noacl:
687 mnt->flags |= NFS_MOUNT_NOACL;
688 break;
689 case Opt_rdirplus:
690 mnt->flags &= ~NFS_MOUNT_NORDIRPLUS;
691 break;
692 case Opt_nordirplus:
693 mnt->flags |= NFS_MOUNT_NORDIRPLUS;
694 break;
Trond Myklebust75180df2007-05-16 16:53:28 -0400695 case Opt_sharecache:
696 mnt->flags &= ~NFS_MOUNT_UNSHARED;
697 break;
698 case Opt_nosharecache:
699 mnt->flags |= NFS_MOUNT_UNSHARED;
700 break;
Chuck Leverbf0fd762007-07-01 12:13:44 -0400701
702 case Opt_port:
703 if (match_int(args, &option))
704 return 0;
705 if (option < 0 || option > 65535)
706 return 0;
Al Viro410896442007-07-22 10:59:06 +0100707 mnt->nfs_server.address.sin_port = htons(option);
Chuck Leverbf0fd762007-07-01 12:13:44 -0400708 break;
709 case Opt_rsize:
710 if (match_int(args, &mnt->rsize))
711 return 0;
712 break;
713 case Opt_wsize:
714 if (match_int(args, &mnt->wsize))
715 return 0;
716 break;
717 case Opt_bsize:
718 if (match_int(args, &option))
719 return 0;
720 if (option < 0)
721 return 0;
722 mnt->bsize = option;
723 break;
724 case Opt_timeo:
725 if (match_int(args, &mnt->timeo))
726 return 0;
727 break;
728 case Opt_retrans:
729 if (match_int(args, &mnt->retrans))
730 return 0;
731 break;
732 case Opt_acregmin:
733 if (match_int(args, &mnt->acregmin))
734 return 0;
735 break;
736 case Opt_acregmax:
737 if (match_int(args, &mnt->acregmax))
738 return 0;
739 break;
740 case Opt_acdirmin:
741 if (match_int(args, &mnt->acdirmin))
742 return 0;
743 break;
744 case Opt_acdirmax:
745 if (match_int(args, &mnt->acdirmax))
746 return 0;
747 break;
748 case Opt_actimeo:
749 if (match_int(args, &option))
750 return 0;
751 if (option < 0)
752 return 0;
753 mnt->acregmin =
754 mnt->acregmax =
755 mnt->acdirmin =
756 mnt->acdirmax = option;
757 break;
758 case Opt_namelen:
759 if (match_int(args, &mnt->namlen))
760 return 0;
761 break;
762 case Opt_mountport:
763 if (match_int(args, &option))
764 return 0;
765 if (option < 0 || option > 65535)
766 return 0;
767 mnt->mount_server.port = option;
768 break;
769 case Opt_mountprog:
770 if (match_int(args, &option))
771 return 0;
772 if (option < 0)
773 return 0;
774 mnt->mount_server.program = option;
775 break;
776 case Opt_mountvers:
777 if (match_int(args, &option))
778 return 0;
779 if (option < 0)
780 return 0;
781 mnt->mount_server.version = option;
782 break;
783 case Opt_nfsprog:
784 if (match_int(args, &option))
785 return 0;
786 if (option < 0)
787 return 0;
788 mnt->nfs_server.program = option;
789 break;
790 case Opt_nfsvers:
791 if (match_int(args, &option))
792 return 0;
793 switch (option) {
794 case 2:
795 mnt->flags &= ~NFS_MOUNT_VER3;
796 break;
797 case 3:
798 mnt->flags |= NFS_MOUNT_VER3;
799 break;
800 default:
801 goto out_unrec_vers;
802 }
803 break;
804
805 case Opt_sec:
806 string = match_strdup(args);
807 if (string == NULL)
808 goto out_nomem;
809 token = match_token(string, nfs_secflavor_tokens, args);
810 kfree(string);
811
812 /*
813 * The flags setting is for v2/v3. The flavor_len
814 * setting is for v4. v2/v3 also need to know the
815 * difference between NULL and UNIX.
816 */
817 switch (token) {
818 case Opt_sec_none:
819 mnt->flags &= ~NFS_MOUNT_SECFLAVOUR;
820 mnt->auth_flavor_len = 0;
821 mnt->auth_flavors[0] = RPC_AUTH_NULL;
822 break;
823 case Opt_sec_sys:
824 mnt->flags &= ~NFS_MOUNT_SECFLAVOUR;
825 mnt->auth_flavor_len = 0;
826 mnt->auth_flavors[0] = RPC_AUTH_UNIX;
827 break;
828 case Opt_sec_krb5:
829 mnt->flags |= NFS_MOUNT_SECFLAVOUR;
830 mnt->auth_flavor_len = 1;
831 mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5;
832 break;
833 case Opt_sec_krb5i:
834 mnt->flags |= NFS_MOUNT_SECFLAVOUR;
835 mnt->auth_flavor_len = 1;
836 mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I;
837 break;
838 case Opt_sec_krb5p:
839 mnt->flags |= NFS_MOUNT_SECFLAVOUR;
840 mnt->auth_flavor_len = 1;
841 mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P;
842 break;
843 case Opt_sec_lkey:
844 mnt->flags |= NFS_MOUNT_SECFLAVOUR;
845 mnt->auth_flavor_len = 1;
846 mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY;
847 break;
848 case Opt_sec_lkeyi:
849 mnt->flags |= NFS_MOUNT_SECFLAVOUR;
850 mnt->auth_flavor_len = 1;
851 mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI;
852 break;
853 case Opt_sec_lkeyp:
854 mnt->flags |= NFS_MOUNT_SECFLAVOUR;
855 mnt->auth_flavor_len = 1;
856 mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP;
857 break;
858 case Opt_sec_spkm:
859 mnt->flags |= NFS_MOUNT_SECFLAVOUR;
860 mnt->auth_flavor_len = 1;
861 mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM;
862 break;
863 case Opt_sec_spkmi:
864 mnt->flags |= NFS_MOUNT_SECFLAVOUR;
865 mnt->auth_flavor_len = 1;
866 mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI;
867 break;
868 case Opt_sec_spkmp:
869 mnt->flags |= NFS_MOUNT_SECFLAVOUR;
870 mnt->auth_flavor_len = 1;
871 mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP;
872 break;
873 default:
874 goto out_unrec_sec;
875 }
876 break;
877 case Opt_proto:
878 string = match_strdup(args);
879 if (string == NULL)
880 goto out_nomem;
881 token = match_token(string,
882 nfs_xprt_protocol_tokens, args);
883 kfree(string);
884
885 switch (token) {
Chuck Leverfdb66ff2007-08-29 17:58:57 -0400886 case Opt_xprt_udp:
Chuck Leverbf0fd762007-07-01 12:13:44 -0400887 mnt->flags &= ~NFS_MOUNT_TCP;
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -0400888 mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
Chuck Leverbf0fd762007-07-01 12:13:44 -0400889 mnt->timeo = 7;
890 mnt->retrans = 5;
891 break;
Chuck Leverfdb66ff2007-08-29 17:58:57 -0400892 case Opt_xprt_tcp:
Chuck Leverbf0fd762007-07-01 12:13:44 -0400893 mnt->flags |= NFS_MOUNT_TCP;
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -0400894 mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
Chuck Leverbf0fd762007-07-01 12:13:44 -0400895 mnt->timeo = 600;
896 mnt->retrans = 2;
897 break;
898 default:
899 goto out_unrec_xprt;
900 }
901 break;
902 case Opt_mountproto:
903 string = match_strdup(args);
904 if (string == NULL)
905 goto out_nomem;
906 token = match_token(string,
907 nfs_xprt_protocol_tokens, args);
908 kfree(string);
909
910 switch (token) {
Chuck Leverfdb66ff2007-08-29 17:58:57 -0400911 case Opt_xprt_udp:
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -0400912 mnt->mount_server.protocol = XPRT_TRANSPORT_UDP;
Chuck Leverbf0fd762007-07-01 12:13:44 -0400913 break;
Chuck Leverfdb66ff2007-08-29 17:58:57 -0400914 case Opt_xprt_tcp:
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -0400915 mnt->mount_server.protocol = XPRT_TRANSPORT_TCP;
Chuck Leverbf0fd762007-07-01 12:13:44 -0400916 break;
917 default:
918 goto out_unrec_xprt;
919 }
920 break;
921 case Opt_addr:
922 string = match_strdup(args);
923 if (string == NULL)
924 goto out_nomem;
925 mnt->nfs_server.address.sin_family = AF_INET;
926 mnt->nfs_server.address.sin_addr.s_addr =
927 in_aton(string);
928 kfree(string);
929 break;
930 case Opt_clientaddr:
931 string = match_strdup(args);
932 if (string == NULL)
933 goto out_nomem;
934 mnt->client_address = string;
935 break;
Chuck Lever0ac83772007-09-11 18:01:04 -0400936 case Opt_mountaddr:
Chuck Leverbf0fd762007-07-01 12:13:44 -0400937 string = match_strdup(args);
938 if (string == NULL)
939 goto out_nomem;
940 mnt->mount_server.address.sin_family = AF_INET;
941 mnt->mount_server.address.sin_addr.s_addr =
942 in_aton(string);
943 kfree(string);
944 break;
945
946 case Opt_userspace:
947 case Opt_deprecated:
948 break;
949
950 default:
951 goto out_unknown;
952 }
953 }
954
955 return 1;
956
957out_nomem:
958 printk(KERN_INFO "NFS: not enough memory to parse option\n");
959 return 0;
960
961out_unrec_vers:
962 printk(KERN_INFO "NFS: unrecognized NFS version number\n");
963 return 0;
964
965out_unrec_xprt:
966 printk(KERN_INFO "NFS: unrecognized transport protocol\n");
967 return 0;
968
969out_unrec_sec:
970 printk(KERN_INFO "NFS: unrecognized security flavor\n");
971 return 0;
972
973out_unknown:
974 printk(KERN_INFO "NFS: unknown mount option: %s\n", p);
975 return 0;
976}
977
978/*
Chuck Lever0076d7b2007-07-01 12:13:49 -0400979 * Use the remote server's MOUNT service to request the NFS file handle
980 * corresponding to the provided path.
981 */
982static int nfs_try_mount(struct nfs_parsed_mount_data *args,
983 struct nfs_fh *root_fh)
984{
985 struct sockaddr_in sin;
986 int status;
987
988 if (args->mount_server.version == 0) {
989 if (args->flags & NFS_MOUNT_VER3)
990 args->mount_server.version = NFS_MNT3_VERSION;
991 else
992 args->mount_server.version = NFS_MNT_VERSION;
993 }
994
995 /*
996 * Construct the mount server's address.
997 */
998 if (args->mount_server.address.sin_addr.s_addr != INADDR_ANY)
999 sin = args->mount_server.address;
1000 else
1001 sin = args->nfs_server.address;
James Lentiniaad70002007-09-24 17:32:49 -04001002 /*
1003 * autobind will be used if mount_server.port == 0
1004 */
1005 sin.sin_port = htons(args->mount_server.port);
Chuck Lever0076d7b2007-07-01 12:13:49 -04001006
1007 /*
1008 * Now ask the mount server to map our export path
1009 * to a file handle.
1010 */
1011 status = nfs_mount((struct sockaddr *) &sin,
1012 sizeof(sin),
1013 args->nfs_server.hostname,
1014 args->nfs_server.export_path,
1015 args->mount_server.version,
1016 args->mount_server.protocol,
1017 root_fh);
Chuck Leverefd83402007-09-11 18:00:58 -04001018 if (status == 0)
1019 return 0;
Chuck Lever0076d7b2007-07-01 12:13:49 -04001020
Chuck Leverefd83402007-09-11 18:00:58 -04001021 dfprintk(MOUNT, "NFS: unable to mount server " NIPQUAD_FMT
1022 ", error %d\n", NIPQUAD(sin.sin_addr.s_addr), status);
Chuck Lever0076d7b2007-07-01 12:13:49 -04001023 return status;
1024}
1025
1026/*
David Howells54ceac42006-08-22 20:06:13 -04001027 * Validate the NFS2/NFS3 mount data
1028 * - fills in the mount root filehandle
Chuck Lever136d5582007-07-01 12:13:54 -04001029 *
1030 * For option strings, user space handles the following behaviors:
1031 *
1032 * + DNS: mapping server host name to IP address ("addr=" option)
1033 *
1034 * + failure mode: how to behave if a mount request can't be handled
1035 * immediately ("fg/bg" option)
1036 *
1037 * + retry: how often to retry a mount request ("retry=" option)
1038 *
1039 * + breaking back: trying proto=udp after proto=tcp, v2 after v3,
1040 * mountproto=tcp after mountproto=udp, and so on
1041 *
1042 * XXX: as far as I can tell, changing the NFS program number is not
1043 * supported in the NFS client.
David Howellsf7b422b2006-06-09 09:34:33 -04001044 */
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001045static int nfs_validate_mount_data(void *options,
1046 struct nfs_parsed_mount_data *args,
Chuck Lever136d5582007-07-01 12:13:54 -04001047 struct nfs_fh *mntfh,
1048 const char *dev_name)
David Howellsf7b422b2006-06-09 09:34:33 -04001049{
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001050 struct nfs_mount_data *data = (struct nfs_mount_data *)options;
Chuck Lever136d5582007-07-01 12:13:54 -04001051
Chuck Lever5df36e72007-07-01 12:12:56 -04001052 if (data == NULL)
1053 goto out_no_data;
David Howells54ceac42006-08-22 20:06:13 -04001054
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001055 memset(args, 0, sizeof(*args));
1056 args->flags = (NFS_MOUNT_VER3 | NFS_MOUNT_TCP);
1057 args->rsize = NFS_MAX_FILE_IO_SIZE;
1058 args->wsize = NFS_MAX_FILE_IO_SIZE;
1059 args->timeo = 600;
1060 args->retrans = 2;
1061 args->acregmin = 3;
1062 args->acregmax = 60;
1063 args->acdirmin = 30;
1064 args->acdirmax = 60;
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -04001065 args->mount_server.protocol = XPRT_TRANSPORT_UDP;
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001066 args->mount_server.program = NFS_MNT_PROGRAM;
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -04001067 args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001068 args->nfs_server.program = NFS_PROGRAM;
1069
David Howellsf7b422b2006-06-09 09:34:33 -04001070 switch (data->version) {
Chuck Lever5df36e72007-07-01 12:12:56 -04001071 case 1:
1072 data->namlen = 0;
1073 case 2:
1074 data->bsize = 0;
1075 case 3:
1076 if (data->flags & NFS_MOUNT_VER3)
1077 goto out_no_v3;
1078 data->root.size = NFS2_FHSIZE;
1079 memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
1080 case 4:
1081 if (data->flags & NFS_MOUNT_SECFLAVOUR)
1082 goto out_no_sec;
1083 case 5:
1084 memset(data->context, 0, sizeof(data->context));
1085 case 6:
1086 if (data->flags & NFS_MOUNT_VER3)
1087 mntfh->size = data->root.size;
1088 else
1089 mntfh->size = NFS2_FHSIZE;
1090
1091 if (mntfh->size > sizeof(mntfh->data))
1092 goto out_invalid_fh;
1093
1094 memcpy(mntfh->data, data->root.data, mntfh->size);
1095 if (mntfh->size < sizeof(mntfh->data))
1096 memset(mntfh->data + mntfh->size, 0,
1097 sizeof(mntfh->data) - mntfh->size);
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001098 /*
1099 * Translate to nfs_parsed_mount_data, which nfs_fill_super
1100 * can deal with.
1101 */
1102 args->flags = data->flags;
1103 args->rsize = data->rsize;
1104 args->wsize = data->wsize;
1105 args->flags = data->flags;
1106 args->timeo = data->timeo;
1107 args->retrans = data->retrans;
1108 args->acregmin = data->acregmin;
1109 args->acregmax = data->acregmax;
1110 args->acdirmin = data->acdirmin;
1111 args->acdirmax = data->acdirmax;
1112 args->nfs_server.address = data->addr;
1113 if (!(data->flags & NFS_MOUNT_TCP))
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -04001114 args->nfs_server.protocol = XPRT_TRANSPORT_UDP;
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001115 /* N.B. caller will free nfs_server.hostname in all cases */
1116 args->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL);
1117 args->namlen = data->namlen;
1118 args->bsize = data->bsize;
1119 args->auth_flavors[0] = data->pseudoflavor;
Chuck Lever5df36e72007-07-01 12:12:56 -04001120 break;
Chuck Lever136d5582007-07-01 12:13:54 -04001121 default: {
1122 unsigned int len;
1123 char *c;
1124 int status;
Chuck Lever136d5582007-07-01 12:13:54 -04001125
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001126 if (nfs_parse_mount_options((char *)options, args) == 0)
Chuck Lever136d5582007-07-01 12:13:54 -04001127 return -EINVAL;
1128
Chuck Lever136d5582007-07-01 12:13:54 -04001129 c = strchr(dev_name, ':');
1130 if (c == NULL)
1131 return -EINVAL;
Chuck Lever350c73a2007-08-29 17:59:01 -04001132 len = c - dev_name;
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001133 /* N.B. caller will free nfs_server.hostname in all cases */
1134 args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL);
Chuck Lever136d5582007-07-01 12:13:54 -04001135
1136 c++;
1137 if (strlen(c) > NFS_MAXPATHLEN)
Chuck Lever7d1cca722007-08-29 17:59:03 -04001138 return -ENAMETOOLONG;
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001139 args->nfs_server.export_path = c;
Chuck Lever136d5582007-07-01 12:13:54 -04001140
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001141 status = nfs_try_mount(args, mntfh);
Chuck Lever136d5582007-07-01 12:13:54 -04001142 if (status)
Chuck Leverfdc6e2c2007-08-29 17:58:59 -04001143 return status;
Chuck Lever136d5582007-07-01 12:13:54 -04001144
Chuck Lever136d5582007-07-01 12:13:54 -04001145 break;
1146 }
David Howellsf7b422b2006-06-09 09:34:33 -04001147 }
David Howells54ceac42006-08-22 20:06:13 -04001148
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001149 if (!(args->flags & NFS_MOUNT_SECFLAVOUR))
1150 args->auth_flavors[0] = RPC_AUTH_UNIX;
Trond Myklebust36b15c52006-08-22 20:06:14 -04001151
David Howellsf7b422b2006-06-09 09:34:33 -04001152#ifndef CONFIG_NFS_V3
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001153 if (args->flags & NFS_MOUNT_VER3)
Chuck Lever5df36e72007-07-01 12:12:56 -04001154 goto out_v3_not_compiled;
1155#endif /* !CONFIG_NFS_V3 */
David Howells54ceac42006-08-22 20:06:13 -04001156
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001157 if (!nfs_verify_server_address((struct sockaddr *)
1158 &args->nfs_server.address))
Chuck Lever5df36e72007-07-01 12:12:56 -04001159 goto out_no_address;
David Howells54ceac42006-08-22 20:06:13 -04001160
1161 return 0;
Chuck Lever5df36e72007-07-01 12:12:56 -04001162
1163out_no_data:
1164 dfprintk(MOUNT, "NFS: mount program didn't pass any mount data\n");
1165 return -EINVAL;
1166
1167out_no_v3:
1168 dfprintk(MOUNT, "NFS: nfs_mount_data version %d does not support v3\n",
1169 data->version);
1170 return -EINVAL;
1171
1172out_no_sec:
1173 dfprintk(MOUNT, "NFS: nfs_mount_data version supports only AUTH_SYS\n");
1174 return -EINVAL;
1175
Chuck Lever5df36e72007-07-01 12:12:56 -04001176#ifndef CONFIG_NFS_V3
1177out_v3_not_compiled:
1178 dfprintk(MOUNT, "NFS: NFSv3 is not compiled into kernel\n");
1179 return -EPROTONOSUPPORT;
1180#endif /* !CONFIG_NFS_V3 */
1181
1182out_no_address:
1183 dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n");
1184 return -EINVAL;
1185
1186out_invalid_fh:
1187 dfprintk(MOUNT, "NFS: invalid root filehandle\n");
1188 return -EINVAL;
David Howells54ceac42006-08-22 20:06:13 -04001189}
1190
1191/*
1192 * Initialise the common bits of the superblock
1193 */
1194static inline void nfs_initialise_sb(struct super_block *sb)
1195{
1196 struct nfs_server *server = NFS_SB(sb);
1197
1198 sb->s_magic = NFS_SUPER_MAGIC;
1199
1200 /* We probably want something more informative here */
1201 snprintf(sb->s_id, sizeof(sb->s_id),
1202 "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev));
1203
1204 if (sb->s_blocksize == 0)
1205 sb->s_blocksize = nfs_block_bits(server->wsize,
1206 &sb->s_blocksize_bits);
1207
1208 if (server->flags & NFS_MOUNT_NOAC)
1209 sb->s_flags |= MS_SYNCHRONOUS;
1210
1211 nfs_super_set_maxbytes(sb, server->maxfilesize);
1212}
1213
1214/*
1215 * Finish setting up an NFS2/3 superblock
1216 */
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001217static void nfs_fill_super(struct super_block *sb,
1218 struct nfs_parsed_mount_data *data)
David Howells54ceac42006-08-22 20:06:13 -04001219{
1220 struct nfs_server *server = NFS_SB(sb);
1221
1222 sb->s_blocksize_bits = 0;
1223 sb->s_blocksize = 0;
1224 if (data->bsize)
1225 sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
1226
1227 if (server->flags & NFS_MOUNT_VER3) {
1228 /* The VFS shouldn't apply the umask to mode bits. We will do
1229 * so ourselves when necessary.
1230 */
1231 sb->s_flags |= MS_POSIXACL;
1232 sb->s_time_gran = 1;
1233 }
1234
1235 sb->s_op = &nfs_sops;
1236 nfs_initialise_sb(sb);
1237}
1238
1239/*
1240 * Finish setting up a cloned NFS2/3 superblock
1241 */
1242static void nfs_clone_super(struct super_block *sb,
1243 const struct super_block *old_sb)
1244{
1245 struct nfs_server *server = NFS_SB(sb);
1246
1247 sb->s_blocksize_bits = old_sb->s_blocksize_bits;
1248 sb->s_blocksize = old_sb->s_blocksize;
1249 sb->s_maxbytes = old_sb->s_maxbytes;
1250
1251 if (server->flags & NFS_MOUNT_VER3) {
1252 /* The VFS shouldn't apply the umask to mode bits. We will do
1253 * so ourselves when necessary.
1254 */
1255 sb->s_flags |= MS_POSIXACL;
1256 sb->s_time_gran = 1;
1257 }
1258
1259 sb->s_op = old_sb->s_op;
1260 nfs_initialise_sb(sb);
1261}
1262
Trond Myklebust275a5d22007-05-16 16:53:28 -04001263#define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS)
1264
1265static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags)
1266{
1267 const struct nfs_server *a = s->s_fs_info;
1268 const struct rpc_clnt *clnt_a = a->client;
1269 const struct rpc_clnt *clnt_b = b->client;
1270
1271 if ((s->s_flags & NFS_MS_MASK) != (flags & NFS_MS_MASK))
1272 goto Ebusy;
1273 if (a->nfs_client != b->nfs_client)
1274 goto Ebusy;
1275 if (a->flags != b->flags)
1276 goto Ebusy;
1277 if (a->wsize != b->wsize)
1278 goto Ebusy;
1279 if (a->rsize != b->rsize)
1280 goto Ebusy;
1281 if (a->acregmin != b->acregmin)
1282 goto Ebusy;
1283 if (a->acregmax != b->acregmax)
1284 goto Ebusy;
1285 if (a->acdirmin != b->acdirmin)
1286 goto Ebusy;
1287 if (a->acdirmax != b->acdirmax)
1288 goto Ebusy;
1289 if (clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor)
1290 goto Ebusy;
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001291 return 1;
Trond Myklebust275a5d22007-05-16 16:53:28 -04001292Ebusy:
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001293 return 0;
1294}
1295
1296struct nfs_sb_mountdata {
1297 struct nfs_server *server;
1298 int mntflags;
1299};
1300
1301static int nfs_set_super(struct super_block *s, void *data)
1302{
1303 struct nfs_sb_mountdata *sb_mntdata = data;
1304 struct nfs_server *server = sb_mntdata->server;
1305 int ret;
1306
1307 s->s_flags = sb_mntdata->mntflags;
1308 s->s_fs_info = server;
1309 ret = set_anon_super(s, server);
1310 if (ret == 0)
1311 server->s_dev = s->s_dev;
1312 return ret;
1313}
1314
1315static int nfs_compare_super(struct super_block *sb, void *data)
1316{
1317 struct nfs_sb_mountdata *sb_mntdata = data;
1318 struct nfs_server *server = sb_mntdata->server, *old = NFS_SB(sb);
1319 int mntflags = sb_mntdata->mntflags;
1320
1321 if (memcmp(&old->nfs_client->cl_addr,
1322 &server->nfs_client->cl_addr,
1323 sizeof(old->nfs_client->cl_addr)) != 0)
1324 return 0;
1325 /* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */
1326 if (old->flags & NFS_MOUNT_UNSHARED)
1327 return 0;
1328 if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0)
1329 return 0;
1330 return nfs_compare_mount_options(sb, server, mntflags);
Trond Myklebust275a5d22007-05-16 16:53:28 -04001331}
1332
David Howells54ceac42006-08-22 20:06:13 -04001333static int nfs_get_sb(struct file_system_type *fs_type,
1334 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
1335{
1336 struct nfs_server *server = NULL;
1337 struct super_block *s;
1338 struct nfs_fh mntfh;
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001339 struct nfs_parsed_mount_data data;
David Howells54ceac42006-08-22 20:06:13 -04001340 struct dentry *mntroot;
Trond Myklebust75180df2007-05-16 16:53:28 -04001341 int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001342 struct nfs_sb_mountdata sb_mntdata = {
1343 .mntflags = flags,
1344 };
David Howells54ceac42006-08-22 20:06:13 -04001345 int error;
1346
1347 /* Validate the mount data */
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001348 error = nfs_validate_mount_data(raw_data, &data, &mntfh, dev_name);
David Howells54ceac42006-08-22 20:06:13 -04001349 if (error < 0)
Chuck Lever06559602007-07-01 12:12:35 -04001350 goto out;
David Howells54ceac42006-08-22 20:06:13 -04001351
1352 /* Get a volume representation */
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001353 server = nfs_create_server(&data, &mntfh);
David Howells54ceac42006-08-22 20:06:13 -04001354 if (IS_ERR(server)) {
1355 error = PTR_ERR(server);
Chuck Lever06559602007-07-01 12:12:35 -04001356 goto out;
David Howells54ceac42006-08-22 20:06:13 -04001357 }
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001358 sb_mntdata.server = server;
David Howells54ceac42006-08-22 20:06:13 -04001359
Trond Myklebust75180df2007-05-16 16:53:28 -04001360 if (server->flags & NFS_MOUNT_UNSHARED)
1361 compare_super = NULL;
1362
David Howells54ceac42006-08-22 20:06:13 -04001363 /* Get a superblock - note that we may end up sharing one that already exists */
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001364 s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata);
Trond Myklebust816724e2006-06-24 08:41:41 -04001365 if (IS_ERR(s)) {
1366 error = PTR_ERR(s);
David Howells54ceac42006-08-22 20:06:13 -04001367 goto out_err_nosb;
Trond Myklebust816724e2006-06-24 08:41:41 -04001368 }
1369
David Howells54ceac42006-08-22 20:06:13 -04001370 if (s->s_fs_info != server) {
1371 nfs_free_server(server);
1372 server = NULL;
David Howellsf7b422b2006-06-09 09:34:33 -04001373 }
David Howells54ceac42006-08-22 20:06:13 -04001374
1375 if (!s->s_root) {
1376 /* initial superblock/root creation */
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001377 nfs_fill_super(s, &data);
David Howells54ceac42006-08-22 20:06:13 -04001378 }
1379
1380 mntroot = nfs_get_root(s, &mntfh);
1381 if (IS_ERR(mntroot)) {
1382 error = PTR_ERR(mntroot);
1383 goto error_splat_super;
1384 }
1385
David Howellsf7b422b2006-06-09 09:34:33 -04001386 s->s_flags |= MS_ACTIVE;
David Howells54ceac42006-08-22 20:06:13 -04001387 mnt->mnt_sb = s;
1388 mnt->mnt_root = mntroot;
Chuck Lever06559602007-07-01 12:12:35 -04001389 error = 0;
1390
1391out:
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -04001392 kfree(data.nfs_server.hostname);
Chuck Lever06559602007-07-01 12:12:35 -04001393 return error;
Trond Myklebust816724e2006-06-24 08:41:41 -04001394
David Howells54ceac42006-08-22 20:06:13 -04001395out_err_nosb:
1396 nfs_free_server(server);
Chuck Lever06559602007-07-01 12:12:35 -04001397 goto out;
David Howells54ceac42006-08-22 20:06:13 -04001398
1399error_splat_super:
1400 up_write(&s->s_umount);
1401 deactivate_super(s);
Chuck Lever06559602007-07-01 12:12:35 -04001402 goto out;
David Howellsf7b422b2006-06-09 09:34:33 -04001403}
1404
David Howells54ceac42006-08-22 20:06:13 -04001405/*
1406 * Destroy an NFS2/3 superblock
1407 */
David Howellsf7b422b2006-06-09 09:34:33 -04001408static void nfs_kill_super(struct super_block *s)
1409{
1410 struct nfs_server *server = NFS_SB(s);
1411
1412 kill_anon_super(s);
David Howells54ceac42006-08-22 20:06:13 -04001413 nfs_free_server(server);
David Howellsf7b422b2006-06-09 09:34:33 -04001414}
1415
David Howells54ceac42006-08-22 20:06:13 -04001416/*
1417 * Clone an NFS2/3 server record on xdev traversal (FSID-change)
1418 */
1419static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags,
1420 const char *dev_name, void *raw_data,
1421 struct vfsmount *mnt)
David Howellsf7b422b2006-06-09 09:34:33 -04001422{
1423 struct nfs_clone_mount *data = raw_data;
David Howells54ceac42006-08-22 20:06:13 -04001424 struct super_block *s;
1425 struct nfs_server *server;
1426 struct dentry *mntroot;
Trond Myklebust75180df2007-05-16 16:53:28 -04001427 int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001428 struct nfs_sb_mountdata sb_mntdata = {
1429 .mntflags = flags,
1430 };
David Howells54ceac42006-08-22 20:06:13 -04001431 int error;
1432
1433 dprintk("--> nfs_xdev_get_sb()\n");
1434
1435 /* create a new volume representation */
1436 server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr);
1437 if (IS_ERR(server)) {
1438 error = PTR_ERR(server);
1439 goto out_err_noserver;
1440 }
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001441 sb_mntdata.server = server;
David Howells54ceac42006-08-22 20:06:13 -04001442
Trond Myklebust75180df2007-05-16 16:53:28 -04001443 if (server->flags & NFS_MOUNT_UNSHARED)
1444 compare_super = NULL;
1445
David Howells54ceac42006-08-22 20:06:13 -04001446 /* Get a superblock - note that we may end up sharing one that already exists */
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001447 s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata);
David Howells54ceac42006-08-22 20:06:13 -04001448 if (IS_ERR(s)) {
1449 error = PTR_ERR(s);
1450 goto out_err_nosb;
1451 }
1452
1453 if (s->s_fs_info != server) {
1454 nfs_free_server(server);
1455 server = NULL;
1456 }
1457
1458 if (!s->s_root) {
1459 /* initial superblock/root creation */
David Howells54ceac42006-08-22 20:06:13 -04001460 nfs_clone_super(s, data->sb);
1461 }
1462
1463 mntroot = nfs_get_root(s, data->fh);
1464 if (IS_ERR(mntroot)) {
1465 error = PTR_ERR(mntroot);
1466 goto error_splat_super;
1467 }
1468
1469 s->s_flags |= MS_ACTIVE;
1470 mnt->mnt_sb = s;
1471 mnt->mnt_root = mntroot;
1472
1473 dprintk("<-- nfs_xdev_get_sb() = 0\n");
1474 return 0;
1475
1476out_err_nosb:
1477 nfs_free_server(server);
1478out_err_noserver:
1479 dprintk("<-- nfs_xdev_get_sb() = %d [error]\n", error);
1480 return error;
1481
1482error_splat_super:
1483 up_write(&s->s_umount);
1484 deactivate_super(s);
1485 dprintk("<-- nfs_xdev_get_sb() = %d [splat]\n", error);
1486 return error;
David Howellsf7b422b2006-06-09 09:34:33 -04001487}
1488
1489#ifdef CONFIG_NFS_V4
David Howells54ceac42006-08-22 20:06:13 -04001490
1491/*
1492 * Finish setting up a cloned NFS4 superblock
1493 */
1494static void nfs4_clone_super(struct super_block *sb,
1495 const struct super_block *old_sb)
David Howellsf7b422b2006-06-09 09:34:33 -04001496{
David Howells54ceac42006-08-22 20:06:13 -04001497 sb->s_blocksize_bits = old_sb->s_blocksize_bits;
1498 sb->s_blocksize = old_sb->s_blocksize;
1499 sb->s_maxbytes = old_sb->s_maxbytes;
1500 sb->s_time_gran = 1;
1501 sb->s_op = old_sb->s_op;
1502 nfs_initialise_sb(sb);
David Howellsf7b422b2006-06-09 09:34:33 -04001503}
1504
1505/*
1506 * Set up an NFS4 superblock
1507 */
David Howells54ceac42006-08-22 20:06:13 -04001508static void nfs4_fill_super(struct super_block *sb)
David Howellsf7b422b2006-06-09 09:34:33 -04001509{
David Howellsf7b422b2006-06-09 09:34:33 -04001510 sb->s_time_gran = 1;
David Howellsf7b422b2006-06-09 09:34:33 -04001511 sb->s_op = &nfs4_sops;
David Howells54ceac42006-08-22 20:06:13 -04001512 nfs_initialise_sb(sb);
David Howellsf7b422b2006-06-09 09:34:33 -04001513}
1514
David Howells54ceac42006-08-22 20:06:13 -04001515/*
Chuck Leverf0768eb2007-07-01 12:13:01 -04001516 * Validate NFSv4 mount options
1517 */
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001518static int nfs4_validate_mount_data(void *options,
1519 struct nfs_parsed_mount_data *args,
1520 const char *dev_name)
Chuck Leverf0768eb2007-07-01 12:13:01 -04001521{
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001522 struct nfs4_mount_data *data = (struct nfs4_mount_data *)options;
Chuck Leverf0768eb2007-07-01 12:13:01 -04001523 char *c;
1524
1525 if (data == NULL)
1526 goto out_no_data;
1527
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001528 memset(args, 0, sizeof(*args));
1529 args->rsize = NFS_MAX_FILE_IO_SIZE;
1530 args->wsize = NFS_MAX_FILE_IO_SIZE;
1531 args->timeo = 600;
1532 args->retrans = 2;
1533 args->acregmin = 3;
1534 args->acregmax = 60;
1535 args->acdirmin = 30;
1536 args->acdirmax = 60;
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -04001537 args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001538
Chuck Leverf0768eb2007-07-01 12:13:01 -04001539 switch (data->version) {
1540 case 1:
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001541 if (data->host_addrlen != sizeof(args->nfs_server.address))
Chuck Leverf0768eb2007-07-01 12:13:01 -04001542 goto out_no_address;
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001543 if (copy_from_user(&args->nfs_server.address,
1544 data->host_addr,
Trond Myklebust20c71f52007-09-20 20:23:51 -04001545 sizeof(args->nfs_server.address)))
Chuck Leverf0768eb2007-07-01 12:13:01 -04001546 return -EFAULT;
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001547 if (args->nfs_server.address.sin_port == 0)
1548 args->nfs_server.address.sin_port = htons(NFS_PORT);
1549 if (!nfs_verify_server_address((struct sockaddr *)
1550 &args->nfs_server.address))
Chuck Leverf0768eb2007-07-01 12:13:01 -04001551 goto out_no_address;
1552
1553 switch (data->auth_flavourlen) {
1554 case 0:
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001555 args->auth_flavors[0] = RPC_AUTH_UNIX;
Chuck Leverf0768eb2007-07-01 12:13:01 -04001556 break;
1557 case 1:
Trond Myklebust20c71f52007-09-20 20:23:51 -04001558 if (copy_from_user(&args->auth_flavors[0],
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001559 data->auth_flavours,
Trond Myklebust20c71f52007-09-20 20:23:51 -04001560 sizeof(args->auth_flavors[0])))
Chuck Leverf0768eb2007-07-01 12:13:01 -04001561 return -EFAULT;
1562 break;
1563 default:
1564 goto out_inval_auth;
1565 }
1566
1567 c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
1568 if (IS_ERR(c))
1569 return PTR_ERR(c);
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001570 args->nfs_server.hostname = c;
Chuck Leverf0768eb2007-07-01 12:13:01 -04001571
1572 c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN);
1573 if (IS_ERR(c))
1574 return PTR_ERR(c);
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001575 args->nfs_server.export_path = c;
1576 dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", c);
Chuck Leverf0768eb2007-07-01 12:13:01 -04001577
1578 c = strndup_user(data->client_addr.data, 16);
1579 if (IS_ERR(c))
1580 return PTR_ERR(c);
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001581 args->client_address = c;
1582
1583 /*
1584 * Translate to nfs_parsed_mount_data, which nfs4_fill_super
1585 * can deal with.
1586 */
1587
1588 args->flags = data->flags & NFS4_MOUNT_FLAGMASK;
1589 args->rsize = data->rsize;
1590 args->wsize = data->wsize;
1591 args->timeo = data->timeo;
1592 args->retrans = data->retrans;
1593 args->acregmin = data->acregmin;
1594 args->acregmax = data->acregmax;
1595 args->acdirmin = data->acdirmin;
1596 args->acdirmax = data->acdirmax;
1597 args->nfs_server.protocol = data->proto;
Chuck Leverf0768eb2007-07-01 12:13:01 -04001598
1599 break;
Chuck Lever80071222007-07-01 12:13:59 -04001600 default: {
1601 unsigned int len;
Chuck Lever80071222007-07-01 12:13:59 -04001602
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001603 if (nfs_parse_mount_options((char *)options, args) == 0)
Chuck Lever80071222007-07-01 12:13:59 -04001604 return -EINVAL;
1605
1606 if (!nfs_verify_server_address((struct sockaddr *)
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001607 &args->nfs_server.address))
Chuck Lever80071222007-07-01 12:13:59 -04001608 return -EINVAL;
Chuck Lever80071222007-07-01 12:13:59 -04001609
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001610 switch (args->auth_flavor_len) {
Chuck Lever80071222007-07-01 12:13:59 -04001611 case 0:
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001612 args->auth_flavors[0] = RPC_AUTH_UNIX;
Chuck Lever80071222007-07-01 12:13:59 -04001613 break;
1614 case 1:
Chuck Lever80071222007-07-01 12:13:59 -04001615 break;
1616 default:
1617 goto out_inval_auth;
1618 }
1619
1620 /*
Chuck Lever80071222007-07-01 12:13:59 -04001621 * Split "dev_name" into "hostname:mntpath".
1622 */
1623 c = strchr(dev_name, ':');
1624 if (c == NULL)
1625 return -EINVAL;
1626 /* while calculating len, pretend ':' is '\0' */
1627 len = c - dev_name;
1628 if (len > NFS4_MAXNAMLEN)
Chuck Lever7d1cca722007-08-29 17:59:03 -04001629 return -ENAMETOOLONG;
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001630 args->nfs_server.hostname = kzalloc(len, GFP_KERNEL);
1631 if (args->nfs_server.hostname == NULL)
Chuck Lever80071222007-07-01 12:13:59 -04001632 return -ENOMEM;
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001633 strncpy(args->nfs_server.hostname, dev_name, len - 1);
Chuck Lever80071222007-07-01 12:13:59 -04001634
1635 c++; /* step over the ':' */
1636 len = strlen(c);
1637 if (len > NFS4_MAXPATHLEN)
Chuck Lever7d1cca722007-08-29 17:59:03 -04001638 return -ENAMETOOLONG;
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001639 args->nfs_server.export_path = kzalloc(len + 1, GFP_KERNEL);
1640 if (args->nfs_server.export_path == NULL)
Chuck Lever80071222007-07-01 12:13:59 -04001641 return -ENOMEM;
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001642 strncpy(args->nfs_server.export_path, c, len);
Chuck Lever80071222007-07-01 12:13:59 -04001643
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001644 dprintk("MNTPATH: %s\n", args->nfs_server.export_path);
Chuck Lever80071222007-07-01 12:13:59 -04001645
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001646 if (args->client_address == NULL)
Jeff Layton0a87cf12007-07-18 11:28:43 -04001647 goto out_no_client_address;
1648
Chuck Lever80071222007-07-01 12:13:59 -04001649 break;
1650 }
Chuck Leverf0768eb2007-07-01 12:13:01 -04001651 }
1652
1653 return 0;
1654
1655out_no_data:
1656 dfprintk(MOUNT, "NFS4: mount program didn't pass any mount data\n");
1657 return -EINVAL;
1658
1659out_inval_auth:
1660 dfprintk(MOUNT, "NFS4: Invalid number of RPC auth flavours %d\n",
1661 data->auth_flavourlen);
1662 return -EINVAL;
1663
1664out_no_address:
1665 dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
1666 return -EINVAL;
Jeff Layton0a87cf12007-07-18 11:28:43 -04001667
1668out_no_client_address:
1669 dfprintk(MOUNT, "NFS4: mount program didn't pass callback address\n");
1670 return -EINVAL;
Chuck Leverf0768eb2007-07-01 12:13:01 -04001671}
1672
1673/*
David Howells54ceac42006-08-22 20:06:13 -04001674 * Get the superblock for an NFS4 mountpoint
1675 */
Trond Myklebust816724e2006-06-24 08:41:41 -04001676static int nfs4_get_sb(struct file_system_type *fs_type,
1677 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
David Howellsf7b422b2006-06-09 09:34:33 -04001678{
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001679 struct nfs_parsed_mount_data data;
David Howells54ceac42006-08-22 20:06:13 -04001680 struct super_block *s;
1681 struct nfs_server *server;
David Howells54ceac42006-08-22 20:06:13 -04001682 struct nfs_fh mntfh;
1683 struct dentry *mntroot;
Trond Myklebust75180df2007-05-16 16:53:28 -04001684 int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001685 struct nfs_sb_mountdata sb_mntdata = {
1686 .mntflags = flags,
1687 };
David Howells54ceac42006-08-22 20:06:13 -04001688 int error;
David Howellsf7b422b2006-06-09 09:34:33 -04001689
Chuck Leverf0768eb2007-07-01 12:13:01 -04001690 /* Validate the mount data */
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001691 error = nfs4_validate_mount_data(raw_data, &data, dev_name);
Chuck Leverf0768eb2007-07-01 12:13:01 -04001692 if (error < 0)
1693 goto out;
David Howellsf7b422b2006-06-09 09:34:33 -04001694
David Howells54ceac42006-08-22 20:06:13 -04001695 /* Get a volume representation */
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001696 server = nfs4_create_server(&data, &mntfh);
David Howells54ceac42006-08-22 20:06:13 -04001697 if (IS_ERR(server)) {
1698 error = PTR_ERR(server);
Chuck Lever29eb9812007-07-01 12:12:30 -04001699 goto out;
David Howellsf7b422b2006-06-09 09:34:33 -04001700 }
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001701 sb_mntdata.server = server;
David Howellsf7b422b2006-06-09 09:34:33 -04001702
Trond Myklebust75180df2007-05-16 16:53:28 -04001703 if (server->flags & NFS4_MOUNT_UNSHARED)
1704 compare_super = NULL;
1705
David Howells54ceac42006-08-22 20:06:13 -04001706 /* Get a superblock - note that we may end up sharing one that already exists */
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001707 s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata);
Trond Myklebust816724e2006-06-24 08:41:41 -04001708 if (IS_ERR(s)) {
1709 error = PTR_ERR(s);
David Howellsf7b422b2006-06-09 09:34:33 -04001710 goto out_free;
Trond Myklebust816724e2006-06-24 08:41:41 -04001711 }
1712
Trond Myklebust5dd31772006-08-24 01:03:05 -04001713 if (s->s_fs_info != server) {
1714 nfs_free_server(server);
1715 server = NULL;
1716 }
1717
David Howells54ceac42006-08-22 20:06:13 -04001718 if (!s->s_root) {
1719 /* initial superblock/root creation */
David Howells54ceac42006-08-22 20:06:13 -04001720 nfs4_fill_super(s);
Trond Myklebust816724e2006-06-24 08:41:41 -04001721 }
David Howellsf7b422b2006-06-09 09:34:33 -04001722
David Howells54ceac42006-08-22 20:06:13 -04001723 mntroot = nfs4_get_root(s, &mntfh);
1724 if (IS_ERR(mntroot)) {
1725 error = PTR_ERR(mntroot);
1726 goto error_splat_super;
David Howellsf7b422b2006-06-09 09:34:33 -04001727 }
David Howells54ceac42006-08-22 20:06:13 -04001728
David Howellsf7b422b2006-06-09 09:34:33 -04001729 s->s_flags |= MS_ACTIVE;
David Howells54ceac42006-08-22 20:06:13 -04001730 mnt->mnt_sb = s;
1731 mnt->mnt_root = mntroot;
Chuck Lever29eb9812007-07-01 12:12:30 -04001732 error = 0;
David Howells54ceac42006-08-22 20:06:13 -04001733
Chuck Lever29eb9812007-07-01 12:12:30 -04001734out:
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001735 kfree(data.client_address);
1736 kfree(data.nfs_server.export_path);
1737 kfree(data.nfs_server.hostname);
Trond Myklebust816724e2006-06-24 08:41:41 -04001738 return error;
David Howells54ceac42006-08-22 20:06:13 -04001739
Chuck Lever29eb9812007-07-01 12:12:30 -04001740out_free:
1741 nfs_free_server(server);
1742 goto out;
1743
David Howells54ceac42006-08-22 20:06:13 -04001744error_splat_super:
1745 up_write(&s->s_umount);
1746 deactivate_super(s);
Chuck Lever29eb9812007-07-01 12:12:30 -04001747 goto out;
David Howellsf7b422b2006-06-09 09:34:33 -04001748}
1749
1750static void nfs4_kill_super(struct super_block *sb)
1751{
1752 struct nfs_server *server = NFS_SB(sb);
1753
1754 nfs_return_all_delegations(sb);
1755 kill_anon_super(sb);
1756
1757 nfs4_renewd_prepare_shutdown(server);
David Howells54ceac42006-08-22 20:06:13 -04001758 nfs_free_server(server);
David Howellsf7b422b2006-06-09 09:34:33 -04001759}
1760
1761/*
David Howells54ceac42006-08-22 20:06:13 -04001762 * Clone an NFS4 server record on xdev traversal (FSID-change)
David Howellsf7b422b2006-06-09 09:34:33 -04001763 */
David Howells54ceac42006-08-22 20:06:13 -04001764static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags,
1765 const char *dev_name, void *raw_data,
1766 struct vfsmount *mnt)
David Howellsf7b422b2006-06-09 09:34:33 -04001767{
1768 struct nfs_clone_mount *data = raw_data;
David Howells54ceac42006-08-22 20:06:13 -04001769 struct super_block *s;
1770 struct nfs_server *server;
1771 struct dentry *mntroot;
Trond Myklebust75180df2007-05-16 16:53:28 -04001772 int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001773 struct nfs_sb_mountdata sb_mntdata = {
1774 .mntflags = flags,
1775 };
David Howells54ceac42006-08-22 20:06:13 -04001776 int error;
1777
1778 dprintk("--> nfs4_xdev_get_sb()\n");
1779
1780 /* create a new volume representation */
1781 server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr);
1782 if (IS_ERR(server)) {
1783 error = PTR_ERR(server);
1784 goto out_err_noserver;
1785 }
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001786 sb_mntdata.server = server;
David Howells54ceac42006-08-22 20:06:13 -04001787
Trond Myklebust75180df2007-05-16 16:53:28 -04001788 if (server->flags & NFS4_MOUNT_UNSHARED)
1789 compare_super = NULL;
1790
David Howells54ceac42006-08-22 20:06:13 -04001791 /* Get a superblock - note that we may end up sharing one that already exists */
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001792 s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata);
David Howells54ceac42006-08-22 20:06:13 -04001793 if (IS_ERR(s)) {
1794 error = PTR_ERR(s);
1795 goto out_err_nosb;
1796 }
1797
1798 if (s->s_fs_info != server) {
1799 nfs_free_server(server);
1800 server = NULL;
1801 }
1802
1803 if (!s->s_root) {
1804 /* initial superblock/root creation */
David Howells54ceac42006-08-22 20:06:13 -04001805 nfs4_clone_super(s, data->sb);
1806 }
1807
1808 mntroot = nfs4_get_root(s, data->fh);
1809 if (IS_ERR(mntroot)) {
1810 error = PTR_ERR(mntroot);
1811 goto error_splat_super;
1812 }
1813
1814 s->s_flags |= MS_ACTIVE;
1815 mnt->mnt_sb = s;
1816 mnt->mnt_root = mntroot;
1817
1818 dprintk("<-- nfs4_xdev_get_sb() = 0\n");
1819 return 0;
1820
1821out_err_nosb:
1822 nfs_free_server(server);
1823out_err_noserver:
1824 dprintk("<-- nfs4_xdev_get_sb() = %d [error]\n", error);
1825 return error;
1826
1827error_splat_super:
1828 up_write(&s->s_umount);
1829 deactivate_super(s);
1830 dprintk("<-- nfs4_xdev_get_sb() = %d [splat]\n", error);
1831 return error;
David Howellsf7b422b2006-06-09 09:34:33 -04001832}
1833
David Howells54ceac42006-08-22 20:06:13 -04001834/*
1835 * Create an NFS4 server record on referral traversal
1836 */
1837static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags,
1838 const char *dev_name, void *raw_data,
1839 struct vfsmount *mnt)
David Howellsf7b422b2006-06-09 09:34:33 -04001840{
1841 struct nfs_clone_mount *data = raw_data;
David Howells54ceac42006-08-22 20:06:13 -04001842 struct super_block *s;
1843 struct nfs_server *server;
1844 struct dentry *mntroot;
1845 struct nfs_fh mntfh;
Trond Myklebust75180df2007-05-16 16:53:28 -04001846 int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001847 struct nfs_sb_mountdata sb_mntdata = {
1848 .mntflags = flags,
1849 };
David Howells54ceac42006-08-22 20:06:13 -04001850 int error;
1851
1852 dprintk("--> nfs4_referral_get_sb()\n");
1853
1854 /* create a new volume representation */
1855 server = nfs4_create_referral_server(data, &mntfh);
1856 if (IS_ERR(server)) {
1857 error = PTR_ERR(server);
1858 goto out_err_noserver;
1859 }
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001860 sb_mntdata.server = server;
David Howells54ceac42006-08-22 20:06:13 -04001861
Trond Myklebust75180df2007-05-16 16:53:28 -04001862 if (server->flags & NFS4_MOUNT_UNSHARED)
1863 compare_super = NULL;
1864
David Howells54ceac42006-08-22 20:06:13 -04001865 /* Get a superblock - note that we may end up sharing one that already exists */
Trond Myklebuste89a5a42007-08-31 10:45:17 -04001866 s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata);
David Howells54ceac42006-08-22 20:06:13 -04001867 if (IS_ERR(s)) {
1868 error = PTR_ERR(s);
1869 goto out_err_nosb;
1870 }
1871
1872 if (s->s_fs_info != server) {
1873 nfs_free_server(server);
1874 server = NULL;
1875 }
1876
1877 if (!s->s_root) {
1878 /* initial superblock/root creation */
David Howells54ceac42006-08-22 20:06:13 -04001879 nfs4_fill_super(s);
1880 }
1881
Trond Myklebustf2d0d852007-02-02 14:46:09 -08001882 mntroot = nfs4_get_root(s, &mntfh);
David Howells54ceac42006-08-22 20:06:13 -04001883 if (IS_ERR(mntroot)) {
1884 error = PTR_ERR(mntroot);
1885 goto error_splat_super;
1886 }
1887
1888 s->s_flags |= MS_ACTIVE;
1889 mnt->mnt_sb = s;
1890 mnt->mnt_root = mntroot;
1891
1892 dprintk("<-- nfs4_referral_get_sb() = 0\n");
1893 return 0;
1894
1895out_err_nosb:
1896 nfs_free_server(server);
1897out_err_noserver:
1898 dprintk("<-- nfs4_referral_get_sb() = %d [error]\n", error);
1899 return error;
1900
1901error_splat_super:
1902 up_write(&s->s_umount);
1903 deactivate_super(s);
1904 dprintk("<-- nfs4_referral_get_sb() = %d [splat]\n", error);
1905 return error;
David Howellsf7b422b2006-06-09 09:34:33 -04001906}
1907
David Howells54ceac42006-08-22 20:06:13 -04001908#endif /* CONFIG_NFS_V4 */